先看下官方定义
Subscriptions 是一种从源获取数据的方法,它来自于 elm。
Subscription 语义是订阅,用于订阅一个数据源,然后根据条件 dispatch 需要的 action。数据源可以是当前的时间、服务器的 websocket 连接、keyboard 输入、geolocation 变化、history 路由变化等等。
| 1 2 3 4 5 6 7 8 9 10
 | import key from 'keymaster'; ... app.model({   namespace: 'count',   subscriptions: {     keyEvent(dispatch) {       key('⌘+up, ctrl+up', () => { dispatch({type:'add'}) });     },   } });
 | 
subscriptions要求内部所有成员都是函数.
疑惑1:subscriptions里面都可以写什么
Q: 官方例子中是监听了按键事件,除此之外还可以些什么。
A: 任意自定义函数
dva-core/src/checkModel.js中可以清楚看到:
code
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
 | export default function checkModel(model, existModels) {          if (subscriptions) {                  invariant(         isPlainObject(subscriptions),         `[app.model] subscriptions should be plain object, but got ${typeof subscriptions}`,         );                  invariant(         isAllFunction(subscriptions),         `[app.model] subscription should be function`,         );     } }
 | 
疑惑2:subscriptions里的函数什么时候会被执行
首先subscriptions是定义在model里的,而一般加载model的方法是:
| 1 2 3 4 5 6 7 8 9
 | const app = dva({   history: browserHistory,   onError(error) {          } }); app.model(require('./models/app'));
 | 
所以看下dva中调用model(...)方法时做了什么:
code
| 1 2 3 4 5 6
 | function model(m) {     if (process.env.NODE_ENV !== 'production') {         checkModel(m, app._models);     }     app._models.push(prefixNamespace(m)); }
 | 
可以看到基本就是处理了一下(加上命名空间前缀)后,保存在了内部的_models数组中。
同时可以注意到,dva在生产环境中是不做模型检查的,可以提高一点儿效率,但是在开发测试环境中是开启的,便于debug
真正执行Subscriptions的地方在app.start()中
code
| 1 2 3 4 5 6 7 8 9 10 11 12
 | function start() {               const unlisteners = {};     for (const model of this._models) {       if (model.subscriptions) {         unlisteners[model.namespace] = runSubscription(model.subscriptions, model, app, onError);       }     } }
 | 
runSubscription方法定义在 code
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
 | 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 }; }
 | 
具体的调用代码如下:
| 1 2 3 4
 | const unlistener = sub({        dispatch: prefixedDispatch(app._store.dispatch, model),        history: app._history,      }, onError);
 | 
所以subscription的方法签名为({dispatch, history}, onError)。
如果subscription的返回值是function,则认为是unlistener在model从app卸载时会被调用。
在app.start之后,还可以动态注入model,这时候model里面的subscription还是会被执行的。
首先看:code
| 1 2 3 4 5 6 7
 | function start(){               app.model = injectModel.bind(app, createReducer, onError, unlisteners);     app.unmodel = unmodel.bind(app, createReducer, reducers, unlisteners); }
 | 
在app启动后,运行期间,再次调用app.model(...)其实就是调用injectModel:
code
| 1 2 3 4 5 6 7 8 9
 | function injectModel(createReducer, onError, unlisteners, m) {     model(m);           if (m.subscriptions) {          unlisteners[m.namespace] = runSubscription(m.subscriptions, m, app, onError);     } }
 |