angular中,controller、directive和factory分別該在何時使用?

即怎麼組織代碼呢?哪些代碼是要寫在控制器中,哪些要寫在指令中呢?


要了解使用方式的話,先要簡(shen)單(ru)理解 Controller、Directive 和 Factory 分別是什麼,鑒於樓上的答主都沒有說得很清楚,這裡繼續強答一下。。

文章主旨:Angular 2 就是 Angular 1.x 最佳實踐的延伸,所以了解 Angular 2 中的做法,就可以了解 Angular 1.x 中應該怎麼做。。

1. Controller

Controller 雖然叫 Controller,但遠沒有後端框架中的 Controller 那麼複雜的作用,Angular 1.x 中的 Controller,說得不好聽一下,就僅僅是一個初始化器(Initializer),進行相關的數據綁定(包括欄位和方法)而已,一般的初學者用法如下:

// Angular 1.x with $scope in ES5
myApp.controller("myController", ["$scope", "itemService", function($scope, itemService) {
$scope.currentItem = itemService.current();
$scope.items = itemService.all();
$scope.add = function(item) {
itemService.add(item);
};
}]);

可以看到,上面的代碼中,Controller 乾的事情僅僅就是把相應的數據或者方法綁定到 ViewModel 中。隨著 Angular 1.2 中 Controller As 語法的到來,就不再需要 $scope 了:

// Angular 1.x with "controller as" in ES5
myApp.controller("myController", ["itemService", function(itemService) {
this.currentItem = itemService.current();
this.items = itemService.all();
this.add = function(item) {
itemService.add(item);
};
}]);

這樣,就可以拋棄 $scope 了(其實事件傳遞還是有可能用到)。看到 this 之後,我們不妨思考一下,這些屬性到底是對象獨有的還是類(嚴格地說JavaScript中叫原型)共有的呢?通常都是後者,即只要是這個 Controller,都要有這些屬性。這樣,我們就可以考慮,把不變性的內容(比如基本上所有的方法)作為原型的屬性而非對象的屬性:

// Angular 1.x with "controller as" in ES5
myApp.controller("myController", ["itemService", ItemController]);

function ItemController(itemService) {
this.currentItem = itemService.current();
this.items = itemService.all();
}

ItemController.protoype.add = function(item) {
itemService.add(item);
};

這樣就避免了把一坨方法綁定也擠到 Controller 里,也可以更清晰的看出 Controller 的初始化作用。(this 會順著原型鏈查找的,但是可能很多用 Angular 的人並沒有清晰的看到 Angular 仍然是普通的 JavaScript。)然後,隨著 ES6 的流行,我們可以很輕鬆的轉換成 ES6 的版本:

// Angular 1.x with "controller as" in ES6
myApp.controller("myController", ["itemService", ItemController]);

class ItemController {
constructor(itemService) {
this.itemService = itemService;
this.currentItem = itemService.current();
this.items = itemService.all();
}
add(item) {
this.itemService.add(item);
}
}

所以,因為 Controller 的作用就只是初始化,在有了類(class)的情況下,就完全可以被同化到 class 的 constructor 裡面了。如果開發人員喜歡靜態類型的話,還可以使用 TypeScript,也就相當於在 ES6 的基礎上增加了一個靜態的類型系統:

// Angular 1.x with "controller as" in TypeScript
myApp.controller("myController", ["itemService", ItemController]);

class ItemController {
private currentItem: Item;
private items: Item[];
constructor(private itemService: ItemService) {
this.currentItem = itemService.current();
this.items = itemService.all();
}
add(item: Item): void {
this.itemService.add(item);
}
}

其中,用到了 TypeScript 的一個語法糖,即在構造函數的參數名前加上可訪問性修飾符(private、public)可以直接添加為實例的屬性。最後,我們就可以很容易理解 Angular 2 中的寫法了:

// Angular 2 in TypeScript
@Component({
...
})
class ItemController {
private currentItem: Item;
private items: Item[];
constructor(private itemService: ItemService) {
this.currentItem = itemService.current();
this.items = itemService.all();
}
add(item: Item): void {
this.itemService.add(item);
}
}

由於 TypeScript 提供了 Metadata 支持,Angular 2 可以直接按類型注入(當然也依然可以使用按名注入,如果有特殊需求的話),不需要再手動鍵入依賴名稱了。

綜上所述,Controller 唯一的作用就是初始化,除了從外部到 ViewModel 的簡單賦值(或極其簡單的運算外),其他所有過程都不應該出現在 Controller 中。

2. Directive

Angular 1.x 中的一個奇葩問題就是把所有自定義的 HTML 擴展都叫做 Directive,引起了很多誤會。Angular 中的 Directive 按用途分為三種:

  • Component Directive
  • Attribute Directive
  • Structural Directive

Structural Directive 是最特殊的,相當於一般 MVC 框架中 Template 的關鍵字,作用是影響 HTML 的文檔結構而非特定元素,比如 ngRepeat、ngInclude、ngIf、ngSwtich 等。

