这一篇主要是讲解
vuex
的大概实现,以及一些细节的说明。vue
vuex
是如何实现的?先从install
方法看,安装插件的方法实现比较简单,调用applyMixin
,最后执行的是这段逻辑Vue.mixin({ beforeCreate: vuexInit })
,其实是在每一个组件建立时混入store
实例。因此咱们能够在每一个组件上获取到store
实例上的数据。react
if (version >= 2) {
Vue.mixin({ beforeCreate: vuexInit })
} else {
// override init and inject vuex init procedure
// for 1.x backwards compatibility.
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
}
}
复制代码
function install (_Vue) {
if (Vue && _Vue === Vue) {
if (process.env.NODE_ENV !== 'production') {
console.error(
'[vuex] already installed. Vue.use(Vuex) should be called only once.'
)
}
return
}
Vue = _Vue
applyMixin(Vue)
}
复制代码
接下来看看store
这个类的代码:vuex
在这个类上暴露了commit
和dispatch
方法,而且绑定了调用上下文为store
的实例。dispatch
支持异步更新数据是由于它内部的实现就是使用了promise
。promise
// bind commit and dispatch to self
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)
}
复制代码
下面两个方法是vuex
的实现主要逻辑,installModule
的具体逻辑不细说,从注释上咱们能够知道,这是一个初始化整个vuex
配置生成对象的方法。根据配置进行递归执行。bash
// init root module.
// this also recursively registers all sub-modules
// and collects all module getters inside this._wrappedGetters
installModule(this, state, [], this._modules.root)
// initialize the store vm, which is responsible for the reactivity
// (also registers _wrappedGetters as computed properties)
resetStoreVM(this, state)
复制代码
resetStoreVM
方法比较重要,下面是该方法的详细代码:app
vuex
中的getter
其实是使用vue
计算属性实现的,Object.defineProperty
里定义了getter
的get
方法。store._vm
实际就是vuex
把installModule
生成的对象改形成响应式的数据,经过一个新的vue
实例。vuex
的响应数据,须要销毁旧的实例,回收内存。function resetStoreVM (store, state, hot) {
const oldVm = store._vm
// bind store public getters
store.getters = {}
const wrappedGetters = store._wrappedGetters
const computed = {}
forEachValue(wrappedGetters, (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 enviroment.
computed[key] = partial(fn, store)
Object.defineProperty(store.getters, key, {
get: () => 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
const silent = Vue.config.silent
Vue.config.silent = true
store._vm = new Vue({
data: {
$$state: state
},
computed
})
Vue.config.silent = silent
// enable strict mode for new vm
if (store.strict) {
enableStrictMode(store)
}
if (oldVm) {
if (hot) {
// dispatch changes in all subscribed watchers
// to force getter re-evaluation for hot reloading.
store._withCommit(() => {
oldVm._data.$$state = null
})
}
Vue.nextTick(() => oldVm.$destroy())
}
}
复制代码
vuex
的数据是如何响应到视图?vuex
的state
和getter
实际上就是对应的store._vm
(vue
实例)中data
和computed
,所以getter
所依赖的state
最后是经过watcher
管理的。vuex
,vuex
生成的实例对象在vue
实例化过程当中被改形成响应式的数据,当咱们有多个页面组件使用了vuex
的数据,其实也是经过watcher
管理,所以当咱们使用commit
或dispatch
修改数据,最后触发了setter
去通知全部订阅者(watcher
)更新。vuex
的设计其实并不复杂,简单的来说,就是一个对象,经过内部的方法管理内部的属性和读取内部的属性。异步
而实现的过程,则是经过一系列方法把咱们的配置生成一个root
对象,而后利用vue
实现内部数据的响应与依赖管理。而这里比较核心的部分则是当数据发生变化时,如何响应到对应的视图部分以及getter
的依赖管理,这些逻辑的实现最后都是经过watcher
。ide