標籤:

angular2中數據狀態管理方案有哪些?

react有redux,vue有vuex,那麼angular2中怎麼做數據狀態管理?


謝 @Saviio 邀

今天剛好看了一下 angular 2 change detection 的部分,遂嘗試回答一下。

先說結論,對性能要求高的場景使用 Rx/Immutable,普通的場景用 POJO。

為什麼有這個結論可以移步 https://www.youtube.com/watch?v=jvKGQSFQf10

對於不想看英文視頻的同學,我來搬運下視頻的主要內容:

1. Change Detection

angular 使用了 https://github.com/angular/zone.js 來監視 component 的變更,並在需要的時候 render dom。由於 ng 2 的 component 的變更檢測永遠是從 root 開始,一直到最底層的 component 結束,大概像下圖這樣(圖中 CD 表示 change detection):

所以在一個正常的 ng2 change detection cycle 中,不管哪一個 component 中的 ngZone 捕獲到了一個非同步事件的發生,就會從根 component 往下遍歷,進行變更檢測,直到最後一個 component。這個思路其實和 ng 1是非常類似的,只不過 ng 1的變更是雙向的,也就是說在 ng 1中任意一個 directive/controller 的 $scope 在 $digest 過程中都可能會引發另一個 $scope 的變更從而讓 $digest give up 掉重新再來一次。而在 ng2 的 change detection 中,子 component 如果在 change detection cycle 中有任意變更它的父 component 的行為,都會拋出異常從而終止。也就是說同樣是 $digest cycle,在 ng 2 中變成了單向的可預測的:

2. 優化

由上面的部分我們可以知道,任意一次數據變更的代價是 time = C * N

C 表示平均每個綁定到模版的值變更檢測所需的時間

N 表示綁定的數量。

在 ng2 中,由於框架已經自動為模版生成的代碼做了非常多的優化,即使是在未使用優化過的 model 的情況下都已經可以達到 ng 1臟檢測性能的 3-10 倍(同樣綁定數量,同樣檢測次數)。視頻中提到了這是因為 ng 2 在生成模版代碼時,會動態生成讓 js 引擎易於優化的代碼,大概原理就是保持每次 check change 前後對象「形狀 」的一致。而如果在性能有瓶頸的地方,可以使用下面兩種方式進行高階優化:

  • Immutable

使用 immutable 的思路是減小 N 的值。我們知道在一個 component 中,綁定到模版上的數據一般都是對象上的屬性,而對象的 check change 是非常昂貴的,需要遍歷綁定到模版上的每一個值。而如果使用了 immutable,則這個 check 的過程將會非常迅速。並且,額外的好處是,在你告訴了框架你使用了 immutable 之後,當你的值並沒有變化時,ng 2 的 change detection cycle 會忽略掉這個 component,如下圖:

假設每個 component 都使用了 immutable model ,白色的部分是變更的部分,則在一個 change detection cycle 中只會 recheck render 白色的部分,從而大大減少處理變更的代價。

你可以通過設置 changeDetection 來告訴框架只有綁定的這個對象引用改變時才處理變動:

@Component({
changeDetection: ChangeDetectionStrategy.OnPush
})
class TodoCmp {
todo: Todo
}

  • Observable

這個通常是使用 Rx ,但也可以是 Backbone/Knockout 等(本人強烈不推薦)。使用這種模式的時候我們面臨的情況和使用 immutable 的時候並不一樣。使用 immutable 時 change detection cycle 的情況和使用 POJO 時很類似,變更非常自然的從 root component 開始往下,依次檢測。而使用 Observable 時,情況就不一樣了,它的變更很可能是從一個非常下層的子 component 中開始發生的,比如:

在圖中一個子 component 通過 observable 觀察到了一次數據的變更。這個時候我們需要告知 ng 2 這個部分發生了變更,它將會把這個 component 與它的父 component 一直到 root component 標記出來,並單獨檢測這一部分的變更:

我們可以使用同樣的方法來設置這種變更檢測:

@Component({
template: "{{counter}}",
changeDetection: ChangeDetectionStrategy.OnPush
})
class CartBadgeCmp {

@Input() addItemStream:Observable&;
counter = 0;

ngOnInit() {
this.addItemStream.subscribe(() =&> {
this.counter++; // application state changed
})
}
}

三種方案是可以混用的(其實 immutable 和 observable 在框架看來是一樣的)。

比如:

只需要在特定的 component 標記出對應的 changeDetection。

3. 坑

由於程序中的 model 和 component 的結構往往並不是相對應的:

所以在處理 model 的變更時,特別是使用 Rx / Backbone 時,要特別小心避免「級聯更新」 也就是所謂的 Cascading Updates , 也就是 Flux/Redux 嘗試解決的問題。ng 2 讓 component 變更處理變成單向的,這並不意味著我們就可以不考慮 model 數據流的設計,在使用 Observable 模式時尤其需要注意不要產生雙向的數據流。backbone 在不經意間就會產生這樣的結果 如何在大型 Web 應用中保持數據的同步更新? - 太狼的回答。參考資料:

  • https://www.youtube.com/watch?v=jvKGQSFQf10

  • Change Detection in Angular 2

  • Angular 2 Change Detection Explained


最為正統(也就所謂官方)的方式當然是 Rx。。

可以參考 Managing State in Angular 2 Applications ,理論上 Redux 能實現的功能 Rx 當然也都可以,截取一小段代碼作為示例:

function todos(initState: Todo[], actions: Observable&): Observable& {
return actions.scan((state, action) =&> {
if (action instanceof AddTodoAction) {
const newTodo = {id: action.todoId, text: action.text, completed: false};
return [...state, newTodo];
} else {
return state.map(t =&> updateTodo(t, action));
}
}, initState);
}

也有 Rx 和 Redux 的組合產品,比如 GitHub - ngrx/store: RxJS powered state management for Angular2 apps, inspired by Redux :

export const counter:Reducer& = (state:number = 0, action:Action) =&> {
switch (action.type) {
case INCREMENT:
return state + 1;
case DECREMENT:
return state - 1;
case RESET:
return 0;
default:
return state;
}
}

當然,也有純粹的 ng2 的 Redux 實現,比如 GitHub - angular-redux/ng2-redux: Angular 2 bindings for Redux 等等。。

總之不外乎是 /R.*?x/ 。。


Try NGRX

ngrx/store

ngrx/effects


推薦閱讀:

PC 前端是不是沒希望了?
Angular和React哪個更有前景?
angular2要用typescript,是不是現在該提前做好準備了,開擼typescript?
如何評價尤雨溪向大漠窮秋的領導就Vue與Angular"爭論"的舉報?

TAG:Angular? |