5

ngrx

ngrx是angular的状态管理库,与react-redux相同,都是由redux的基础上延伸出来。本次演示的示例为通过ngrx的状态管理来控制HTTP请求服务的全局loading动画显示。如下:
图片描述

ngrx地址:https://github.com/ngrx

ngrx主要有四个模块,分别是
ngrx/store, ngrx/effects, ngrx/router-store, ngrx/store-devtools
本次实例用的是ngrx 4.x版本,因为没有跟路由关联,也没有复杂的行为,只用到了ngrx/store。

同react-redux 相似,ngrx的核心也是通过reducer来获取储存在store中的值(状态),通过action来改变store的中值(状态)。关于action,reducer及store不再介绍。详情参考我之前的文章:https://segmentfault.com/a/11...。ngrx和react-redux本质没什么区别。

定义loading的action和reducer

图片描述

在项目中创建ngrx文件夹,并在之下创建action和reducer文件。reducer文件夹下,创建index.ts作为各个reducer的汇总,便于之后管理拓展。

reducer

// loading =>index.ts
import * as load from '../../action/loading';

const initialState = false;

export function reducer(state: boolean = initialState, action: load.Actions) {
  switch (action.type) {
    case load.SHOW_LOADING: {
      return true;
    }

    case load.HIDE_LOADING: {
      return false;
    }
    default: {
      return state;
    }
  }
}

对loading动画的显示隐藏做定义。

// reducer=>index.ts 
// import {compose} from '@ngrx/core/compose';
// import {ActionReducer, combineReducers} from '@ngrx/store';
import * as loading from './loading';

export interface State {
  loading: boolean;
}

export const reducer = {
  loading: loading.reducer
};
// const developmentReducer: ActionReducer<any> = compose(storeFreeze, combineReducers)(reducers);
// const productionReducer: ActionReducer<any> = combineReducers(reducers);
//
// export function reducer(state: any, action: any) {
//   if (environment.production) {
//     return productionReducer(state, action);
//   } else {
//     return developmentReducer(state, action);
//   }
// }

注释掉的代码为参考官方example,将ngrx的状态管理,通过不同环境来托管。但自己本地没有把这段代码跑成功,未报错,但不起作用,就用做简单的方法来实现reducer的汇总。

关联store

ngrx的关联通过在app.module.ts将StoreModule注入reducer。即:

import {StoreModule} from '@ngrx/store';
import {reducer} from './ngrx/reducer';
@NgModule({
  declarations: [
    AppComponent
  ],
  imports: [
    StoreModule.forRoot(reducer)
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule {
}

changeDetection/async

app.conponent.ts

@Component({
  selector: 'app-root',
  changeDetection: ChangeDetectionStrategy.OnPush,
  template: `<router-outlet></router-outlet>
              <md-spinner class="loadPosition" *ngIf="showLoading$ | async"></md-spinner>`,
  styleUrls: ['./app.component.less'],
})

在loading写入根组件,这里有两点注意。

ChangeDetectionStrategy

组建变化的检查策略,以上述代码为例,当为ChangeDetectionStrategy设置OnPush时,组件就不会一直进行脏检查了,而是当输入属性变化时,才会启动检查策略,这里值的注意的是,这个输入的对象需要变化成一个新对象时,组件才会进行检查,而不是仅仅是改变属性的值,或者增减对象的元素。

比如{name:j_bleach}=>{name:bleach}
或者输入属性为一个数组的时候[1,2,3]=>[1,2,3].push(4)
以上这两种方式都不会引发angular的检查策略(前提是在元数据中设置了changeDetection: ChangeDetectionStrategy.OnPush

在此设置基础下,想要启动策略,就需要返回新的对象和数组。

如Object.assign({}, {name:j_bleach}, {name:bleach});
或者[...[1,2,3],4]这样返回一个新的对象。

而在本例中,通过reducer返回的是一个新的值(一般是一个新的对象),新的值变化也会引起组件检查。
ps:这里边个人理解是因为每一个简单类型的值,都会在新开栈上来存储,而对象不同,对象存在同一个指针的引用(是否可以类似深浅拷贝,这里打个问号);

一篇国外的文章帮助理解:https://blog.thoughtram.io/an...
不知道是否需要翻墙,再贴一篇sf上的
https://segmentfault.com/a/11...

async

官方解释:
The async pipe subscribes to an Observable or Promise and returns the latest value it has emitted. When a new value is emitted, the async pipe marks the component to be checked for changes. When the component gets destroyed, the async pipe unsubscribes automatically to avoid potential memory leaks.

高中英语水平翻译一下:async这个管道,会返回订阅的Observe ,promise对象的最新的值,当观察者发现值有改变时,就会触发组件的检查策略,并且在组件销毁时也会自动的去取消订阅避免内存泄漏。

在本例中,如果把async这个过滤器取消的话,会造成一直loading,因为组件无法在监听到控制loading值的改变,也就无法更新视图了。

触发action,隐藏loading

最后再贴一句,触发action的代码

// 控制loading显示隐藏,结合之前的action
this.store.dispatch(new load.ShowAction());
this.store.dispatch(new load.HideAction());

总结

一个非常简单的小demo,通过ngrx控制项目loading动画,但说实话,代码零零散散加起来还挺多的,所以写的很乱,抱歉啦~很多东西不好在博客里面呈现,有兴趣的同学可以把项目down下来看一下。
项目地址:https://github.com/jiwenjiang...


j_bleach
2.5k 声望70 粉丝

1 >> 1