欢送关注公众号【码上出击
】,更多精彩内容敬请关注公众号最新消息。
demo地址
Q:dva中的 subscriptions 到底是干嘛用的。
A:如果你须要订阅一些数据,并且解决数据后的逻辑仅与以后model相干,那么就应该用 subscriptions 。
官网文档对于subscriptions的形容太简略了,以至很多同学对这个概念不是很分明。
1. 代码剖析
从源码中摘取进去了与subscription无关的要害代码如下:
// index.jsimport { 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.jsexport 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 subscriptionsconst 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.jsexport 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) } } }, // ......};