【单向数据流的核心理念】
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
扫码二维码
获取最新动态
