vuex插件
有了上文作为铺垫,我们就可以很轻松的来解释vuex的原理了。
Vuex仅仅是Vue的一个插件。Vuex只能使用在vue上,因为其高度依赖于Vue的双向绑定和插件系统。
Vuex的注入代码比较简单,调用了一下applyMixin方法,现在的版本其实就是调用了Vue.mixin,在所有组件的 beforeCreate生命周期注入了设置 this.$store这样一个对象。
// src/store.js
export function install (_Vue) {
if (Vue && _Vue === Vue) {
return
}
Vue = _Vue
applyMixin(Vue)
}
// src/mixins.js
export default function (Vue) {
const version = Number(Vue.version.split('.')[0])
if (version >= 2) {
Vue.mixin({ beforeCreate: vuexInit })
} else {
const _init = Vue.prototype._init
Vue.prototype._init = function (options = {}) {
options.init = options.init
? [vuexInit].concat(options.init)
: vuexInit
_init.call(this, options)
}
}
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
}
}
}
划重点:1行代码 Vue.mixin
那么 Vuex.Store 是如何实现的呢?
// src/store.js
constructor (options = {}) {
const {
plugins = [],
strict = false
} = options
// 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()
const store = this
const { dispatch, commit } = this
this.dispatch = function boundDispatch (type, payload) {
return dispatch.call(store, type, payload)
}
this.commit = function boundCommit (type, payload, options) {
return commit.call(store, type, payload, options)
}
// strict mode
this.strict = strict
const state = this._modules.root.state
// init root module.
// this also recursively registers all sub-modules
// and collects all module getters inside this._wrappedGetters
installModule(this, state, [], this._modules.root)
resetStoreVM(this, state)
// apply plugins
plugins.forEach(plugin => plugin(this))
}
划重点:其实上面的代码绝大部分都不需要关注的 - -。
其实重点就是一行代码 resetStoreVM(this, state)。
那么 resetStoreVM 里面是什么呢?
// src/store.js
function resetStoreVM (store, state, hot) {
Vue.config.silent = true
store._vm = new Vue({
data: {
$$state: state
},
computed
})
}
划重点: 还是一行代码:new Vue。通过 Vue自己的双向绑定然后注入
你是不是以为就这样结束了呢? NoNoNo,当你再Vue中通过this如果调用store的数据呢?
// 当获取state时,返回以双向绑定的$$sate
var prototypeAccessors$1 = { state: { configurable: true } };
prototypeAccessors$1.state.get = function () {
return this._vm._data.$$state
};
// 将state定义在原型中
Object.defineProperties( Store.prototype, prototypeAccessors$1 );
其实就是获取 this._vm._data.$$state 而已
总结
Vue的双向绑定通过调用new Vue实现,然后通过Vue.mixin注入到Vue的生命周期中,再通过劫持state.get将数据放入组件中。