Vuex的鼎鼎大名,用Vue的同学没有不知道的吧,其实它的实现并没有很复杂,仅仅几十行代码,我们就能构建一个自己的Vuex,还不学会它,在面试官面前装一波!
vuex的使用
在store/index.js
中,我们是这样使用vuex的,包括我们常用的state、getters、mutations、actions
,其中actions
是执行异步操作的:
javascript
<!-- store/index.js -->
import Vuex from 'vuex';
import Vue from 'vue';
Vue.use(Vuex);
const store = new Vuex.Store({
state: {
age: 18
},
getters: {
age(state){
return state.age
}
},
mutations: {
setAge(state,payload){
state.age = state.age + payload
}
},
// actions 只是一个架构, 最终都是通过mutations来修改状态的 ,每个mutation执行完毕之后,可以得到对应的状态, devtools可以追踪每个状态变化
actions: {
delayAge(store,payload){
setTimeout(()=>{store.commit('setAge',payload)},2000)
}
}
})
export default store;
在页面中使用vuex:
vue
<!-- xxx.vue -->
<template>
<div>
state今年:{{$store.state.age}}岁了!
<br />
getters今年:{{$store.getters.age}}岁了!
<div>
<button @click="add">过一年</button>
<br />
<button @click="deleyAdd">等会过一年</button>
</div>
</div>
</template>
<script>
export default {
data() {
return {};
},
mounted() {},
methods: {
add() {
this.$store.commit("setAge", 1);
},
deleyAdd() {
this.$store.dispatch("delayAge", 1);
},
},
};
</script>
这样,我们的页面就可以操作vuex的数据,
点击按钮,第一个按钮是commit("setAge", 1)
,让数字加1,第二个按钮是dispatch("delayAge", 1)
,2秒后让数字加1。
使用自己的vuex
在store/index.js
中,我们将import Vuex from 'vuex'
变为 import Vuex from './vuex'
,也就是将使用node_modules中的vuex变为我们自己新建的vuex.js文件,所有我们需要在store/
下新建一个vuex.js
从使用上可以看出,Vue.use(Vuex)
和const store = new Vuex.Store()
,我们需要有install
方法和一个Store
构造函数:
js
// vuex.js
// Vue.use()时,就会传过了一个Vue
const install = (vue) => {
}
class Store {
// options 就是new Vuex.Store({}) 传入的配置项,包括state、getters、mutations、actions
constructor(options) {
}
}
export default {
Store,
install
};
Vue.use()
时,就会传过来一个Vue实例 ,options
就是new Vuex.Store({})
传入的配置项,包括state、getters、mutations、actions
将store放到每个组件上
js
// vuex.js
let Vue;
// Vue.use()时,就会传过了一个Vue
const install = (vue) => {
Vue = vue;
// 将store放到每个组件上
Vue.mixin({
beforeCreate() {
// 判断options上有没有store 没有直接找父级 因为根组件,我们会手动传store 可以看main.js
if (this.$options.store) {
this.$store = this.$options.store
} else {
this.$store = this.$parent && this.$parent.$store
}
}
})
}
这里有一个判断,因为在main.js
中,我们将store
放在了根组件的options
上,全局混入时,每个组件都需要获取根组件的options
上的store
(因为在组件上我们需要this.$store
获取store
),有的话(跟组件)直接获取,没有的话就需要一级级的向上寻找!然后放在每个组件的实例上的$store
属性上。
js
// main.js
import Vue from 'vue'
import App from './App.vue'
import store from './store'
new Vue({
render: h => h(App),
store, //这里放在根组件
}).$mount('#app')
初始化store/index.js 传入的options
js
//vuex.js
class Store {
constructor(options) {
//state 将state变为响应式
this.vm = new Vue({
data: options.state
})
// this.vm.$data上就是Vue中data定义的数据,都是响应式的
this.state = this.vm.$data;
// 定义getters,mutations,actions 并默认为{}
let { getters = {}, mutations = {}, actions = {} } = options;
// 将store/index.js传入的getters、mutations、actions赋予this.getters、this.mutations、this.actions
// 实现getters
this.getters = {};
// 注意this指向问题
let _this = this;
//Object.keys能打对象中的key遍历成数组,通过每个key,能找到对应的getter, getters[getterName]
Object.keys(getters).forEach(getterName => {
Object.defineProperty(this.getters, getterName, {
get() {
return getters[getterName](_this.state) // age(state){}
}
})
})
// 实现mutations 通过commit触发
this.mutations = {}
Object.keys(mutations).forEach(mutationName => {
this.mutations[mutationName] = function (payload) {
mutations[mutationName](_this.state, payload) //setAge(state,payload){}
}
})
//实现actions 通过dispatch触发
this.actions = {}
Object.keys(actions).forEach(actionName => {
this.actions[actionName] = function (payload) {
//_this就是store哦
actions[actionName](_this, payload) //delayAge(store,payload)
}
})
}
}
实现commit和dispatch
js
// vuex.js
commit(mutationName, payload) { //commit('setAge',1)
this.mutations[mutationName](payload)
}
dispatch(actionName, payload) {
this.actions[actionName](payload)
}
完整代码
js
// vuex.js
/*
这是我们自己写的vuex.js
*/
let Vue;
// Vue.use()时,就会传过了一个Vue
const install = (vue) => {
Vue = vue;
// 将store放到每个组件上
Vue.mixin({
beforeCreate() {
// 判断options上有没有store 没有直接找父级 因为根组件,我们会手动传store 可以看main.js
if (this.$options.store) {
this.$store = this.$options.store
} else {
this.$store = this.$parent && this.$parent.$store
}
}
})
}
class Store {
constructor(options) {
//state 将state变为响应式
this.vm = new Vue({
data: options.state
})
// this.vm.$data上就是Vue中data定义的数据,都是响应式的
this.state = this.vm.$data;
// 定义getters,mutations,actions 并默认为{}
let { getters = {}, mutations = {}, actions = {} } = options;
// 将store/index.js传入的getters、mutations、actions赋予this.getters、this.mutations、this.actions
// 实现getters
this.getters = {};
// 注意this指向问题
let _this = this;
//Object.keys能打对象中的key遍历成数组,通过每个key,能找到对应的getter, getters[getterName]
Object.keys(getters).forEach(getterName => {
Object.defineProperty(this.getters, getterName, {
get() {
return getters[getterName](_this.state) // age(state){}
}
})
})
// 实现mutations 通过commit触发
this.mutations = {}
Object.keys(mutations).forEach(mutationName => {
this.mutations[mutationName] = function (payload) {
mutations[mutationName](_this.state, payload) //setAge(state,payload){}
}
})
//实现actions 通过dispatch触发
this.actions = {}
Object.keys(actions).forEach(actionName => {
this.actions[actionName] = function (payload) {
//_this就是store哦
actions[actionName](_this, payload) //delayAge(store,payload)
}
})
}
commit(mutationName, payload) { //commit('setAge',1)
this.mutations[mutationName](payload)
}
dispatch(actionName, payload) {
this.actions[actionName](payload)
}
}
export default {
Store,
install
};
可以看出,不到70行的代码,就实现了我们自己的vuex,注释我都放在代码上了,不懂留言哦,赶紧去实操下吧!