angular2 学习笔记 ( 状态管理 state management )

更新 : 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 就停. 

相关文章
相关标签/搜索