【单向数据流的核心理念】
view -> action -> reducer -> state -> view 复制代码
在整体规划中,单一路径的数据传递是最关键的布局理念。这种情形下,情形的转变务必借助特定的渠道来引发,从而构成一个明确的信息流转整体:界面部分引发行为,状态中心进行应对,然后刷新界面。这种严苛的单向路径确实保障了情形转变的可以预见性和能够回溯。
【异步处理的直接方案】
实际开发过程中时常碰到异步情况,最直接的办法是在组件里直接启动异步操作。数据送回之后,就依照既定方式去变更数据层。尽管这种做法一目了然,却会使业务规则散落在各个组件里,给后续维护和测试带来麻烦。组件既要负责界面显示,又要处理信息获取,这样违背了职责单一法则。
export default store => next => action => { const result = next(action); if (action.type === 'ping') { store.dispatch({ type: 'pong' }) } return result; } 复制代码
【中间件解决方案】
更理想的做法是借助中介件来应对非同步的数据流。中介件能够精确调控状态变更的时刻,务必等到非同步作业告一段落才去更新状态。它的关键机制在于,在调用next()动作之后执行非同步的程序,直到非同步操作全部结束后才确认状态转换。这种策略把非同步的程序都归集起来处理,同时维持了组件的纯净特质。
【RxJS的增强能力】
运用RxJS这个工具,程序的非同步执行效率大大增强。该库内含众多功能强大的函数,例如映射、筛选、合并等,能够巧妙应对各类非同步情形。核心优势在于其流水线式的数据流转机制:初始行为序列在经过函数的加工后,会形成满足特定用途的全新数据序列。
action$.pipe( switchMap( () => fromPromise(fetch('/api/whatever')).pipe( map(res => action) ) ), catchError(() => {}) ) 复制代码
【Epic模式详解】
import { Subject } from 'rxjs/Subject'; export default (store) => { const action$ = new Subject(); action$.subscribe(store.dispatch); return next => (action) => { const result = next(action); action$.next(action); return result; }; }; 复制代码
Epic是负责处理异步流程的关键部分,其根本是一个能够接收指令序列并输出新指令序列的函数。在Epic的运作中,借助RxJS的管道机制,可以执行多种操作。
1. 过滤特定类型的动作
2. 执行异步操作(如API调用)
const epic = (action$) => { return action$.pipe( // 因为所有的action都会过来 // 所以我们只需要处理我们想要的aciton filter(action => action.type === 'GET_USER'), switchMap( // 将fetch也转化为流 () => fromPromise(fetch('/api/user/get', { method: 'POST', body: { id: 1 }, })).pipe( map(res => ({ type: 'GET_USER_SUCCESS', payload: res })), catchError(error => ({ type: 'GET_USER_FAILED', payload: error })) ) ) ) } 复制代码
3. 转换返回数据为新的动作
标准的Epic模式由:行为性质核实、非同步任务指派、成效形态变更这三个环节组成。
import { Subject } from 'rxjs/Subject'; export default (store) => { const action$ = new Subject(); const newAction$ = epic(action$); newAction$.subscribe(store.dispatch); return next => (action) => { const result = next(action); action$.next(action); return result; }; }; 复制代码
【多Epic合并策略】
实际项目要应对多个并行事件,RxJS有合并功能可以将不同Epic生成的数据流整合在一起。合并后的统一数据流会进行整体订阅,以便所有并行操作都能得到妥善应对。这就要求设立一个总Epic来汇集所有业务Epic,以此维持代码的条理性。
【状态访问优化】
export const combineEpics = (...epics) => { const merger = (...args) => merge( ...epics.map((epic) => { const output$ = epic(...args); return output$; }) ); return merger; }; 复制代码
异步操作时常要查询即时情形,能借助过渡组件把状态查询方法提供给Epic。这样Epic既能发起新指令,又能拿到最新情形数据,达成完备的流程循环。在结构繁复的情境下,可以思量把状态信息也改造成可追踪的序列。
【执行顺序控制】
必须留意,Epic的运行次序会受RxJS调度机制的作用。通常的同步调度机制可能造成意料之外的现象,比如:
1. 动作处理顺序不符合时间线
const pingEpic = action$ => action$.pipe( filter(action => action.type === 'ping'), map(() => ({ type: 'pong' })), ); const getUserEpic = action$ => action$.pipe( filter(action => action.type === 'GET_USER'), map(() => ({ type: 'GET_USER_SUCCESS', payload: { user: { name: 'kang' } } })), ); const rootEpic = combineEpics(pingEpic, getUserEpic); export default (store) => { const action$ = new Subject(); const newAction$ = rootEpic(action$); newAction$.subscribe(store.dispatch); return next => (action) => { const result = next(action); action$.next(action); return result; }; }; 复制代码
2. 异步响应动作可能被提前处理
有效的处理办法是借助恰当的调度机制,比如asyncScheduler,运用delay(0)这类手段来管理操作流程。
export default (store) => { ... const newAction$ = rootEpic(action$, store.getState); ... }; 复制代码
【实践建议】
在具体实现时建议:
const pingEpic = (action$, getState) => action$.pipe( filter(action => action.type === 'ping'), map(() => ({ type: 'pong', payload: getState() })), ); 复制代码
1. 保持Epic的单一职责
2. 使用类型系统严格定义动作
export default store => next => action => { const oldState = store.getState(); const result = next(action); const newState = store.getState(); // 类似这样的写法 if (newState.xxx !== oldState.xxx) { fetch('/api/save', { method: 'POST', body: { } }).then(() => {}).catch(() => {}) } return result; } 复制代码
3. 为复杂异步流添加详细注释
4. 对关键操作添加错误处理
这种设计兼顾了数据单向流动的长处,也能灵活应对繁杂的并发情形,非常适用于构建大型软件项目。
const saveEpic = (action$, state$) => state$.pipe( const autoSaveEpic = (action$, state$) => return action$.pipe( filter(action => action.type === 'AUTO_SAVE_ENABLE'), // 自动保存的开启 exhaustMap(() => state$.pipe( pluck('xxx'), // 获取state.xxx distinctUntilChanged(), // 前后值不同时才将其发出。 concatMap((value) => { // fetch to save }), // 自动保存的关闭 takeUntil(action$.pipe( filter(action => action.type === 'AUTO_SAVE_DISABLE') )) )) ) ) 复制代码
const countEpic = (action$, state$) => action$.pipe( filter(action => action.type === 'count'), withLatestFrom(state$), switchMap(([action, state]) => { return of({ type: 'whatever' }); }) ); 复制代码
版权声明:本文为 “博览广文网” 原创文章,转载请附上原文出处链接及本声明;
工作时间:8:00-18:00
客服电话
0755-88186625
电子邮件
admin@lanyu.com
扫码二维码
获取最新动态