共计 4045 个字符,预计需要花费 11 分钟才能阅读完成。
欢送关注公众号【 码上出击
】,更多精彩内容敬请关注公众号最新消息。
demo 地址
Q:dva 中的 subscriptions 到底是干嘛用的。
A:如果你须要订阅一些数据,并且解决数据后的逻辑仅与以后 model 相干,那么就应该用 subscriptions。
官网文档对于 subscriptions 的形容太简略了,以至很多同学对这个概念不是很分明。
1. 代码剖析
从源码中摘取进去了与 subscription 无关的要害代码如下:
// index.js
import {run as runSubscription, unlisten as unlistenSubscription} from './subscription';
/**
* Create dva-core instance.
*/
export function create(hooksAndOpts = {}, createOpts = {}) {
// ......
const app = {_models: [prefixNamespace({ ...dvaModel})],
_store: null,
_plugin: plugin,
use: plugin.use.bind(plugin),
model,
start,
};
return app;
// ......
/**
* Register model before app is started.
*/
function model(m) {if (process.env.NODE_ENV !== 'production') {checkModel(m, app._models);
}
const prefixedModel = prefixNamespace({...m});
app._models.push(prefixedModel);
return prefixedModel;
}
/**
* Inject model after app is started.
*/
function injectModel(createReducer, onError, unlisteners, m) {m = model(m);
const store = app._store;
store.asyncReducers[m.namespace] = getReducer(m.reducers, m.state, plugin._handleActions);
store.replaceReducer(createReducer());
if (m.effects) {store.runSaga(app._getSaga(m.effects, m, onError, plugin.get('onEffect'), hooksAndOpts));
}
if (m.subscriptions) {unlisteners[m.namespace] = runSubscription(m.subscriptions, m, app, onError);
}
}
/**
* Unregister model.
*/
function unmodel(createReducer, reducers, unlisteners, namespace) {
const store = app._store;
// Delete reducers
delete store.asyncReducers[namespace];
delete reducers[namespace];
store.replaceReducer(createReducer());
store.dispatch({type: '@@dva/UPDATE'});
// Cancel effects
store.dispatch({type: `${namespace}/@@CANCEL_EFFECTS` });
// Unlisten subscrioptions
unlistenSubscription(unlisteners, namespace);
// Delete model from app._models
app._models = app._models.filter(model => model.namespace !== namespace);
}
/**
* Start the app.
*
* @returns void
*/
function start() {
// ......
// Run subscriptions
const unlisteners = {};
for (const model of this._models) {if (model.subscriptions) {unlisteners[model.namespace] = runSubscription(model.subscriptions, model, app, onError);
}
}
// Setup app.model and app.unmodel
app.model = injectModel.bind(app, createReducer, onError, unlisteners);
app.unmodel = unmodel.bind(app, createReducer, reducers, unlisteners);
app.replaceModel = replaceModel.bind(app, createReducer, reducers, unlisteners, onError);
// ......
}
}
// subscription.js
export function run(subs, model, app, onError) {const funcs = [];
const nonFuncs = [];
for (const key in subs) {if (Object.prototype.hasOwnProperty.call(subs, key)) {const sub = subs[key];
const unlistener = sub(
{dispatch: prefixedDispatch(app._store.dispatch, model),
history: app._history,
},
onError,
);
if (isFunction(unlistener)) {funcs.push(unlistener);
} else {nonFuncs.push(key);
}
}
}
return {funcs, nonFuncs};
}
run 办法做的事件就是把 model 中配置的 subscriptions 遍历执行,并且将 dispatch 办法和 history 对象做为参数传给配置的每一个 subscription。
从代码上咱们能够看到,start 办法执行时,会将 app.model 注册进来的所有 model.subscriptions 遍历执行,并且将执行后的返回值收集到了 unlisteners[model.namespace] 中,供 app.unmodel(namespace) 时勾销订阅数据源用。
如果 subscriptions 没有返回函数,调用 app.unmodel 时会正告。
// Run subscriptions
const unlisteners = {};
for (const model of this._models) {if (model.subscriptions) {unlisteners[model.namespace] = runSubscription(model.subscriptions, model, app, onError);
}
}
另外,在 subscription.js 中的 run 办法中,将 prefixedDispatch(app._store.dispatch, model) 做为 dispatch 传给了 subscription 配置的办法,精简后的代码如下:
function prefixedDispatch(dispatch, model){
return action => {
app._store.dispatch({
...action,
type: `${model.namespace}${NAMESPACE_SEP}${action.type}`
})
}
}
因而,能够看出,在 subscriptions 中,只能 dispatch 以后 model 中的 reducer 和 effects。
2. 论断
从代码中我么能够得出以下论断:
- subscriptions 中配置的 key 的名称没有任何束缚,而且只有在 app.unmodel 的时候才有用。
- subscriptions 中配置的只能 dispatch 所在 model 的 reducer 和 effects。
- subscriptions 中配置的函数只会执行一次,也就是在调用 app.start() 的时候,会遍历所有 model 中的 subscriptions 执行一遍。
- subscriptions 中配置的函数须要返回一个函数,该函数应该用来勾销订阅的该数据源。
3. 代码示例
// models/Products.js
export default {
namespace: 'products',
// ......
subscriptions: {setupxxx({ dispatch, history}) {
// history.listen 执行后会返回 unlisten 函数
return history.listen(({pathname, query}) => {console.log('history')
});
},
i_am_just_a_name({dispatch}){console.log('into')
function handleClick() {console.log('hello')
dispatch({
type: 'delete',
payload: 1
})
}
document.addEventListener('click', handleClick);
// 此处返回一个函数,用来移除 click 事件
return () => {document.removeEventListener('click', handleClick)
}
}
},
// ......
};