分享之前面试遇到的一个问题:手写一个简易版的vuex
我们先来分析vuex 在main.js 引入 并通过vue.use()方法调用,知道vuex是作为一个插件使用,并且需用通过 new Vuex.Store 赋值给一个变量,所以我们得知vuex是一个类 并且有一个install方法
// main.js
import Vuex from 'vuex'
Vue.use(Vuex)
const store = new Vuex.Store({
state:{
},
mutations: {
},
getters: {
},
actions: {
}
})
所以我们新建一个store.js文件,在通过mixin混合的方式在beforeCreate周期函数把实例挂载到组件上
// store.js
// 引入vue
import Vue from 'vue'
// 声明一个vue变量 到时候把全局vue对象赋值过去
let vue
//声明Store类
class Store{
// 构造函数
constructor(options){
// console.log('options', options);
}
}
const install = (Vue) => {
vue = Vue
vue.mixin({
beforeCreate(){
// 通过mixin挂载到跟组件示例上
if (this.$options && this.$options.store){
this.$store = this.$options.store
} else {
// 否则去父组件找
this.$store = this.$parent && this.$parent.$store
}
}
})
}
export default {
Store, install
}
我们可以在constructor 函数里面打印看看options是什么:
就是main.js 里面的store对象,因此我们就可以根据options参数来组装我们需要的数据
首先是state对象
由于我们vuex的数据是响应式的数据 所以我们借助vue的响应式来接受state数据
但是此时会发现state被多嵌套了一层 需要this.$store.vm.state.xxx才能调用
所以我们借助类的get 方法把整个对象返回出去
整理后代码:
// store.js
// 引入vue
import Vue from 'vue'
// 声明一个vue变量 到时候把全局vue对象赋值过去
let vue
//声明Store类
class Store{
// 构造函数
constructor(options){
// console.log('options', options);
this.vm = new Vue({
data:{
state: options.state
}
})
}
get state() {
return this.vm.state
}
}
const install = (Vue) => {
vue = Vue
vue.mixin({
beforeCreate(){
// 通过mixin挂载到跟组件示例上
if (this.$options && this.$options.store){
this.$store = this.$options.store
} else {
// 否则去父组件找
this.$store = this.$parent && this.$parent.$store
}
}
})
}
export default {
Store, install
}
接着 getters
拿到getters对象,通过Object.keys() 拿到对象的key
由于getters需要响应式,所以借助js的 Object.defineProperty方法来实现
接下来是 mutations
在实际应用我们是通过comit来提交到mutation 并通过payload 作为参数,所以应该在类里面声明一个commit函数
// commit for mutatiuons
commit(type,payload) {
this.mutations[type](payload)
}
action同理
action方法是通过dispatch 触发,所以类中:
最后整理完整代码:
// store.js
import Vue from "vue";
let vue;
class Store {
constructor(options){
// console.log('options', options);
this.vm = new Vue({
data:{
state: options.state
}
})
// getters
const getters = options.getters
this.getters = {}
Object.keys(getters).forEach((getterName) => {
Object.defineProperty(this.getters, getterName, {
get: () => {
return getters[getterName](this.state)
}
})
})
// mutations
const mutations = options.mutations
this.mutations = {}
Object.keys(mutations).forEach((mutationName) => {
this.mutations[mutationName] = (payload) => {
mutations[mutationName](this.state, payload)
}
})
// actions
const actions = options.actions
this.actions = {}
Object.keys(actions).forEach( actionName => {
this.actions[actionName] = (payload) => {
actions[actionName](this, payload)
}
})
}
// commit for mutatiuons
commit(type,payload) {
this.mutations[type](payload)
}
// dispatch for actions
dispatch(type, payload) {
this.actions[type](payload)
}
get state() {
return this.vm.state
}
}
const install = (_Vue) => {
vue = _Vue
vue.mixin({
beforeCreate() {
// 通过mixi挂载到根组件示例
if (this.$options && this.$options.store) {
this.$store = this.$options.store
} else {
this.$store = this.$parent && this.$parent.$store
}
},
})
}
export default {
Store,
install
}