在 CodeInsight 開發告一段落之后,CTO 大人找到我說要想一個把 Coding.net 的前端拆分重構的方案,于是我從一個歡脫的開發狀態開始切換到要面對一句魔咒的考驗。
動態語言一時爽,代碼重構火葬場。
不管怎么樣,先從梳理現狀開始。
Coding 前端使用 Angular 構建,前端工程化還是使用合并文件打包的方式,并沒有引入 CommonJS 之類的模塊化開發方式,作為一個 SPA 網站,隨著網站規模的增大,前端代碼開始越來越臃腫,開發體驗也直線下降,這是我們考慮重構的原因。
所以首先我們要想清楚重構要解決的問題
要做到后一點尤其困難,但這也是我們能否順利重構的關鍵,重構不是重寫,所以如何在現有代碼基礎上重構,并且還要和當前的開發進度無縫銜接起來就是我們所要面臨的一個挑戰。
好消息是我們使用了 Angular 保證了我們的代碼分 Module 有了一層封裝,不至于太過散亂。作為一個 SPA 網站,前端路由已經很好的分離出了各個功能模塊。我們用到了 Grunt,雖然有點過時,task 寫得有點復雜,但是提供了一個工程化的切入口。
經過小伙伴們幾輪討論之后,終確定了一套比較靠譜的方案:
后一點是我們這套重構思路的核心,在這之前我們有考慮把每個功能模塊拆分出獨立的單元來跑,但是為了保證“邊開車邊換輪子”,重構必須是一個快速迭代的過程,不能說等到某一個完整的功能模塊單元重構完了再去整合到現有的代碼,這種重構方式將是一個漫長耗時的過程,并且風險也很大。
保持 SPA,引入懶加載,我們可以快速將這種架構調整整合到現有代碼中去,驗證是否可行,之后的重構過程就可以細化到每一個 controller,做到“邊開車邊換輪子”。
這一步很簡單,Coding 網站的功能模塊已經比較清晰了,比如冒泡,任務,搜索等等,我們只需要確立一套統一的目錄結構和命名空間規范來重新整理代碼,得益于 Angular 的依賴注入機制,之前的代碼邏輯完全可以保持不變,對于那些獨立的模塊,這個重構過程基本上沒有什么引入 Regression Bugs 的風險,重構一個模塊只是修改命名空間而已。
我們約定:
比如重構后的冒泡可能是類似這樣的結構:
tweet/
├── tweet-list.controller.js
├── tweet-list.html
├── tweet-topic.controller.js
├── tweet-topic.html
├── tweet.module.js
└── tweet.routes.js
tweet.routes.js 將會指定懶加載 tweet.module.js。
為了做到代碼打包拆分,我們使用懶加載的方式,當導航到對應的功能模塊時才去加載相應的功能模塊代碼,引入 webpack 一并實現了對 CommonJS 的支持以及 Lazy Load。
現有的 Angular 路由已經很好的分離出了功能模塊入口,所以我們只要把這個路由文件當做一個切入點,作為 webpack 的打包入口文件,由這個入口文件引入的所有依賴就都可以使用 CommonJS 的模塊化方式了,也就是說我們所有重構的代碼自然而然就可以遷移到使用 CommonJS,在這里 webpack 將作為一個完美的粘合劑,銜接現有的代碼和重構后的代碼,這里通過一個簡單的路由來看一下是如何做到的。
./tweet/tweet.routes.js
$routeProvider.when('/pp/:region?', { templateUrl: require('./tweet-list.html'), controller: 'TweetListController', title: '冒泡', resolve: { lazyLoader: function($q, $ocLazyLoad) { var defer = $q.defer(); require.ensure([], function() { var module = require('./tweet.module'); $ocLazyLoad.load({ name: module.name }); defer.resolve(module); }); return defer.promise; } } });
Angular 的路由支持異步加載,require.ensure 是 webpack 用來異步加載回調函數內部指定的 tweet.module.js,這個 module 注入了 tweet 這個功能模塊的所有依賴,比如TweetListController
'use strict';
var angular = require('angular');
module.exports = angular .module('tweet', [ require('./tweet-list.controller').name, require('./tweet-topic.controller').name, ]);
后我們用到了 ocLazyLoad 來注入這個異步加載的 module。
新引入的 webpack 可以很容易的整合到我們現有的開發流程里面去,使用 grunt-webpack 就可以把 webpack 作為一個新的任務給 grunt 調用,所以我們可以獨立 webpack 的打包編譯流程,并且作為一個子任務插入原來的編譯流程,而不影響原來的開發/發布方式。
gruntfile.js
... webpack: { dev: { entry: { 'routes': ['./src/routes.js'], }, ... }, prod: { ... } } grunt.registerTask('server', [..., 'webpack:dev', ...]);
grunt.registerTask('build', [..., 'webpack:prod', ...]);
...
對于獨立的,不被其他地方依賴的 module,重構可以很方便,但是對于公用的模塊,雖然可以重構這個模塊,但是要更改所有針對這個模塊的引用,牽涉到的代碼就有點不受控制了。
所以我們才要約定所有重構的模塊都會有自己的命名空間,對于那些公用的模塊,遷移到新的命名空間,同時會保留之前的代碼,直到我們重構其他功能模塊到某個時間點,可以確定沒有模塊依賴這些被保留的公用模塊,再去清理,這樣在前期會有一部分代碼冗余,但是保證了我們重構的質量和進度。
至此,Coding 前端開啟了重構之路,相信 Coding.net 將會逐步帶來更好的體驗。
本站文章版權歸原作者及原出處所有 。內容為作者個人觀點, 并不代表本站贊同其觀點和對其真實性負責,本站只提供參考并不構成任何投資及應用建議。本站是一個個人學習交流的平臺,網站上部分文章為轉載,并不用于任何商業目的,我們已經盡可能的對作者和來源進行了通告,但是能力有限或疏忽,造成漏登,請及時聯系我們,我們將根據著作權人的要求,立即更正或者刪除有關內容。本站擁有對此聲明的最終解釋權。