另外兩種的話就很好分了,不出意外的話 restrict: "E" 就是 Component Directive,restrict: "A" 就是 Attribute Directive,前者作為一個獨立元素(或者說組件)存在,比如 ngForm,後者影響現有元素/組件的某些效果,比如 ngClass、ngModel、ngSrc、ngStyle、ngClick 等。(在 Angular 2 中由於引入了通用的屬性綁定和事件綁定機制如 [class.someKey]="someValue"、[style.someKey]="someValue"、[src]="someValue"、(click)="someMethodCall()",所以對原有屬性的封裝和對原生事件的封裝的 Attribute Directive 都不需要了)

Angular 1.x 中,controller 既可以作為 Component Directive 的屬性也可以作為 Attribute Directive 的屬性(比如 ngController、ngView),但是由於由 ngController 的存在,近乎於可以創造獨立於 Directive 的 Controller。Angular 2 中廢棄了 ngController 這個存在,所有需要用到 Controller (或者說 Constructor)的地方都需要自己定義相應的 Directive。

綜上所述(雖然好像並沒有怎麼述),Angular 的哲學理念就是保證 View 層儘可能的使用聲明式語法(而非命令式語法),一切初始化相關的東西都放在 Controller 中(慎用 ngInit),所有需要用到 Controller 的地方都封裝成自己的 Directive。

3. Factory?

如果說的是 Provider、Factory、Service、Value、Constant 的總稱的話,一般來說並沒有 Factory 的叫法,要麼叫 Service(官方早期是這麼叫的,但是會有 a Service is a Service 這種跨層次重名情況),或者叫 Provider(除 Constant 外本質上都是 Provider,Value 和 Service 是調用的 Factory,Factory 是調用的 Provider),或者高雅一點叫 Dependency。這裡暫時叫一下 Provider,如果不習慣的話,腦補成別的名詞就好。。

其實如果按本文這種順序的話,用下排除法就知道什麼時候該用 Provider 了,所有沒有在上面兩者的職責內的情況,包括但不僅限於網路、存儲等其他一切與展示邏輯無關的內容。

至於這幾個東東內部的區別,The Provider(不是泛指)的話具有可配置性,可以在程序運行前進行相應的設定;Factory 和 Service 除了寫法上沒有什麼區別,喜歡用什麼用什麼,不一定要按它的名字來;Value 和 Constant 為單個對象依賴,Constant 可以在 Provider 的配置階段使用,其他都不能。Angular 2 把上面這些東西的名詞叫法合併了,雖然實際上並沒有什麼功能上的改變。(不過,依賴注入的機制倒是改了不少。)

綜上所述,所有與展示邏輯(或者說交互邏輯)無關的部分都應該寫成 Provider(廣義的)。

以上為個人理解,由於本人才疏學淺,難免會有錯誤,如果有意見或者建議歡迎指出,Lambda 表達式結尾: (O=&>_&<=O)


directive一般用來寫一些通用的組件。

controller處理特定頁面的邏輯。

factory/service 是單例模式,service是對factory的封裝,相應的還有constant和value方法。一般公用的靜態方法(如與後端交互的ajax請求),或者需要在不同的controller、directive、service之間共享的數據都可以定義在service中。


directive為指令,封裝通用模塊,類似於自定義標籤

controller為mvc中的c,負責一些指定頁元素對應的邏輯,和作為中間人連接v跟m

service等為mvc中的m,負責跟介面的數據交換


1. 請求資源與數據緩存的東西放進service。

2. 數據需要格式化的東西用filter處理。

3. 偶爾需要的dom操作在指令中去寫。

4. controller與視圖按照一對一的關係維護,在controller內主要初始化scope對象與在scope上添加方法(行為)。


樓上說的挺好啊,稍稍補充一點小觀點,

隨著項目的進程和controller的臃腫,會出現controller--&>service的遷移吧,

也就是不斷從業務代碼中抽出service。


Angular2 摒棄了很多1中的糟粕,所以按照2的方式來開發即是最佳實踐,大致有那麼幾點:

- 全部使用 directive,也就是組件化(component)開發,不是只有公用組件才需要 directive。

- 不要使用 $scope,使用 controllerAs 來綁定數據。

- 業務邏輯全部放入 service(factory),directive 的 controller 只負責當前 directive 的顯示邏輯和調用 service,越輕越好。


控制器

控制scope,代碼越薄越好。

工廠

有倉庫,加工流程,可以使用外包服務

服務

一次性的,$http provider只inject 到這裡。


ItemController.protoype.add = function(item) {
itemService.add(item);這個是不是代表自然方法。


推薦閱讀:

angular中控制器之間的傳值該怎麼實現?
angularjs中不同頁面controller中數據傳遞的問題?
AngularJS的數據雙向綁定是怎麼實現的?
AngularJS 1.x 的缺點如何解決?

TAG:前端開發 | AngularJS |