vuex官方文檔vue
Vuex 是一個專爲 Vue.js 應用程序開發的 狀態管理模式。它採用集中式存儲管理應用的全部組件的狀態,並以相應的規則保證狀態以一種可預測的方式發生變化
每個 Vuex 應用的核心就是 store(倉庫)。「store」基本上就是一個容器,它包含着你的應用中大部分的狀態 (state)。Vuex 和單純的全局對象有如下兩點不一樣:git
import Vue from 'vue'; import Vuex from 'vuex'; // 1.Vue.use(Vuex); Vuex是一個對象 install方法 // 2.Vuex中有一個Store類 // 3.混入到組件中 增添store屬性 Vue.use(Vuex); // 使用這個插件 內部會調用Vuex中的install方法 const store = new Vuex.Store({ state:{ // -> data age:10 }, getters:{ // 計算屬性 myAge(state){ return state.age + 20 } }, mutations:{ // method=> 同步的更改state mutation的參數是狀態 changeAge(state,payload){ state.age += payload; // 更新age屬性 } }, actions:{ // 異步操做作完後將結果提交給mutations changeAge({commit},payload){ setTimeout(() => { commit('changeAge',payload) }, 1000); } }}); export default store;
經過用法能夠知道:算法
因而能夠梳理好入口文件 vuex
vuex/index.js設計模式
import { Store, install } from './store'; // 這個文件是入口文件,核心就是導出全部寫好的方法 export default { Store, install }
vuex/store.jsapi
export let Vue; export class Store { } // _vue 是Vue的構造函數 export const install = (_vue) => { // 須要保存Vue,用戶傳入的Vue構造函數 Vue = _vue; }
接下來就是把store掛載到每一個組件上面,這樣數據才能互通共享,很顯然,經過Vue.mixin 在Vue生命週期beforeCreate 能夠爲每一個組件注入store;數組
import applyMixin from "./mixin";export let Vue; export class Store { } // _vue 是Vue的構造函數 export const install = (_vue) => { // 須要保存Vue,用戶傳入的Vue構造函數 Vue = _vue; // 須要將根組件中注入的store 分派給每個組件 (子組件) Vue.mixin applyMixin(Vue); }
vuex/mixin.js緩存
export default function applyMixin(Vue) { // 父子組件的beforecreate執行順序 Vue.mixin({ // 內部會把生命週期函數 拍平成一個數組 beforeCreate: vuexInit });} // 組件渲染時從父=》子 function vuexInit() { // 給全部的組件增長$store 屬性 指向咱們建立的store實例 const options = this.$options; // 獲取用戶全部的選項 if (options.store) { // 根實例(只有根實例纔會有store屬性) this.$store = options.store; } else if (options.parent && options.parent.$store) { // 兒子 或者孫子.... // 後面的每個都從其父組件拿到store this.$store = options.parent.$store; }}
接下來就是處理state,getters,mutations,actionsapp
export class Store { constructor(options) { const state = options.state; //數據變化要更新視圖 (vue的核心邏輯依賴收集) this._vm = new Vue({ data: { // 屬性若是是經過$開頭的 默認不會將這個屬性掛載到vm上 $$store: state } }) } get state() { // 屬性訪問器 new Store().state Object.defineProperty({get()}) return this._vm._data.$$state } }
首先來處理state,options是用戶傳入的,其中有state,getters,mutations,actions,天然能夠在options.state中取到,可是此時state還不是響應式,能夠藉助new Vue中data的數據是響應式處理這個問題,將state掛載到$$state上,這個屬性是不會被vue暴露出去(多是內部作了處理)。當咱們在組件中去獲取值的時候,好比this.$store.state.age時候 this.$store.state 就走到到了訪問器get state() 就會將整個倉庫的state返回出去,並且數據是響應式的。至於爲何在_vm._data上,須要去看下vue源碼實現。異步
export class Store { constructor(options) { // 1.處理state const state = options.state; //數據變化要更新視圖 (vue的核心邏輯依賴收集) this._vm = new Vue({ data: { // 屬性若是是經過$開頭的 默認不會將這個屬性掛載到vm上 $$store: state } }) // 2.處理getters屬性 具備緩存的 computed 帶有緩存 (屢次取值是若是值不變是不會從新取值) this.getters = {}; Object.key(options.getters).forEach(key => { Object.defineProperty(this.getters, key, { get: () => options.getters[key](this.state) }) }) } get state() { // 屬性訪問器 new Store().state Object.defineProperty({get()}) return this._vm._data.$$state } }
經過循環用戶傳進來的getters,再經過Object.defineProperty把每個getter放入store中。不過目前每一次取值都會從新計算,沒有緩存功能,不符合vue計算屬性的用法以及定義。
先來改造下對象遍歷這個方法,由於這個方法後面用的比較多。
vuex/util.js
export const forEachValue = (obj, callback) => { Object.keys(obj).forEach(key => callback(obj[key], key)) }
export class Store { constructor(options) { // 1.處理state const state = options.state; //數據變化要更新視圖 (vue的核心邏輯依賴收集) this._vm = new Vue({ data: { // 屬性若是是經過$開頭的 默認不會將這個屬性掛載到vm上 $$store: state } }) // 2.處理getters屬性 具備緩存的 computed 帶有緩存 (屢次取值是若是值不變是不會從新取值) this.getters = {}; forEachValue(options.getters, (fn, key) => { Object.defineProperty(this.getters, key, { get: () => fn(this.state) }) }) } get state() { // 屬性訪問器 new Store().state Object.defineProperty({get()}) return this._vm._data.$$state } }
邏輯都是同樣的,接着處理下緩存功能。
export class Store { constructor(options) { // 1.處理state const state = options.state; //數據變化要更新視圖 (vue的核心邏輯依賴收集) const computed = {}; // 2.處理getters屬性 具備緩存的 computed 帶有緩存 (屢次取值是若是值不變是不會從新取值) this.getters = {}; forEachValue(options.getters, (fn, key) => { // 將用戶的getters 定義在實例上, 計算屬性是如何實現緩存 computed[key] = () => fn(this.state); // 當取值的時候執行計算屬性的邏輯,此時就有緩存功能 Object.defineProperty(this.getters, key, { get: () => fn(this._vm[key]) }) }) this._vm = new Vue({ data: { // 屬性若是是經過$開頭的 默認不會將這個屬性掛載到vm上 $$store: state }, computed, }) } get state() { // 屬性訪問器 new Store().state Object.defineProperty({get()}) return this._vm._data.$$state } }
computed具備緩存功能,能夠在用戶傳入的getters的時候,將用戶的getters 定義在實例上,computed[key] = () => fn(this.state) ,在取值的時候fn(this._vm[key])執行計算屬性的邏輯。vuex的做者真是腦洞大開,鬼才啊,這都能想到。
mutation 都有一個字符串的 事件類型 (type) 和 一個 回調函數 (handler)
// 3.實現mutations this.mutations = {};forEachValue(options.mutations, (fn, key) => { this.mutations[key] = (payload) => fn(this.state, payload) }) commit = (type, payload) => { //保證當前this 當前store實例 this.mutations[type](payload) }
commit使用箭頭函數是爲了保證調用的都是當前實例,一是經過this.commit(type,data),二是在action中被解構使用changeAge({commit},payload){}
actions和dispath也是如此。
完整的Store類
export class Store { constructor(options) { // 1.處理state const state = options.state; //數據變化要更新視圖 (vue的核心邏輯依賴收集) const computed = {}; // 2.處理getters屬性 具備緩存的 computed 帶有緩存 (屢次取值是若是值不變是不會從新取值) this.getters = {}; forEachValue(options.getters, (fn, key) => { // 將用戶的getters 定義在實例上, 計算屬性是如何實現緩存 computed[key] = () => fn(this.state); // 當取值的時候執行計算屬性的邏輯,此時就有緩存功能 Object.defineProperty(this.getters, key, { get: () => fn(this._vm[key]) }) }) this._vm = new Vue({ data: { // 屬性若是是經過$開頭的 默認不會將這個屬性掛載到vm上 $$store: state }, computed, }) // 3.實現mutations this.mutations = {}; forEachValue(options.mutations, (fn, key) => { this.mutations[key] = (payload) => fn(this.state, payload) }) // 4.實現actions this.actions = {}; forEachValue(options.actions, (fn, key) => { this.actions[key] = (payload) => fn(this, payload); }); } commit = (type, payload) => { //保證當前this 當前store實例 this.mutations[type](payload) } dispatch = (type, payload) => { this.mutations[type](payload) } get state() { // 屬性訪問器 new Store().state Object.defineProperty({get()}) return this._vm._data.$$state } }
import applyMixin from "./mixin";import { forEachValue } from './util';export let Vue; export class Store { constructor(options) { // 1.處理state const state = options.state; //數據變化要更新視圖 (vue的核心邏輯依賴收集) const computed = {}; // 2.處理getters屬性 具備緩存的 computed 帶有緩存 (屢次取值是若是值不變是不會從新取值) this.getters = {}; forEachValue(options.getters, (fn, key) => { // 將用戶的getters 定義在實例上, 計算屬性是如何實現緩存 computed[key] = () => fn(this.state); // 當取值的時候執行計算屬性的邏輯,此時就有緩存功能 Object.defineProperty(this.getters, key, { get: () => fn(this._vm[key]) }) }) this._vm = new Vue({ data: { // 屬性若是是經過$開頭的 默認不會將這個屬性掛載到vm上 $$store: state }, computed, }) // 3.實現mutations this.mutations = {}; forEachValue(options.mutations, (fn, key) => { this.mutations[key] = (payload) => fn(this.state, payload) }) // 4.實現actions this.actions = {}; forEachValue(options.actions, (fn, key) => { this.actions[key] = (payload) => fn(this, payload); }); } commit = (type, payload) => { //保證當前this 當前store實例 this.mutations[type](payload) } dispatch = (type, payload) => { this.mutations[type](payload) } get state() { // 屬性訪問器 new Store().state Object.defineProperty({get()}) return this._vm._data.$$state } } // _vue 是Vue的構造函數 export const install = (_vue) => { // 須要保存Vue,用戶傳入的Vue構造函數 Vue = _vue; // 須要將根組件中注入的store 分派給每個組件 (子組件) Vue.mixin applyMixin(Vue); }
簡易版的vuex到此完成。接下來就是要處理module。
咱們實現了一個簡易版的Vuex,對state,actions,mutations,getters 進行了功能的實現。可是沒有對modules進行處理,其實modules纔是Vuex中最核心而且是最難實現的。
Vuex 容許咱們將 store 分割成大大小小的對象,每一個對象也都擁有本身的 state、getter、mutation、action,這個對象咱們把它叫作 module(模塊),在模塊中還能夠繼續嵌套子模塊。
Vuex中能夠爲每一個模塊添加namespaced: true來標記爲當前模塊劃分一個命名空間,接下來看下具體怎麼實現一個完整的Vuex。
整體思路能夠分爲如下:
import ModuleCollection from './module/module-collection' export let Vue; export class Store { constructor(options) { const state = options.state; //數據變化要更新視圖 (vue的核心邏輯依賴收集) // 1.模塊收集 this._modules = new ModuleCollection(options); }}
ModuleCollection 類的實現
這個類是收集模塊,格式化數據用的,那咱們先要知道須要什麼樣的格式。
this.root = { _raw: '根模塊', _children:{ a:{ _raw:"a模塊", _children:{ c:{ ..... } }, state:'a的狀態' }, b:{ _raw:"b模塊", _children:{}, state:'b的狀態' } }, state:'根模塊本身的狀態' }``` 最終須要的是這樣一個數結構。
export default class ModuleCollection {
constructor(options) {
// 註冊模塊 須要用到棧結構數據,[根,a],每次循環遞歸的時候將其入棧。這樣每一個模塊能夠清楚的知道本身的父級是誰
this.register([], options)
}
register(path, rootModule) {
// 格式化後的結果
let newModule = {_raw: rootModule, // 用戶定義的模塊
_children: {}, // 模塊的兒子
state: {} // 當前模塊的狀態
}
if (path.length === 0) { // 說明是根模塊
this.root = newModule }
// 用戶在模塊中傳了modules屬性
if (rootModule.modules) {
// 循環模塊 module模塊的定義 moduleName模塊的名字
forEachValue(rootModule.modules, (module, moduleName) => {
this.register(path.concat(moduleName), module)
}) } }
}
第一次進來的時候path是空數組,root就是用戶傳進去的模塊對象;若是模塊有modules屬性,須要循環去註冊這個模塊。path.concat(moduleName) 就返回了[a,c]相似的格式。 接下來看下path不爲空的時候
if (path.length === 0) { // 說明是根模塊
this.root = newModule} else {
// this.register(path.concat(moduleName), module); 遞歸註冊前會把module 的名放在 path的位
this.root._children[path[path.length -1]] = newModule}
path[path.length -1] 能夠取到最後一項,也就是模塊的兒子模塊。這裏咱們用的是this.root._children[path[path.length -1]] = newModule。這樣寫會把有多層路徑的模塊最後一項也提到和它平級,所以須要肯定這個模塊的父級是誰,再把當前模塊掛到父級就okl了
if (path.length === 0) { // 說明是根模塊
this.root = newModule} else {
// this.register(path.concat(moduleName), module); 遞歸註冊前會把module 的名放在 path的位
// path.splice(0, -1) 是最後一項,是須要被掛的模塊
let parent = path.splice(0, -1).reduce((memo, current) => {
return memo._children[current];
}, this.root);
parent._children[path[path.length - 1]] = newModule}
### 模塊的安裝 將全部module收集後須要對收集到數據進行整理 - state數據要合併。 **經過Vue.set(parent,path[path.length-1],rootModule.state),既能夠合併,又能使使 module數據成爲響應式數據;** - action 和mutation 中方法訂閱(數組)
// 1.模塊收集
this._modules = new ModuleCollection(options);
// 2.安裝模塊 根模塊的狀態中 要將子模塊經過模塊名 定義在根模塊上
installModule(this, state, [], this._modules.root);
this就是store, 須要完成installModule方法。installModule中傳入的有當前模塊,這個模塊可能有本身的方法。爲此先改造下代碼,建立Module類。
import { forEachValue } from '../util';
class Module {
get namespaced() {
return !!this._raw.namespaced
}
constructor(newModule) {
this._raw = newModule; this._children = {}; this.state = newModule.state
}
getChild(key) {
return this._children[key];
}
addChild(key, module) {
this._children[key] = module
}
// 給模塊繼續擴展方法
}
export default Module;
ModuleCollection中相應的地方稍做修改。
import Module from './module'
export default class ModuleCollection {
constructor(options) {
// 註冊模塊 須要用到棧結構數據,[根,a],每次循環遞歸的時候將其入棧。這樣每一個模塊能夠清楚的知道本身的父級是誰
this.register([], options)
}
register(path, rootModule) {
// 格式化後的結果
let newModule = new Module(rootModule)
if (path.length === 0) { // 說明是根模塊
this.root = newModule } else { // this.register(path.concat(moduleName), module); 遞歸註冊前會把module 的名放在 path的位
// path.splice(0, -1) 是最後一項,是須要被掛的模塊
let parent = path.splice(0, -1).reduce((memo, current) => {
return memo.getChild(current);
}, this.root);
parent.addChild(path[path.length - 1], newModule) }
// 用戶在模塊中傳了modules屬性
if (rootModule.modules) {
// 循環模塊 module模塊的定義 moduleName模塊的名字
forEachValue(rootModule.modules, (module, moduleName) => {
this.register(path.concat(moduleName), module)
}) }
}}
function installModule(store, rootState, path, module) {
// 這裏我須要遍歷當前模塊上的 actions、mutation、getters 都把他定義在store的_actions, _mutations, _wrappedGetters 中
}
installModule 就須要循環對當前模塊處理對應的actions、mutation、getters。爲此能夠對Module類增長方法,來讓其內部本身處理。
import { forEachValue } from '../util';
class Module {
constructor(newModule) {
this._raw = newModule; this._children = {}; this.state = newModule.state
}
getChild(key) {
return this._children[key];
}
addChild(key, module) {
this._children[key] = module
}
// 給模塊繼續擴展方法
forEachMutation(fn) {
if (this._raw.mutations) {
forEachValue(this._raw.mutations, fn)
} }
forEachAction(fn) {
if (this._raw.actions) {
forEachValue(this._raw.actions, fn);
} }
forEachGetter(fn) {
if (this._raw.getters) {
forEachValue(this._raw.getters, fn);
} }
forEachChild(fn) {
forEachValue(this._children, fn);
}}
export default Module;
function installModule(store, rootState, path, module) {
// 這裏我須要遍歷當前模塊上的 actions、mutation、getters 都把他定義在store的_actions, _mutations, _wrappedGetters 中
// 處理mutation
module.forEachMutation((mutation, key) => {
store._mutations[key] = (store._mutations[key] || [])
store._mutations[key].push((payload) => {
mutation.call(store, module.state, payload)
}) })
// 處理action
module.forEachAction((action, key) => {
store._actions[key] = (store._actions[key] || [])
store._actions[key].push((payload) => {
action.call(store, store, payload)
}) })
// 處理getter
module.forEachGetter((getter, key) => {
store._wrappedGetters[key] = function() {
return getter(module.state)
} })
// 處理children
module.forEachChild((child, key) => {
// 遞歸加載
installModule(store, rootState, path.concat(key), child)
})
}
此時,已經把每一個模塊的actions、mutation、getters都掛到了store上,接下來須要對state處理。
// 將全部的子模塊的狀態安裝到父模塊的狀態上
// 須要注意的是vuex 能夠動態的添加模塊
if (path.length > 0) { let parent = path.slice(0, -1).reduce((memo, current) => {
return memo[current] }, rootState) // 若是這個對象自己不是響應式的 那麼Vue.set 就至關於 obj[屬性 ]= 值
Vue.set(parent, path[path.length - 1], module.state);}
到此已經完成模塊的安裝,接下里是要把這些放到Vue實例上面 ### 模塊與實例的關聯
constructor(options) {
const state = options.state; //數據變化要更新視圖 (vue的核心邏輯依賴收集)
this._mutations = {}; this._actions = {}; this._wrappedGetters = {};
// 1.模塊收集
this._modules = new ModuleCollection(options);
// 2.安裝模塊 根模塊的狀態中 要將子模塊經過模塊名 定義在根模塊上
installModule(this, state, [], this._modules.root);
// 3,將狀態和getters 都定義在當前的vm上
resetStoreVM(this, state);
}
function resetStoreVM(store, state) {
const computed = {}; // 定義計算屬性
store.getters = {}; // 定義store中的getters
forEachValue(store._wrappedGetters, (fn, key) => {
computed[key] = () => { return fn(); } Object.defineProperty(store.getters, key, {
get: () => store._vm[key] // 去計算屬性中取值
}); }) store._vm = new Vue({
data: {
$$state: state
}, computed // 計算屬性有緩存效果
});}
相對應的Store類作如下修改
export class Store {
constructor(options) {
const state = options.state; //數據變化要更新視圖 (vue的核心邏輯依賴收集)
this._mutations = {}; this._actions = {}; this._wrappedGetters = {};
// 1.模塊收集
this._modules = new ModuleCollection(options);
// 2.安裝模塊 根模塊的狀態中 要將子模塊經過模塊名 定義在根模塊上
installModule(this, state, [], this._modules.root);
// 3,將狀態和getters 都定義在當前的vm上
resetStoreVM(this, state);
}
commit = (type, payload) => { //保證當前this 當前store實例
this._mutations[type].forEach(mutation => mutation.call(this, payload))
}
dispatch = (type, payload) => { this._actions[type].forEach(action => action.call(this, payload))
}
get state() { // 屬性訪問器 new Store().state Object.defineProperty({get()}) return this._vm._data.$$state
}
}
### 命名空間nameSpaced 默認狀況下,模塊內部的 action、mutation 和 getter 是註冊在**全局命名空間**的——這樣使得多個模塊可以對同一 mutation 或 action 做出響應。 若是但願你的模塊具備更高的封裝度和複用性,你能夠經過添加 `namespaced: true` 的方式使其成爲帶命名空間的模塊。當模塊被註冊後,它的全部 getter、action 及 mutation 都會自動根據模塊註冊的路徑調整命名。  日常寫上面基本上都要加上 namespaced,防止命名衝突,方法重複屢次執行。如今就算每一個 modules 的方法命同樣,也默認回加上這個方法別包圍的全部父結點的 key,核心就是 **path** 變量,在安裝模塊的時候把path處理下:
// 我要給當前訂閱的事件 增長一個命名空間
let namespace = store._modules.getNamespaced(path); // 返回前綴便可
**store._modules**就是模塊收集好的模塊,給它增長一個獲取命名空間的方法。 給ModuleCollection類增長一個getNamespaced方法,其參數就是path。
// 獲取命名空間, 返回一個字符串
getNamespaced(path) {
let root = this.root; // 從根模塊找起來
return path.reduce((str, key) => { // [a,c]
root = root.getChild(key); // 不停的去找當前的模塊
return str + (root.namespaced ? key + '/' : '') }, ''); // 參數就是一個字符串
}
固然Module類也須要增長一個屬性訪問器
get namespaced() {
return !!this._raw.namespaced
}
接下來就是在處理mutation,action,getters的時候key的值加上namespace就能夠了。
// 處理mutation
module.forEachMutation((mutation, key) => {
store._mutations[namespace + key] = (store._mutations[namespace + key] || [])
store._mutations[namespace + key].push((payload) => {
mutation.call(store, module.state, payload)
})})
// 處理action
module.forEachAction((action, key) => {
store._actions[namespace + key] = (store._actions[namespace + key] || [])
store._actions[namespace + key].push((payload) => {
action.call(store, store, payload)
})})
// 處理getter
module.forEachGetter((getter, key) => {
store._wrappedGetters[namespace + key] = function() {
return getter(module.state) }})
namespaces 核心就是對數據格式的處理,來進行發佈與訂閱。 ### 插件 > Vuex 的 store 接受 `plugins` 選項,這個選項暴露出每次 mutation 的鉤子。Vuex 插件就是一個函數,它接收 store 做爲惟一參數 使用的時候:
const store = new Vuex.Store({
// ... plugins: [myPlugin]
})
在插件中不容許直接修改狀態——相似於組件,只能經過提交 mutation 來觸發變化 先來看下一個vuex本地持久化的一個插件
function persists() {
return function(store) { // store是當前默認傳遞的
let data = localStorage.getItem('VUEX:STATE');
if (data) { store.replaceState(JSON.parse(data));
} store.subscribe((mutation, state) => {
localStorage.setItem('VUEX:STATE', JSON.stringify(state));
}) }}
插件返回一個函數,函數的參數就是store。其中replaceState, subscribe是關鍵點,也是vuex其中的2個api,接下來實現一下這2個方法。
export class Store {
constructor(options) {
const state = options.state; //數據變化要更新視圖 (vue的核心邏輯依賴收集)
this._mutations = {}; this._actions = {}; this._wrappedGetters = {};
// 1.模塊收集
this._modules = new ModuleCollection(options);
// 2.安裝模塊 根模塊的狀態中 要將子模塊經過模塊名 定義在根模塊上
installModule(this, state, [], this._modules.root);
// 3,將狀態和getters 都定義在當前的vm上
resetStoreVM(this, state);
// 插件內部會依次執行
options.plugins.forEach(plugin=>plugin(this));
}
commit = (type, payload) => { //保證當前this 當前store實例
this._mutations[type].forEach(mutation => mutation.call(this, payload))
}
dispatch = (type, payload) => { this._actions[type].forEach(action => action.call(this, payload))
}
get state() { // 屬性訪問器 new Store().state Object.defineProperty({get()}) return this._vm._data.$$state
}
}
options.plugins.forEach(plugin=>plugin(this))就是讓全部插件依次執行,參數就是**store**.
this._subscribes = [];// ...
subscribe(fn){
this._subscribes.push(fn);
}
subscribe就介紹一個函數,放入到一個數組或者隊列中去。
// 處理mutation
module.forEachMutation((mutation, key) => {
store._mutations[namespace + key] = (store._mutations[namespace + key] || [])
store._mutations[namespace + key].push((payload) => {
mutation.call(store, module.state, payload)
store._subscribes.forEach(fn => {
fn(mutation, rootState) }) })})
相應的在安裝模塊處理mutation的時候,須要讓訂閱的store._subscribes執行。fn的參數就是mutation和根狀態。
replaceState(state){
// 替換掉最新的狀態
this._vm._data.$$state = state
}
這是最簡單的改變狀態的方法,但此時雖然是ok的,可是mutation提交的仍是舊值,mutation.call(store, module.state, payload)這個地方仍是有點問題,module.state拿到的不是最新的狀態。
function getState(store, path) { // 獲取最新的狀態 能夠保證視圖更新
return path.reduce((newState, current) => {
return newState[current]; }, store.state);
}
能夠經過這個方法能獲取到最新的轉態,相應的在處理mutation,getters的地方作相應調整。
// 處理mutation
module.forEachMutation((mutation, key) => {
store._mutations[namespace + key] = (store._mutations[namespace + key] || [])
store._mutations[namespace + key].push((payload) => {
mutation.call(store, getState(store, path), payload)
store._subscribes.forEach(fn => {
fn(mutation, store.state)
}) })})
// 處理getter
module.forEachGetter((getter, key) => {
store._wrappedGetters[namespace + key] = function() {
return getter(getState(store, path))
}})
以前的mutation.state所有替換成getState去獲取最新的值。n(mutation, rootState) 也替換爲fn(mutation, store.state),這樣就能夠了。固然源碼中並無getState去或獲取最新狀態的方法。 ### Vuex中的輔助方法 > 所謂輔助函數,就是輔助咱們平時使用,說白了就是讓咱們偷懶。 咱們在頁面組件中可能會這樣使用
<template>
<div id="app"> 個人年齡是:{{this.$store.getters.age}}
<button @click="$store.commit('changeAge',5)">同步更新age</button>
<button @click="$store.commit('b/changeAge',10)">異步更新age</button>
</div></template>
<script>
export default {
computed: {
}, mounted() {
console.log(this.$store);
},};
</script>
this.$store.getters.age這樣用固然是能夠,可是就是有點囉嗦,咱們能夠作如下精簡
computed:{
age() {
return this.$store.getters.age
}}
this.$store.getters.age 直接替換成 age,效果確定是同樣的。可是寫了在computed中寫了age方法,感受仍是囉嗦麻煩,那再來簡化一下吧,先看下用法:
computed:{
...mapState(['age'])
}
mapState實現
export function mapState(stateArr) {
let obj = {}; for (let i = 0; i < stateArr.length; i++) {
let stateName = stateArr[i]; obj[stateName] = function() { return this.$store.state[stateName]
} } return obj}
那如法炮製,mapGetters
export function mapGetters(gettersArr) {
let obj = {}; for (let i = 0; i < gettersArr.length; i++) {
let gettName = gettersArr[i]; obj[gettName] = function() { return this.$store.getters[gettName]
} } return obj}
mapMutations
export function mapMutations(obj) {
let res = {}; Object.entries(obj).forEach(([key, value]) => {
res[key] = function (...args) { this.$store.commit(value, ...args)
} }) return res;}
mapActions
export function mapActions(obj) {
let res = {}; Object.entries(obj).forEach(([key, value]) => {
res[key] = function (...args) { this.$store.dispatch(value, ...args)
} }) return res;}
其中這些方法都是在一個helpers文件中。在vuex/index文件中將其導入。
import { Store, install } from './store';
// 這個文件是入口文件,核心就是導出全部寫好的方法
export default {
Store, install
}
export * from './helpers';
### createNamespacedHelpers 能夠經過使用 `createNamespacedHelpers` 建立基於某個命名空間輔助函數。它返回一個對象,對象裏有新的綁定在給定命名空間值上的組件綁定輔助函數。
export const createNamespacedHelpers = (namespace) => ({ mapState: mapState.bind(null, namespace),
mapGetters: mapGetters.bind(null, namespace),
mapMutations: mapMutations.bind(null, namespace),
mapActions: mapActions.bind(null, namespace)
})
## 總結 vuex的核心功能基本是完成,也能實現基本功能,不過看源碼對不少細節作了處理,邊界作了判斷。並且其中用到 了不少設計模式以及不少技巧和算法。