1. 前言
Vuex版本:3.4.0
Vuex倉庫:https://github.com/vuejs/vuex
Vux檔案:https://vuex.vuejs.org/zh/guide/
文章時間:2020-06-09
2. 執行順序
首先看個簡單的代碼塊:
// store.js
import Vue from 'vue';
import Vuex from 'vuex';
Vue.use(Vuex);
let baseStore = new Vuex.Store({
state: {
count: 0
},
});
export default baseStore;
// app.js
import Vue from 'vue'
import store from './store'
new Vue({
el: '#app',
store
})
2.1 第一步:Vue.use(Vuex)
說明:這一步是Vuex在Vue的beforeCreate事件內增加一個回呼函式,其目的是為把初始化后的store物件掛載到this.$store,即Vue.$store,
代碼:
Vue.mixin({ beforeCreate: vuexInit });
function vuexInit() {
const options = this.$options;
// store injection store注入
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;
}
}
2.2 第二步:new Vuex.Store({})
說明:初始化具體的store物件,
2.3 第三步:new Vue({ store })
說明:這里主要是為了執行第一步的代碼,因為第一步的Vue.use(Vuex)只是注入一個回呼,內部的條件判斷options.store 和 options.parent && options.parent.$store都沒有生效,只有在這一步時才會生效,其目的就向上面說的把初始化后的store物件掛載到this.$store,這樣所有子組件就可以通過this.$store呼叫store物件,
代碼:
new Vue({
el: '#app',
router,
components: { App },
store: {
baseStore: baseStore
},
template: '<App/>'
});
3. 探究new Vuex.Store({})
說明:這里將著重介紹new Vuex.Store({})都干了什么,
注意:此處的講解都是以使用單一狀態樹為前提條件,沒有Module以及Module內的namespaced等知識點,modules這塊會單獨講解,
3.1 重新系結dispatch、commit
說明:此處重新系結dispatch、commit方法,把store自身插入到第一個引數前面,
這也是為什么我們在外部呼叫this.$store.dispatch('actionName')時,所創建的action第一個引數為store本身,
代碼:
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)
}
3.2 轉換為Module物件
說明:在這里Vuex將傳入的Vuex代碼決議為Model物件(后面將以options表示傳入的代碼塊):
new Vuex.Store({
state: {
count: 0
},
getters,
actions,
mutations
});
在Vuex原始碼中,會將options轉換為Module集合:
代碼:
// src/store.js
this._modules = new ModuleCollection(options)
其this._modules,即Model物件初始化后的欄位含義為:
root: { // Module物件
state:{
count: 0
} // 傳入Vuex({options})內的state
_children: {} // 內部嵌套Module
_rawModule: options // 傳入的options物件
}
3.3 installModule
說明:在這里將對Module進行封裝處理,
處理步驟:
1) 若module.namespaced = true : 此Module將被加入store._modulesNamespaceMap內,其key為Module嵌套的路徑,
if (module.namespaced) {
store._modulesNamespaceMap[namespace] = module
}
2) 非root Module時:子Module.state注入到父節點的state物件里,
if (!isRoot && !hot) {
const parentState = getNestedState(rootState, path.slice(0, -1)) // path.slice(0, -1) 表示只回傳前面的父節點
const moduleName = path[path.length - 1]
store._withCommit(() => {
Vue.set(parentState, moduleName, module.state)
})
}
3) 對store進行區域化,這里主要對module.namespaced= true 的module進行另外處理,其內部的成員都需要進行namespace路徑處理處理,
官方說明:默認情況下,模塊內部的 action、mutation 和 getter 是注冊在全域命名空間的——這樣使得多個模塊能夠對同一 mutation 或 action 作出回應, 如果希望你的模塊具有更高的封裝度和復用性,你可以通過添加 namespaced: true 的方式使其成為帶命名空間的模塊,當模塊被注冊后,它的所有 getter、action 及 mutation 都會自動根據模塊注冊的路徑調整命名,
4) 對module的mutation進行封裝:
①添加到store._mutations陣列內,_mutations的key默認為mutation的名稱,如果module.namespaced = true,那么key就為namespace+mutation的名稱,可多個module注冊同名,
②將module.mutation第二個引數修改為區域化的state,
const entry = store._mutations[type] || (store._mutations[type] = []);
entry.push(function wrappedMutationHandler(payload) {
handler.call(store, local.state, payload);
});
5) 對module的action進行封裝:
①添加到store._actions陣列內,_actions的key默認為action的名稱,如果module.namespaced = true,那么key就為namespace+action的名稱,可多個module注冊同名,
②將module.action第二個引數修改為區域化和root的state,
onst entry = store._actions[type] || (store._actions[type] = []);
entry.push(function wrappedActionHandler(payload) {
let res = handler.call(
store,
{
dispatch: local.dispatch,
commit: local.commit,
getters: local.getters,
state: local.state,
rootGetters: store.getters,
rootState: store.state
},
payload
);
if (!isPromise(res)) {
res = Promise.resolve(res);
}
if (store._devtoolHook) {
return res.catch((err) => {
store._devtoolHook.emit('vuex:error', err);
throw err;
});
} else {
return res;
}
});
6) 對module的getter進行封裝:
①添加到store._wrappedGetters陣列內,_wrappedGetters的key默認為action的名稱,如果module.namespaced = true,那么key就為namespace+action的名稱,只能單一注冊,
②module.mutation第一個引數修改為區域化和root的state,
if (store._wrappedGetters[type]) {
if (__DEV__) {
console.error(`[vuex] duplicate getter key: ${type}`);
}
return;
}
store._wrappedGetters[type] = function wrappedGetter(store) {
return rawGetter(
local.state, // local state
local.getters, // local getters
store.state, // root state
store.getters // root getters
);
};
7) 若當前module含有子module時,遍歷當前model的_children屬性,迭代執行installModule,
3.4 resetStoreVM
說明:初始化storevm,并負責將getter注冊為計算屬性,并保存快取特性,
處理步驟:
1) 遍歷wrappedGetters,封裝到store.getters里,
forEachValue(wrappedGetters, (fn, key) => {
// 使用computed來利用其延遲快取機制
// 直接行內函式的使用將導致保留oldVm的閉包,
// 使用partial回傳只保留閉包環境中的引數的函式,
// 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: () => store._vm[key],
enumerable: true // for local getters
})
})
2) 初始化store._vm,傳入data和computed,
說明:這里主要把state與getter(computed)進行系結,state有更改時,getter(computed)進行自動更新,采用的方式就是本地創建一個Vue物件,
store._vm = new Vue({
data: {
$$state: state
},
computed
})
3.5 注冊插件
4. commit()執行了什么
當在action內呼叫了commit方法時,其內部步驟如下:
1) 從store._mutations[]陣列內讀取對應的mutation名稱的陣列,
2) 從獲取到的_mutations[]陣列遍歷執行對應的mutation處理程式,
3) 觸發store._subscribers對應的回呼,
5. dispatch()執行了什么
在通過this.$store.dispatch()呼叫對應的action時,其內部步驟如下:
1) 與commit()呼叫的方法類似,從store._actions[]陣列內獲取對應的acticon陣列,
2) 執行active對應的處理程式并以Promise回傳,
End Web開發之路系列文章 選單加載中...
轉載請註明出處,本文鏈接:https://www.uj5u.com/qiye/73792.html
標籤:JavaScript
