欢迎关注公众号【码上出击
】,更多精彩内容敬请关注公众号最新消息。
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) 时取消订阅数据源用。
// 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)
}
}
},
// ......
};
常见问题FAQ
- 免费下载或者VIP会员专享资源能否直接商用?
- 本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
- 提示下载完但解压或打开不了?
- 找不到素材资源介绍文章里的示例图片?
- 模板不会安装或需要功能定制以及二次开发?
发表评论
还没有评论,快来抢沙发吧!