欢送关注公众号【码上出击】,更多精彩内容敬请关注公众号最新消息。

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. 论断

从代码中我么能够得出以下论断:

  1. subscriptions 中配置的key的名称没有任何束缚,而且只有在app.unmodel的时候才有用。
  2. subscriptions 中配置的只能dispatch所在model的reducer和effects。
  3. subscriptions 中配置的函数只会执行一次,也就是在调用 app.start() 的时候,会遍历所有 model 中的 subscriptions 执行一遍。
  4. 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)      }    }  },      // ......};