dva-loading
概述
dva-loading 是 dva 的插件,可以在 store 里面自动添加 loading 状态,具体使用方法可以看这里
版本
2.0.6
源码地址
dva/packages/dva-loading/src/index.js
解析
dva-loading 给外部暴露的就是 createLoading 这个方法,接下来我们就来看看这个方法做了什么。
- 定义 namespace: namespace 是用户可以自定义的参数,如果未定义则为 'loading'
- 对 only 和 except 做校验: 这两个参数也是外部传入的,只不过没有在文档上找到它们的用法
- 定义 initialState: loading store 的初始状态,它包含三个属性:global modals 和 effects
- 返回 hooks: 返回了 extraReducers 和 onEffect 这两个 hooks 那么接下来我们就来看下 extraReducers 和 onEffect 这两个 hooks。
const SHOW = '@@DVA_LOADING/SHOW';
const HIDE = '@@DVA_LOADING/HIDE';
const NAMESPACE = 'loading';
function createLoading(opts = {}) {
const namespace = opts.namespace || NAMESPACE;
const { only = [], except = [] } = opts;
if (only.length > 0 && except.length > 0) {
throw Error('It is ambiguous to configurate `only` and `except` items at the same time.');
}
const initialState = {
global: false,
models: {},
effects: {},
};
const extraReducers = {
// ......
};
function onEffect(effect, { put }, model, actionType) {
// ......
}
return {
extraReducers,
onEffect,
};
}
onEffect
onEffect 方法定义了如何处理 saga 的 effect
- 首先对 only 和 except 进行判断,在这里我们发现了 only 和 except 的作用,only 表示只对哪些 action 处理,except 表示哪些 action 不处理,所以这里需要对它们做一个判断
- 如果 only 和 except 都为空则表示对所有 action 都处理
- 如果 only 数组里面包含 action 则处理
- 如果 except 数组里面不包含 action 则处理
- 如果以上情况都不成立则不处理直接返回 effect
- 如果上面的判断成立则对外部传入的 effect 方法做些额外的处理:
- 在调用 effect 方法之前先调用 saga put 派发一个 SHOW action
- 调用 effect 方法
- 在调用 effect 方法完成后调用 saga put 派发一个 HIDE action 接下来我们来看一下 extraReducers
提示
关于 onEffect 的内部实现可以去看 dva 源码解析中的 getSaga。
function onEffect(effect, { put }, model, actionType) {
const { namespace } = model;
if (
(only.length === 0 && except.length === 0)
|| (only.length > 0 && only.indexOf(actionType) !== -1)
|| (except.length > 0 && except.indexOf(actionType) === -1)
) {
return function*(...args) {
yield put({ type: SHOW, payload: { namespace, actionType } });
yield effect(...args);
yield put({ type: HIDE, payload: { namespace, actionType } });
};
} else {
return effect;
}
}
extraReducers
在这个 reducer 里面处理了两个 action,这两个 action 就是上面触发的 SHOW 和 HIDE
- SHOW: 对于 SHOW action,reducer 会将 global 置为 true,models 里面对应的 namespace 置为 true;effects 里面对应的 actionType 置为 true
- HIDE: 对于 HIDE action,reducer 会首先将 effects 里面对应的 actionType 置为 true,然后对所有 effects 做 some 遍历对比当前的 namespace 和 每一个 effect 的 namespace 如果相等就返回 effects[actionType] 否则返回 false 最终的结果就是 models 里面对应的 namespace的结果;最后对 models keys 做 some 遍历条件是返回 models[namespace] 最终结果就是 globals 的值
这里最后总结一下: - SHOW: 这个逻辑比较简单就是只要调用了 effect 就全部设置为 true
- HIDE: 首先 effects 对象里面对应的 effect 肯定设为 false,对于 models 只要任何 effects 里面能和当前 namespace 匹配上并且只要有一个为 true 就为 true 否则为 false;对于 globals 只要 models 里面有一项为 true 就为 true 否则为 false
提示
关于 extraReducers 的内部实现可以去看 dva 源码解析中的 core.create() 方法。
const extraReducers = {
[namespace](state = initialState, { type, payload }) {
const { namespace, actionType } = payload || {};
let ret;
switch (type) {
case SHOW:
ret = {
...state,
global: true,
models: { ...state.models, [namespace]: true },
effects: { ...state.effects, [actionType]: true },
};
break;
case HIDE: // eslint-disable-line
const effects = { ...state.effects, [actionType]: false };
const models = {
...state.models,
[namespace]: Object.keys(effects).some((actionType) => {
const _namespace = actionType.split('/')[0];
if (_namespace !== namespace) return false;
return effects[actionType];
}),
};
const global = Object.keys(models).some((namespace) => {
return models[namespace];
});
ret = {
...state,
global,
models,
effects,
};
break;
default:
ret = state;
break;
}
return ret;
},
};