本文作者Cliff Meyers是一個(gè)前端工程師,熟悉HTML5、JavaScript、J2EE開發(fā),他在開發(fā)過程中總結(jié)了自己在應(yīng)對(duì)JavaScript應(yīng)用越來越龐大情況下的文件結(jié)構(gòu),深得其他開發(fā)者認(rèn)可。
首先,我們來看看angular-seed,它是AngularJS應(yīng)用開發(fā)的官方入門項(xiàng)目,其文件結(jié)構(gòu)是這樣的,
- css/
- img/
- js/
- app.js
- controller.js
- directives.js
- filters.js
- services.js
- lib/
- partials
看起來就像是把衣服按類型堆在地板上,一堆襪子、一堆內(nèi)衣、一堆襯衫等等。你知道拐角的那堆襪子里有今天要穿的黑色羊毛襪,但你仍需要花上一段時(shí)間來尋找。
這種組織方式很凌亂。一旦你的代碼中存在6、7個(gè)甚至更多的控制器或者服務(wù),文件管理就會(huì)變得難以處理——很難找到想要尋找的對(duì)象,源代碼控制中的文件也變更集變得難懂。
常見的JavaScript文件結(jié)構(gòu)還有另一種形式,即按原型將文件分類。我們繼續(xù)用整理衣服來比喻——現(xiàn)在我們買了有很多抽屜的衣柜,打算將襪子放在其中一個(gè)抽屜里,內(nèi)衣放在另一個(gè)抽屜,再把襯衫整齊地疊在第三個(gè)抽屜……
想象一下,我們正在開發(fā)一個(gè)簡單的電子商務(wù)網(wǎng)站,包括登陸流程、產(chǎn)品目錄以及購物車UI。同樣,我們將文件分為以下幾個(gè)原型:models
(業(yè)務(wù)邏輯和狀態(tài))、controllers
以及services
(HTTP/JSON端點(diǎn)加密),而按照Angular默認(rèn)那樣非籠統(tǒng)地歸到service架構(gòu)。
因此我們的JavaScript目錄變成了這樣,
- controllers/
- LoginController.js
- RegistrationController.js
- ProductDetailController.js
- SearchResultsController.js
- directives.js
- filters.js
- models/
- CartModel.js
- ProductModel.js
- SearchResultsModel.js
- UserModel.js
- services/
- CartService.js
- UserService.js
- ProductService.js
不錯(cuò),現(xiàn)在已經(jīng)可以通過樹形文件目錄或者IDE快捷鍵更方便地查找文件了,源代碼控制中的變更集(changeset)也能夠清楚地描述文件修改記錄。雖然已經(jīng)獲得了極大的改進(jìn),但是仍有一定的局限性。
想象一下,你現(xiàn)在正在辦公室,突然發(fā)現(xiàn)明天有個(gè)商務(wù)出差,需要幾套干洗的衣服,因此給家里打電話告訴另一半把黑色和藍(lán)色的西裝交給清潔工,還有黑紋領(lǐng)帶配灰色襯衫、白襯衫配純黃領(lǐng)帶。如果你的另一半并不熟悉衣柜,又該如何從三條黃色的領(lǐng)帶中挑出你的正確需求?
希望衣服的比喻沒有讓你覺得過于陳舊。下面舉一個(gè)實(shí)例,
controllers
、models
、services
等文件夾整齊地排列著,但是他仍然不清楚對(duì)象間的依賴關(guān)系。信或不信,你確實(shí)很少會(huì)在新項(xiàng)目中重用很多代碼,但你很可能需要重用登陸系統(tǒng)這樣的整個(gè)模塊。所以,是不是按功能劃分文件會(huì)更好?
下面的文件結(jié)構(gòu)是以功能劃分后的應(yīng)用結(jié)構(gòu),
- cart/
- CartModel.js
- CartService.js
- common/
- directives.js
- filters.js
- product/
- search/
- SearchResultsController.js
- SearchResultsModel.js
- ProductDetailController.js
- ProductModel.js
- ProductService.js
- user/
- LoginController.js
- RegistrationController.js
- UserModel.js
- UserService.js
雖然現(xiàn)實(shí)世界中有空間限制,難以隨意整理服裝,但是編程中類似的處理卻是零成本的。
現(xiàn)在即使是新來的開發(fā)者也能通過頂級(jí)文件夾的命名理解應(yīng)用的功能,相同文件夾下的文件會(huì)存在互相依賴等關(guān)系,而且僅僅通過瀏覽文件組織結(jié)構(gòu)就能輕易理解登錄、注冊(cè)等功能的原理。新的項(xiàng)目也可以通過復(fù)制粘貼來重用其中的代碼了。
使用AngularJS我們可以進(jìn)一步將相關(guān)代碼組織為模塊,
var userModule = angular.module('userModule',[]);
userModule.factory('userService', ['$http', function($http) {
return new UserService($http);
}]);
userModule.factory('userModel', ['userService', function(userService) {
return new UserModel(userService);
}]);
userModule.controller('loginController', ['$scope', 'userModel', LoginController]);
userModule.controller('registrationController', ['$scope', 'userModel', RegistrationController]);
如果我們將UserModule.js
文件放到user文件夾,它就成了這個(gè)模塊中使用到的對(duì)象的“manifest”,這也是適合RequireJS
或者Browserify
中放置某些加載指令的地方。
每個(gè)應(yīng)用都會(huì)有某些代碼廣泛使用在多個(gè)模塊中,我們常常使用名為commom或者shared的文件夾來存放這些功能代碼。
又該如何處理這些通用代碼呢?
Facade
(外觀模式)。這有助于減少每個(gè)對(duì)象的依賴者,而過多的關(guān)聯(lián)對(duì)象通常意味著糟糕的代碼結(jié)構(gòu)。SOLID
中接口隔離原則的變種。$emit
、$broadcast
以及$on
方法使得這種方式變得現(xiàn)實(shí)??刂破髂軌蛴|發(fā)一個(gè)事件來執(zhí)行某些動(dòng)作,然后再動(dòng)作結(jié)束后收到相應(yīng)地通知。
更多建議: