更新 : 2019-01-10 javascript
對 redux 的思考html
假設有一個場景 前端
ParentComponent java
ChildAComponentreact
ChildBComponentgit
parent.person 是一個對象 github
<child-a [person]="person" ></child-a> <child-b [person]="person" ></child-b>
咱們把對象傳入 ChildA 和 ChildB ajax
如今若是咱們在 ChildA 裏修改 person.name = 'dada';數據庫
ChildB 是沒法同步更新的, 這是由於使用了 onpush, person ref 沒有改變.json
那咱們用 immutable 試試, 在 ChildA 裏調用 person = { ...person, ...{ name : 'dada' } }
仍是不行丫, ChildA 直接覆蓋 input 屬性 person, 那就斷了連接了丫.
因此只能交由 Parent 調用了 person = { ...person, ...{ name : 'dada' } }
成了.
上面就是我對 redux 的理解.
更新 : 2019-01-07
不錯的文章
refer :
https://blog.angularindepth.com/if-you-think-ngdocheck-means-your-component-is-being-checked-read-this-article-36ce63a3f3e5
更新 : 2018-12-19
async pipe 的好與壞
在使用 onpush 的狀況下,一但遇到異步情形,好比 ajax 獲取數據, 咱們就須要調用 markforcheck
常見的情形是這樣的.
有個 ajax 方法
class Person { name: string; } function getPersons(): Promise<Person[]> { return new Promise((resolve) => { setTimeout(() => { const person = new Person(); person.name = 'keatkeat'; resolve([person]); }, 3000); }); }
有個 controller
export class TestAsyncPipeComponent implements OnInit { constructor( private cdr : ChangeDetectorRef ) { } persons: Person[] | undefined; async ngOnInit() { this.persons = await getPersons(); this.cdr.markForCheck(); } }
view
<div *ngIf="persons !== undefined; else loading" > {{ persons[0].name }} </div> <ng-template #loading >loading...</ng-template>
這樣的寫法對比 async 好處是直觀.
controller 沒有 stream 的概念, 經過一個 await 獲取到最終的值.
缺點是須要調用 mark for check, 同時要顧慮到 person 在 ajax 尚未返回的時候是 undefiend 的.
若是換成 stream + async pipe 的寫法是
export class TestAsyncPipeComponent implements OnInit { constructor( ) { } personsPromise: Promise<Person[]>; ngOnInit() { this.personsPromise = getPersons(); } }
view :
<div *ngIf="personsPromise | async as persons; else loading" >
{{ person[0].name }}
</div>
<ng-template #loading >loading...</ng-template>
看得出來, controller 乾淨了許多, view 只是多了一個 | async.
因爲 controller binding 的是 promise 因此在 init 時就有了,沒有了 undefined 的概念, 而後 async pipe 負責了 mark for check 因此 controller 也無需注入 cdr 了. (注 : 在 promise 尚未 resolve 的時候 async pipe 返回值是 null 而不是 undefined 哦)
在這種簡單狀況下, async pipe 是不錯的. 但若是遇到複雜一些些的狀況就不必定了.
好比如今 getPerson 只返回 10 條數據, 有一個 show all 按鈕, 用戶按下後須要返回全部數據 (這是很常見的狀況)
若是咱們使用的是第一個方案, 那麼咱們只要寫一個 click 事件, 而後寫一個 show all loading, ajax 回來後把 loading 關掉, 而後賦值給 persons 就能夠了.
方案二就不一樣了. 因爲 controller view binding 的是 promise, 而咱們不可能替換掉 promise 若是替換 promise loading 就會出現了. 這不是咱們要的.
那若是依照 stream 的概念去修改的話, persons 這個 stream 是會發布 2 次值得. 咱們須要用一個 subject 來表示或者經過 merge(firstload, showAll)
2 個 action 表示, 這有點像作 ngrx 了. 一個 state 2個 action 可改變它. 而後又要處理 2 個 loading (first big loading and show all small loading..)
整個感受就差不少了.
這裏順便說說對 ngrx 的想法 (雖然我只是看了看教程)
store 保管了全部的 state, 它只提供 2 個方式讓外部使用,一個是 get () 返回 observable, 另外一個 publish action (payload) 一個事件名字 + parameter
重點是 1. 外部是不能夠直接改變 state. 2. 全部 state 都是 observalbe
controller 要 state 就跟 store 拿 ( 好比上面的 persons, this.persons$ = store.get('person'))
init 時, controller 要 publish 一個 init action, 可能叫 firstLoadPerson
這時 reducer 出現了, reducer 負責監聽全部 action 而後去修改 state
這個階段修改的 state 是 loading = true;
同時呢, 另外一個叫 effect 的出現了, 它也監聽 action
它的工做不是修改 state, 而是作一些事情,好比發 http 請求去 ajax persons
effect 監聽一個 action 而後... 返回另外一個 action, 返回 action 意思是 publish this action with payload
好比 personLoadSuccess(personsData) 成功了.
reducer 又會監聽到這個 action 而後修改 state
好比 loading = false; persons = personsData.
整個過程當中 controller 就監聽 state 和 publish action 就行了, 既然 state 是 observable 一般就用 async pipe 來處理了咯.
那麼 show all 也是同樣的作法, publish 一個 show all action
show all loading obserable 被 reducer 改變
effect 去 ajax 而後發佈 done action reducer 又改變.
大概就是這樣一個流程.
我我的的感受是 redux 這種方式職責分的很清楚, 因此常常的感受就是, 處理大事分工很好,處理小事分工很廢...
最後說說感想, 雖然 ng 給了咱們不少很優秀的方案實現需求,可是咱們依然須要搞清楚不一樣情形下適合用什麼方案. 用錯了的話,可能本末倒置.
更新 : 2017-12-29
ng5 移除 zone.js
https://zhuanlan.zhihu.com/p/29577461
zone 的用途就是攔截遊覽器事件, 好比 click, ajax, timeout 等
ng 就是用它來實現 dirty check 的, 或者叫 change detech
這個很方便, 可是每一次事件觸發都來個 change detech 有時候會很浪費性能.
因此有了 onPush + markforcheck
若是你想更極端一點,乾脆就連 zone 都不要了. 本身調用 changeDetechRef 來管理更新.
platformBrowserDynamic().bootstrapModule(AppModule, { ngZone: 'noop' }) .catch(err => console.log(err));
運行上面代碼後,你會發現, ng 不會更新 view 了.... , click, ajax , async pipe 通通不靈了.
這時咱們能夠經過一些方法來讓 ng 更新.
1. ApplicationRef.tick() 這個就是 root detech change. 而後會一直往下流, 以前有講過 detech change 的流程.
2. changeDetectorRef.detectChanges() or markForCheck()
這 2 個 用途和區別以前有講過了, markforcheck 只是 mark 而已, 之因此你調用 markforcheck 會 update view 是由於
你調用它的時候剛巧在 ngZone scope 內. 而咱們把 zone 移除後, 單單調用 markforcheck 就沒用了,這也是爲何 pipe async 也不 work 了. 由於它只是調用了 markforcheck
那咱們調用 markforcheck 後咱們必須在調用一個 app.tick() or detechchange() 讓一切正常工做.
這就是移除 zone 的代價.
再一次提醒 :
app.tick 就是 root component detechchange
detech change 是當前的 component 一直往下到子孫 compoent docheck + detech change ...
markforcheck 是當前的 component 一直往上到 root 把每個 component mark to check (等待下一次 detech change 觸發就 update view).
更新 : 2017-08-25
markforcheck 經常使用在 onPush 狀況下,可是能調用的地方依然是基於 ngZone 內的. 若是 ngZone 外的狀況, markForCheck 是不靈的, 用 detechChange 倒能夠..
this.ngZone.run((0 => { this.cdr.markForCheck() }) 這樣就 OK.
markforcheck 和 click event 相似, self to ancestor (祖先root) 都會detechChange, 而 detechChange 則是 self to descendants (子孫).
往上的 detechChange 是確定一條龍觸發到 root 的, 往下的 detechChange 則遇到 onPush component 只作 docheck, 不必定會 detechChange, 若是有detechChange 則繼續往下走,否則就中止了.
更新 : 2017-08-06
doCheck != detectChange
只要 parent component 發生了 detechChange, 那麼其全部的 children 都會 doCheck.(咱們攔截與否那是另一件事, )
parent 爲何會發生 detechChange, 不少緣由啦, 能夠是 (click), 能夠是 markForcheck 等等等. 參考我以前寫的.
若是觸發 doCheck 的 component 是 ChangeDetectionStrategy.OnPush
那麼 component 不必定會 detechChange. detechChange( @input changed 就會自動 detech change ) , 若是沒有 detech change 那麼就中斷了 . 子孫就不會觸發任何東西了。這也是 angular 優化的原理.
相反若是沒有設置 ChangeDetectionStrategy.OnPush , angular 就會設置此 component detechChange. 而因爲 detechChange 了, 其全部的 children 又會觸發 doCheck 而後一直循環到子子孫孫.
因此記住 parent detechChange -> all children will doCheck -> if children not OnPush then continue detechChange 一直下去...
if children is OnPush then no detechChange(是否 detechchange depend on input and docheck ) then 結束 ( but we still have chance 攔截 doCheck and manual call markForCheck to let it detechChange )
更新 : 2017-03-27
change detech 在沒有使用 onpush 的狀況下是很聰明的
ajax, timeout, document.addeven 均可以監聽到.
-detech
-reattach
-markascheck
component detech 以後 ng 在檢測時就不會理它了。
須要 component 本身維護
能夠調用 detechChange 來使它 check
也能夠調用 reattach 讓它加回去, 調用這個也會立刻 detechChange
detech 後使用 markascheck 是無效的. 這也是 markascheck 和 detechchange 最大的區別.
markascheck 是用於 onpush 場景下的. 而 detech,detechChange,reattach 跟 onpush 則沒有關係.
onpush 以後
timeout, ajax, document.addevent 通通不會跑了 (用 ng bind 的 event 會跑, e.g. (click)="這個會跑" )
使用 markascheck 來手動讓它 check
@input 傳進來的值 change 了會觸發 check, 不過必須是值類型,若是是引用類型好比 object array, 那麼你就累了,由於你須要本身 watch 這個 @input
ng 沒有 $watch 了, 因此通常的作法就是 "強轉" 這個值取 immutable 或則是 observable
service 更慘, 必定要用 observable, 你用 immutable 都沒有用.
redux 走的是 immutable 的路, mobx 走的是 obserable 的路. 我的比較須要 mobx 你們有興趣能夠多留意.
更新 : 2017-02-22
在實際開發中對狀態有了多一些的體會.
在沒有使用 state management tools or framework (like redux) 的狀況下, angular 怎麼走..
先說說 angular 對狀態變化和渲染.
ng認爲能改變狀態的只有 event (click,timeout,ajax等)
因此只要檢測到 event 觸發 (經過zone.js) 就讓全部的模板從新去抓取值 (模板上的表達式)
而後進行對比和渲染.
這個好理解效果也不錯. 可是它只能幫你搞定模板渲染.
若是你想寫依賴的話, angular2 沒有 $watch 了, 全部的依賴值應該使用 getter 去寫.
或者把依賴對象封裝起來,相似 activateRoute 那樣. 讓你們能夠去 subsribe 它.
有人可能會說沒使用 onPush 性能會很差, 主要仍是看項目, 若是大部分的 component 都不傷性能的話, 能不寫 onPush 就別寫了.
只在須要性能優化的 component 手動調用 ChangeDetectorRef.detach, detectChanges 就能夠了。
以上前提是在不使用 state management tools 的狀況下哦.
redux 真的好嗎?
refer :
https://medium.com/@machnicki/why-redux-is-not-so-easy-some-alternatives-24816d5ad22d#.1a8o2ehv9
http://blog.zhangrgk.ninja/program/2016/12/06/redux-is-not-good-enough.html
http://blog.angular-university.io/angular-2-redux-ngrx-rxjs/
refer :
http://cn.redux.js.org/ (redux 中文版)
https://egghead.io/courses/getting-started-with-redux ( 入門 videos, 偏向 react )
https://egghead.io/courses/building-react-applications-with-idiomatic-redux ( 高級 videos, 偏向 react )
http://blog.thoughtram.io/angular/2016/02/22/angular-2-change-detection-explained.html ( ng2 change detection 機制)
http://blog.angular-university.io/how-does-angular-2-change-detection-really-work/ ( ng2 change detection 機制 )
https://egghead.io/courses/building-a-time-machine-with-angular-2-and-rxjs (ngrx 收費 videos)
https://egghead.io/lessons/angular-2-ngrx-store-in-10-minutes ( ngrx 初次見面 )
由於前端愈來愈複雜, 因此須要定義更多的規則來管理,
state management 指的是對前端全部狀態的管理方案.
ng 經過 zone 攔截 event, timeout , ajax callback 等
而後 component detector 從上到下檢驗一次, 而後更新 dom
by default 任何值都不會放過, obj is deep scan
若是你要優化可使用2 種方式
1 .immutables
就是說若是對象的指針沒有換就表示內部都沒有改變 (不論是@input 仍是use a service get a share obj)
若是發現 parent component no change then his child all will skip check, even child no say want to use immutables strategy,
因此它須要一整套的規範才能夠作的好哦!要當心.
using angular2 and immutable.js
2. observer
監聽依賴 stream 而後 manual check
能夠把 ng2 的檢測機制改爲 onPush
它只有在5種狀況下才會檢測
1. component got fire event ( timeout, ajaxcallback 不算,dom event 而已 )
2. child component got check
3. manual check
4. input changed (obj no deep scan哦)
5. async pipe receive event
http://gold.xitu.io/entry/576cb79a2e958a0078d08b67
http://onehungrymind.com/build-better-angular-2-application-redux-ngrx/
https://egghead.io/lessons/angular-2-ngrx-store-in-10-minutes
https://egghead.io/lessons/javascript-redux-describing-state-changes-with-actions
change detect, 單向數據流, 函數式 , redux, flux, ngrx
http://gold.xitu.io/entry/576cb79a2e958a0078d08b67 (flux redux 前世此生)
http://blog.thoughtram.io/angular/2016/02/22/angular-2-change-detection-explained.html (angular change detection)
https://www.youtube.com/watch?v=CUxD91DWkGM ( youtube angular change detection )
redux :
keywords : dumb and smart component, redux, flux, ngrx
https://egghead.io/courses/getting-started-with-redux ( tube 教程入門 )
https://egghead.io/courses/building-react-applications-with-idiomatic-redux ( tube 教程高級 )
https://medium.com/javascript-scene/10-tips-for-better-redux-architecture-69250425af44#.yr1kwwlf4 ( 解釋 redux 基本概念 )
http://cn.redux.js.org/docs/advanced/AsyncActions.html ( 中文官方文檔, 在讀着 )
react 提出了 flux 單向數據流的概念來簡化 model 和 view 以前雙向更新帶來的複雜和難以跟蹤.
flux 只是一個抽象的概念, 通過一陣子你們的研究, 最終誕生了 redux
redux 是一種 flux 的實現 + 變種.
redux 有幾個好處.
1. 集中 state
2. undo/redo and time travel
3. 性能
4. easy test and track state
好處都圍繞在維護和性能上.
咱們不必定須要 redux, 就好像咱們不必定須要設計模式同樣, 它是一個平衡針對不一樣項目的管理方案.
redux 經過規範來達到可維護.
咱們來看看它是怎麼作的
規範 : redux 把前端全部的狀態都放入一個對象裏, 咱們叫 "store", 這比如一個數據庫.
好處 : 這樣咱們就能夠一次看完整個 appication 的狀態了.
規範 : redux 的 store 能夠直接 json 化.
好處 : 這樣能夠很容易的導出, 導入和收藏,
規範 : 要修改 state, 就必須發佈一個 action 給 store. store 會運行 reducer 建立出新的 state.
time travel 就是有了上面這 3 個規範才變的容易實現的.
規範 : reducer is immutable
好處 : 性能. immutable 在對比對象數組引用時能夠不須要 deepEqual
規範 : reducer is pure function
好處 : pure function 的好處咯, 好比容易測試.
目前看上去不太好的地方 :
1.reducer 在維護共享 state 的地方有點弱, 由於 immutable 後咱們失去了對象引用的好處. ( 目前替代引用的作法是把對象 Id 化, 引用的對象都用 Id 來替代, 而後再 convert 回去對象. )
參考 :
http://cn.redux.js.org/docs/advanced/AsyncActions.html
https://github.com/paularmstrong/normalizr
ng2 目前有 2 個 redux 的 tools
一個是 ngrx
一個是 ng2-redux
資源教程都挺少的, 因此我並無打算如今就使用 redux.
之後使用了再分享心得吧.
更新 : 2017-02-10
提醒本身
1.onPush 的狀況下,
若是咱們想經過 service share value 那麼 service 必定要提供 value change observer 讓 component transaction subscribe
若是咱們想經過 component input share value to child 的話,當 parent 要修改 value 時, 必定要使用 override referrence 的方式 ( immutable concept )
2.切記不管什麼狀況下, child component shouldn't change @input value, child 應該經過 @output 去讓 parent change value, 單向數據流 concept.
3.parent 經過 #child or viewChild 修改 child 的 property 值, child 也是不會更新的哦. 能夠在child inject ChangeDetectorRef 而後 parent 調用 changeDetectorRef.markForCheck();
4.流程 : 全部 event 運行結束後, event 觸發地(dom event only, timeout 不算) 和 markForCheck 的 component 往上到 root 都要 check, 而後一直往下一層 check @input change 直到 not change 就停.