likes
comments
collection
share

Vuex很难?几十行代码我们实现一个!

作者站长头像
站长
· 阅读数 9

Vuex的鼎鼎大名,用Vue的同学没有不知道的吧,其实它的实现并没有很复杂,仅仅几十行代码,我们就能构建一个自己的Vuex,还不学会它,在面试官面前装一波!

Vuex很难?几十行代码我们实现一个!

vuex的使用

store/index.js中,我们是这样使用vuex的,包括我们常用的state、getters、mutations、actions,其中actions是执行异步操作的:

<!-- 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:

<!-- 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的数据,

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构造函数:

// 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放到每个组件上

// 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属性上。

// 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

//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

// vuex.js
commit(mutationName, payload) {  //commit('setAge',1)
    this.mutations[mutationName](payload)
}
dispatch(actionName, payload) {
    this.actions[actionName](payload)
}

完整代码

//  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,注释我都放在代码上了,不懂留言哦,赶紧去实操下吧!