這一篇主要是講解
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