原稿:语雀 · 【原理六】vuex 响应式 · 原目录:Vue › 【原理六】vuex 响应式
注入
- 首先在 vuex.js 中使用 applyMixin() 进行注入:调用 Vue.mixin(),在所有组件(全局混入)的 beforeCreate 钩子混入了 vuexInit(),因此之后创建的所有 vue 组件都会在 beforeCreate 钩子中执行 vuexInit()
- vuexInit() 执行时,会获取当前组件或父组件的 options 中的 $store 对象,当还未创建时则调用 store()
export default function (Vue) {
const version = Number(Vue.version.split('.')[0])
if (version >= 2) {
Vue.mixin({ beforeCreate: vuexInit })
} else {
//...
}
function vuexInit () {
const options = this.$options
// store injection
if (options.store) {
this.$store = typeof options.store === 'function'
? options.store()
: options.store
} else if (options.parent && options.parent.$store) {
this.$store = options.parent.$store
}
}
}
store() 执行过程:
var Store = function Store (options) {
var this$1 = this;
if ( options === void 0 ) options = {};
if (!Vue && typeof window !== 'undefined' && window.Vue) {
install(window.Vue);
}
{
assert(Vue, "must call Vue.use(Vuex) before creating a store instance.");
assert(typeof Promise !== 'undefined', "vuex requires a Promise polyfill in this browser.");
assert(this instanceof Store, "store must be called with the new operator.");
}
var plugins = options.plugins; if ( plugins === void 0 ) plugins = [];
var strict = options.strict; if ( strict === void 0 ) strict = false;
// store internal state
this._committing = false;
this._actions = Object.create(null);
this._actionSubscribers = [];
this._mutations = Object.create(null);
this._wrappedGetters = Object.create(null);
this._modules = new ModuleCollection(options);
this._modulesNamespaceMap = Object.create(null);
this._subscribers = [];
this._watcherVM = new Vue();
this._makeLocalGettersCache = Object.create(null);
// bind commit and dispatch to self
var store = this;
var ref = this;
var dispatch = ref.dispatch;
var commit = ref.commit;
this.dispatch = function boundDispatch (type, payload) {
return dispatch.call(store, type, payload)
};
响应式
以上最重要的就是 resetStoreVM(this, state),它实现了 store.state 和 store.getters 的响应式,核心代码如下
var computed = {};
forEachValue(wrappedGetters, function (fn, key) {
// use computed to leverage its lazy-caching mechanism
// direct inline function use will lead to closure preserving oldVm.
// using partial to return function with only arguments preserved in closure environment.
computed[key] = partial(fn, store);
Object.defineProperty(store.getters, key, {
get: function () { return store._vm[key]; },
enumerable: true // for local getters
});
});
// use a Vue instance to store the state tree
// suppress warnings just in case the user has added
// some funky global mixins
store._vm = new Vue({
data: {
$$state: state
},
computed: computed
});
可以看到 store._vm 实际是一个 vue 实例,传入的 state 会成为这个 vue 实例 data 中的 $$state 属性,而 getters 定义为 store._vm 上的计算属性,
Object.defineProperty() 使得 store.getters
获取到 store._vm 上得计算属性
小结
当使用了 vuex 时 2. vuex 会通过 Vue.mixin() 在全局 beforeCreate() 中注入 vuexInit() 4. 根组件执行 vuexInit() 会调用 store() 创建 store 对象,它相当于状态存放的容器;子组件执行 vueInit() 则会获取父组件 $options 上的 store 对象,不同层级的组件将状态存在一处 store 中 6. 创建 store 时调用 resetStoreVM() 以实现 state 和 getter 的响应式: 8. 创建 vue 实例 store._vm,将 store.state 作为 store._vm 的 $$state 属性 10. 同时将 store.getters 封装为一个 computed 对象作为 store._vm 上的计算属性,利用 defineProperty(),使得 store.getters
实际被定向到 store._vm 上的计算属性