前言
在使用antd pro的時(shí)候發(fā)現(xiàn)此框架的loading是使用dva自帶的dva-loading嗜憔。之前分析源碼的時(shí)候有看見幾個(gè)附帶工具,但沒有去研究下氏仗。今天好奇下dva-loading的實(shí)現(xiàn)吉捶。
開始
先從用法了解
loading是針對(duì)effects的,所以我們所有需要顯示loading的異步操作都需要寫在effects中才可以體現(xiàn)dva-loading的方便性皆尔,下面例子中呐舔,loading是針對(duì)namespace為form的model的submitStepForm的effect,
import createLoading from 'dva-loading';
...
app.use(createLoading());
...
@connect(({ form, loading }) => ({
submitting: loading.effects['form/submitStepForm'],
data: form.step,
}))
app.use是什么慷蠕,從源碼里知道珊拼,就是plugin.use,注冊(cè)對(duì)應(yīng)的插件鉤子的。所以createLoading返回的是
return {
extraReducers,
onEffect,
};
extraReducers:加一個(gè)state流炕,默認(rèn)名字是loading澎现,可以createLoading時(shí)傳入{namespace:xxx}
去覆蓋。
onEffect:是為了包裝所有的effect每辟,在執(zhí)行前后發(fā)出供上面reducers接收的action昔头。
所以先看前置onEffect
- 前面的if的條件是外部傳入的,可用此進(jìn)行優(yōu)化影兽,不然所有的effect都被重包裝
- 目標(biāo)effect執(zhí)行前揭斧,發(fā)出SHOW類型的action,結(jié)束后發(fā)出HIDE
- payload是model的namespace及effect的名字(在之前收集的時(shí)候,已經(jīng)給所有的effect加上namespace前綴)讹开。上面的例子就是
{namespace:'form',actionType :'form/submitStepForm'}
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
- state說(shuō)明盅视,從內(nèi)到外,effects:可知道有哪些effect在loading旦万,models:可知道有哪些model在loading闹击,gobal有沒有在的loading的effect〕伤遥可以知道后面兩個(gè)可以從第一個(gè)推理得出赏半,但dva幫我們算出來(lái),有這兩個(gè)確認(rèn)會(huì)方便淆两,比如一個(gè)功能的數(shù)據(jù)由一個(gè)model(kk)控制断箫,這就可以不管有多少effect,都可以從
loading.models['kk']
中獲得該功能的loading狀態(tài)秋冰。 - 當(dāng)接收到SHOW的action仲义,會(huì):保存payload里的namespace與actionType,并設(shè)為true,表示namespace的model和actionType的effect在加載剑勾,global一定為true,有在進(jìn)行effect
- 當(dāng)接收到HIDE的action埃撵,會(huì): 把這個(gè)payload的effect名稱actionType設(shè)為false,表示該effect加載完,如果effects中找到有namespace前綴的在loading的effect,則該model也在loading中,如果models中找到有model在loading,則該全局loading中
const initialState = {
gobal: false, // 有沒有在的loading的effect
models: {}, // 可知道有哪些model在loading
effects: {}, // 可知道有哪些effect在loading
};
const extraReducers = {
[namespace](state = initialState, { type, payload }) {
const { namespace, actionType } = payload || {};
let ret;
switch (type) {
// 保存payload里的namespace與actionType,并設(shè)為true,表示namespace的model和actionType的effect在加載虽另,
// global一定為true,有在進(jìn)行effect
case SHOW:
ret = {
...state,
global: true,
models: { ...state.models, [namespace]: true },
effects: { ...state.effects, [actionType]: true },
};
break;
case HIDE: // eslint-disable-line
// 把這個(gè)payload的effect名稱actionType設(shè)為false,表示該effect加載完
const effects = { ...state.effects, [actionType]: false };
// 如果effects中找到有namespace前綴的在loading的effect,則該model也在loading中
const models = {
...state.models,
[namespace]: Object.keys(effects).some((actionType) => {
const _namespace = actionType.split('/')[0];
if (_namespace !== namespace) return false;
return effects[actionType];
}),
};
// 如果models中找到有model在loading,則該全局loading中
const global = Object.keys(models).some((namespace) => {
return models[namespace];
});
ret = {
...state,
global,
models,
effects,
};
break;
default:
ret = state;
break;
}
return ret;
},
};