筆記一下有關ngrx的配置方式,還有個人的一些想法
前言
redux在前端現在可說是無人不知無人不曉,Rx系列也是越來越多技術採用了,不管是Swift甚至是android都有相關對應rx的實做,筆者因為有從無到有實作過vuex,也有從無到有實作過react+redux+redux thunk,每種實現方式都有點不同,也有各自技術的限制,在這方面其實ngRx的限制很少,甚至在angular2根本不需要redux也能有很多辦法可以做到組件的溝通,所以這只是一個選擇性方案,並不是必要的,那難道我只是為了研究技術的心態來做ngRx嗎,當然筆者沒有那麼無聊,畢竟要學的東西還有非常多,使用ngRx最重要的目的,就是redux devtool了,可以方便我們有工具幫忙可以trace state。
導覽
Angular2組件溝通的幾種方式
其實angular2有非常多的方式,當然在組件溝通的時候,我們必須意識到並不是所有都得使用ngrx來實做,我們可能必須得考慮到通用性的部份,也就是我們如果做為一個通用的元件,這個元件應該以最少知識原則來看待,盡量把所有的knowleage交由調用方來實做,其實最簡單的就是@input和@output了,接著我們還可以延用angular1的service溝通的方式,來讓組件間溝通,或者我們也可以使用rxjs的subject來實現,不過對我來說有redux的devtool的監控,在組件間的溝通,我還是選擇以ngRx為方案。
Redux的一些簡單說明
Redux其實就是狀態管理機制,也就是從Flux的啟發而改良開發出來的,原因是因為react其實是完全的one way binding,所以要畫面重刷的話,必須都得自己手動去更新state,但是如果我們畫面非常複雜,元件拆得非常細的話,手動更新state其實也是一個挺大的負擔,如果用redux的話,我們只要調用action去通知reducer的話,store一有改變,就會自己去觸發畫面改變,所以在react的世界裡面,redux幾乎可說是必用方案,而不是可選方案了,這就造成了其實所有的邏輯都會放在reducer裡面,然後我們觸發ajax的時候必須要在有一個middleware去處理非同步的部份,不然的話react-redux會直接報錯,基本介紹redux就到這邊了。
NgRx和Redux的一點差異
那ngRx呢?在ngRx也有實做middleware,叫做是ngrx effects,但在ngrx其實操作非同步,不使用ngrx effects也不會有問題,馬照跑舞照跳,我個人比較喜歡的方式,是把邏輯放在service層實做,包括ajax的調用,當我們做完某段邏輯,想要去通知組件更新的時候,再去調用action觸發reducer來實做就可以了,而且需注意NgRx的註冊方式一樣是回傳一個Observable哦,我們一樣可以使用subscribe的方式來觸發訂閱,接下來就是一些安裝和說明囉
NgRx的安裝
首先照著官網的說明,就是先安裝npm install @ngrx/core @ngrx/store --save,接著因為我需要redux的devtool來協助追蹤狀態,所以我需要再安裝 npm install @ngrx/store-devtools --save,然後我習慣在src/app的目錄夾裡面新增一個stores的目錄,來區分這個是redux相關的,因為redux強調只有一個store的觀念,實際上我們可以以自己的區分來做多個reducer最後再對應回一個store,這邊也是看個人的自由發揮,在store目錄夾底下,我新增了一支action-type.ts(定義一些對應reducer的actoin的常數)和root-reducer.ts (主要的reducer),在此我還是以一個最簡單的count來做示例,先在actoin-type.ts新增常數
src/app/store/action-type.ts
export const INCREASE_COUNT = 'INCREASE_COUNT';
export const REDUCE_COUNT = 'REDUCE_COUNT';
接著就直接新增reducer的邏輯了
import { INCREASE_COUNT, REDUCE_COUNT } from './action-type';
import { Action } from '@ngrx/store';
export interface RootState {
count: number;
};
export const initialRootState: RootState = {
count: 0
};
export function rootReducer(state = initialRootState, action: Action): RootState {
switch (action.type) {
case INCREASE_COUNT:
state.count++;
return Object.assign({}, state); //這邊得注意一下,這邊就算直接return state也不會出錯,但在redux devtool的時候,追蹤狀態就沒有效果了,主要還是pure function的概念,防止side effect。
case REDUCE_COUNT:
state.count--;
return Object.assign({}, state);
default:
return Object.assign({}, state);
}
}
接著在app.module.ts裡面,import需要的module
imports: [
BrowserModule,
FormsModule,
HttpModule,
StoreModule.provideStore({ rootReducer: rootReducer }), //除了註冊store也要加進rootReducer,這邊不要使用一個rootReducer就好,因為筆者嘗試在build --prod的時候,會有問題
StoreDevtoolsModule.instrumentOnlyWithExtension() //這邊是註冊redux的devtool
],
NgRx與component結合
接著我在component需要在construct的部份,選擇我們的store和對應的reducer了
/src/app/app.component.ts
import { INCREASE_COUNT, REDUCE_COUNT } from './store/action-type';
import { RootState } from './store/root-reducer';
import { Store } from '@ngrx/store';
import { Component } from '@angular/core';
import { Observable } from "rxjs/Observable";
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
rootStore$: Observable<RootState>;
//下面的RootState是我們定義在RootReducer的state哦
constructor(private store: Store<RootState>) {
this.rootStore$ = store.select('rootReducer'); //這邊則是我們定義在app module裡面的reducer名稱哦
}
plus() {
this.store.dispatch({ type: INCREASE_COUNT });
}
reduce() {
this.store.dispatch({ type: REDUCE_COUNT });
}
}
/src/app/app.component.html
<h1>
{{(rootStore$ | async).count}}
</h1>
<button (click)="plus()">+1</button>
<button (click)="reduce()">-1</button>
最後我們必須要安裝redux的devtool,請自行為chrome安裝,然後就可以開啟redux devtool來監控state的變化了
比較複雜的應用
其實這個示例是直接在component就調用了,如果你是把邏輯都寫在recuder而且有調用effects的話,確實是可以這樣子做,component只要觸發一個action,但是因為我個人的選擇是對service來封裝邏輯和調用ajax,所以我在調用dispatch的部份,幾乎都是在service裡面調用,而且必須要注意,並不是所有的東西都會去使用到NgRx,我個人是只有針對組件間的溝通,才會使用NgRx來操作,還有如果一旦我們使用NgRx調用溝通,我們要把通用性的元件搬去另一個專案使用,就會很難重覆使用,因為我們同時候還要搬動store的部份,甚至還要搬動service,而且另一個專案也必須得安裝NgRx,所以什麼樣的組件要使用NgRx來管理狀態,甚至是不是真的需要使用NgRx,這方面需要好好規劃和思考。
結論
因為angular2有太多方式可以做元件間的溝通,所以每個團隊的實做方式都不一定相同,但提供了以redux的概念方式來管理狀態,我個人覺得這是一種比較完美的選擇,以上如果有什麼建議或覺得筆者有任何錯誤的地方,再請多多指導。