知識導航
- vuex的使用
- Vuex的基本實現
知識點:
1. vuex的使用
Vuex想必我們都很熟悉了,它就是實現全局狀態的一種管理機制。即可以實現所有組件共享同一數據。
1.1 實例化store時傳入的參數
state:放數據;mutations:改變數據的一些方法;actions:異步方法或一些複雜邏輯;getters:類似於計算屬性,對數據的包裝。
{
state: {
count: 0
},
mutations: {
add(state, a) {
state.count += a;
},
sub(state, step) {
state.count = state.count - step;
}
},
actions: {
//第一個參數是希望給一個store的實例,因爲只有store實例纔有commit方法
asyncAdd(context, step) {
setTimeout(function() {
context.commit("add", step);
}, 1000)
},
asyncsub(context, step) {
setTimeout(function() {
context.commit("sub", step);
}, 1000)
}
},
getters: {
showData(state) {
return `數字:${state.count}`
}
},
modules: {}
}
1.2 組件1中的使用(我比較喜歡的方式)
commit:可調用mutations中的方法,dispatch:可調用actions中的方法
<template>
<div>
{{$store.state.count}}
{{$store.getters.showData}}
<button @click="add">加</button>
<button @click="addAsy">延時加</button>
</div>
</template>
<script>
export default {
methods: {
add(){
this.$store.commit("add",2);
},
addAsy(){
this.$store.dispatch('asyncAdd', 3)
}
},
}
</script>
1.2 組件2中的使用
這裏只是用了一下vuex提供的一些輔助函數
<template>
<div>
{{$store.state.count}}{{showData()}}
<button @click="subHander">減</button>
<button @click="subHanderAsy">延時減</button>
</div>
</template>
<script>
import { mapMutations, mapActions,mapGetters } from "vuex";
export default {
methods: {
...mapMutations(["sub"]),//mapMutations輔助函數可將this.sub()映射爲this.$store.commit('sub'),因爲我們可能要的不止是一個所以用下擴展運算符展開它
...mapActions(["asyncsub"]),
...mapGetters(['showData']),
subHander() {
this.sub(2);
},
subHanderAsy() {
this.asyncsub(2);
}
}
};
</script>
2. Vuex的基本實現
2.1 老樣子,還是看使用時做了哪些工作
配置js中:
入口文件main.js中:
思路
通過觀察我們可以知道插件中要實現的事情。
- 實現install方法
- 接收參數
- 實現commit和dispatch
- 注意getters(我們在組件中使用getters中的東西時是這樣this.$store.getters.showData。這可不是一個方法的調用。它更像一個去對象中取值的操作。其實仔細觀察它其實更類似於雙向數據綁定,即數據一變,數據包裝跟着改變,getters裏的方法只是決定了如何包裝數據而已)
- 實現install
這裏的主要工作還是用混入將這個store實例掛到vue原型上去
function install(_vue) {
Vue = _vue;
Vue.mixin({
beforeCreate() {
if (this.$options.store) {
Vue.prototype.$store = this.$options.store;
}
},
})
}
- 接收參數
接收參數分別存儲時要注意state中的數據是響應式的
class Store {
constructor(args = {}) {
this.state = new Vue({
data: args.state
});
this.actions = args.actions || {};
this.mutations = args.mutations || {};
}
- 實現commit
cmmit要做的工作很簡單,觀察到組件中使用commit時會傳一個mutations 中的方法名,和一些其他的參數
commit(fnName, arg) {
let fn = this.mutations[fnName];//利用傳過來的方法名去mutations找對應的函數
fn(this.state, arg);//調用這個函數即可,傳入要改的state數據源和組件中該過來的參數
}
- 實現dispatch
它的實現和commit類似,只不過因爲它裏面要調用的可能是有返回值的異步函數。所以可能需要返回函數執行結果。同時這次調用參數中的方法時要傳的第一個參數是這個store實例,或者說是此處的上下文
dispatch(fnName, arg) {
let fn = this.actions[fnName];
return fn(this, arg);
}
- 包裝數據
這個實現getters應該是目前這裏最難的,首先在接收參數時我們先要判斷參數中是否有getters的東西,有則需要一個方法來處理它。這個方法要實現什麼功能呢?其實也很簡單就是把傳過來的getters中的方法全部執行一遍。把執行完的數據存起來就可以了。
我首先想到的是像這樣(錯誤示例):
gettersHander(objGetFn) {
this.getters = {};
Object.keys(objGetFn).forEach(key => {
this.getters[key] = objGetFn[key](this.state);
})
}
我因爲this.state是響應式的,故只需state的狀態一發生變化我this.getters中會跟着變化。但是這裏程序好像不是按照我期望的流程走的。
我現在是希望在訪問 this.getters中數據時有一段劫持代碼用來做中間件來處理一下,即一訪問這個 this.getters中數據,中間件會幫我們更新this.getters中的數據。以到達響應式的結果。
可以實現數據劫持的有兩個方法:Object.defineProperty()和Proxy。(後面還會用到這裏不寫使用了,去MDN看一下就行)
修改上面代碼:
gettersHander(objGetFn) {
this.getters = {};
Object.keys(objGetFn).forEach(key => {
Object.defineProperty(this.getters, key, {
get: () => {
return objGetFn[key](this.state);
}
})
})
}
效果:
全部代碼:
// 思路
// 1. 首先實現install
// 2. 接收參數並找分類容器保存
// 3. 將state裏面的數據做出響應式
// 4. 實現三方法,commit,dispath
let Vue;
function install(_vue) {
Vue = _vue;
Vue.mixin({
beforeCreate() {
if (this.$options.store) {
Vue.prototype.$store = this.$options.store;
}
},
})
}
class Store {
constructor(args = {}) {
this.state = new Vue({
data: args.state
});
this.actions = args.actions || {};
this.mutations = args.mutations || {};
args.getters && this.gettersHander(args.getters);
}
commit(fnName, arg) {
let fn = this.mutations[fnName];
fn(this.state, arg);
}
dispatch(fnName, arg) {
let fn = this.actions[fnName];
return fn(this, arg);
}
gettersHander(objGetFn) {
this.getters = {};
Object.keys(objGetFn).forEach(key => {
Object.defineProperty(this.getters, key, {
get: () => {
return objGetFn[key](this.state);
}
})
})
}
}
export default { install, Store }