八股文不用背-手写Vuex

260 阅读2分钟

组成

  • state:状态
  • mutation:原子级修改状态
  • action:调用mutation
  • getter:状态的computed

环境搭建

  1. npm install --global vue-cli@3.10.0
  2. 通过vue create vuex 创建项目,手动选择配置,只选上babel和vuex
  3. 打开啊vuex项目,目录结构如下

image.png

这个index.js文件如下:

import Vue from 'vue'
// 使用了npm 下载的vuex
import Vuex from 'vuex'

Vue.use(Vuex)

export default new Vuex.Store({
  state: {
  },
  getters: {
  },
  mutations: {
  },
  actions: {
  },
  modules: {
  }
})

我们在store文件夹下新建一个myVuex.js文件实现vuex功能, 在index.js中使用myVuex.js,并给出一个基础的vuex配置

import Vue from 'vue'
// 使用了npm 下载的vuex
// import Vuex from 'vuex'
// 换成我们自己写的
import Vuex from './myVuex.js'

Vue.use(Vuex)

export default new Vuex.Store({
  state: {
    a:1,
    b:2
  },
  getters: {
    add(state){
      return state.a + state.b
    }
  },
  mutations: {
    changeA(state,newValue){
      state.a = newValue 
    }
  },
  actions: {
    changeAAfterOneSec(context,newValue){
      setTimeout(() => {
        context.commit('changeA',newValue)
      }, 1000);
    }
  },
  modules: {
  }
})

挺简单的,两个state、一个mutation、一个action

设计myVuex

打开store/myVuex.js文件

// 引入vue,等下有用
import vue from 'vue'
// 因为vuex是通过vue.use(Vuex)作为vue插件使用的,所以我们得导出一个install方法
function install(Vue) {
    // 全局的Vue对象,我们混入一些东西
    Vue.mixin({
        // 因为在beforeCreate阶段能拿到this.$options对象,而根Vue中有vuex创建出来的store对象,我们将它绑定到this.$store中,这样组件内才能通过this.$store去访问store
        beforeCreate() {
            // 如果是根结点
            if (this.$options && this.$options.store) {
                this.$store = this.$options.store
            } // 如果不是根结点,则将父组件的$store绑定过来 
            else {
                this.$store = this.$parent && this.$parent.$store
            }
        },
    })
}
// 设计vuex,首先store是一个类
class Store{
    constructor(options){
        // options是vuex的配置,在上面的代码块中可查看
        let state = options.state || {}
        let getters = options.getters || {}
        let mutations = options.mutations || {}
        let actions = options.actions || {}
        
        // 对于this.state,我们肯定是要将其属性设计成响应式的
        // 有两种实现方式,这边只说最简单的,通过vue自带的一个方法来实现
        this.state = vue.observable(state)
        
        // 对于this.gettters,我们在vuex的配置中是通过方法来定义的
        // getters: {
        //    add(state){
        //      return state.a + state.b
        //    }
        //  },
        // 我们在组件中通过$store.getters.add来访问
        // 所以我们得通过属性描述符的get来实现
        this.getters = {}
        for(let key in getters){
            let getter = getters[key]
            Object.defineProperty(this.getters,key,{
                get:()=>{
                    getter(this.state)
                }
            })
        }
        
        // 对于this.mutations,我们在vuex的配置中是通过方法来定义的
        // mutations: {
        //   changeA(state,newValue){
        //     state.a = newValue 
        //   }
        // },
        // 我们在组件中通过$store.commit('changeA',3)来访问
        // 我们先将配置中的mutations放入this.mutations
        this.mutations = {}
        for(let key in mutations){
            let mutation = mutations[key]
            this.mutations[key] = (args)=>{
                return mutation(this.state,args)
            }
        }
        
        this.commit = (mutationName,args)=>{
            return this.mutations[mutationName](args)
        }
        
        // 对于this.actions,我们在vuex的配置中是通过方法来定义的
        // actions: {
        //    changeAAfterOneSec(context,newValue){
        //        setTimeout(() => {
        //            context.commit('changeA',newValue)
        //        }, 1000);
        //    }
        // },
        // 我们在组件中通过$store.disptach('changeAAfterOneSec',3)来访问
        // 我们先将配置中的actions放入this.actions
        this.actions = {}
        for(let key in actions){
            let action = actions[key]
            this.actions[key] = (args)=>{
                return action(this,args)
            }
        }
        
        this.commit = (actionName,args)=>{
            return this.actions[actionName](args)
        }
        
    }
}
export default {
    install,
    Store
}

设计mapState、mapGetters、mapMutations、mapActions

我们一般通过以下方式使用这四个辅助函数

import {mapState,mapGetters,mapMutations,mapActions}
export default {
    data(){
        return {}
    },
    computed:{
        ...mapState(['a','b']),
        ...mapGetter(['add']),
    },
    methods:{
        ...mapMutations(['changeA']),
        ...mapActions(['changeAAfterOneSec']),
    }
}

设计

import vue from 'vue'
function install(Vue) {
    ....
}
// 设计vuex,首先store是一个类
class Store{
   ...
}
function mapState(arr){
    let obj = {}
    for(let i = 0;i<arr.length;i++){
       let item = arr[i]
       obj[item] = () => this.$store.state[item]
    }
    return obj
}
function mapGetters(arr){
    let obj = {}
    for(let i = 0;i<arr.length;i++){
       let item = arr[i]
       obj[item] = () => this.$store.getters[item]
    }
    return obj
}
function mapMutations(arr){
    let obj = {}
    for(let i = 0;i<arr.length;i++){
       let item = arr[i]
       obj[item] = (args)=>{this.$store.mutations[item](args)} 
    }
    return obj
}
function mapActions(arr){
    let obj = {}
    for(let i = 0;i<arr.length;i++){
       let item = arr[i]
       obj[item] = (args)=>{this.$store.actions[item](args)} 
    }
    return obj
}
export default {
    install,
    Store,
    mapState,
    mapGetters,
    mapMutations,
    mapActions
}

整体代码

// 引入vue,等下有用
import vue from 'vue'
// 因为vuex是通过vue.use(Vuex)作为vue插件使用的,所以我们得导出一个install方法
function install(Vue) {
    // 全局的Vue对象,我们混入一些东西
    Vue.mixin({
        // 因为在beforeCreate阶段能拿到this.$options对象,而根Vue中有vuex创建出来的store对象,我们将它绑定到this.$store中,这样组件内才能通过this.$store去访问store
        beforeCreate() {
            // 如果是根结点
            if (this.$options && this.$options.store) {
                this.$store = this.$options.store
            } // 如果不是根结点,则将父组件的$store绑定过来 
            else {
                this.$store = this.$parent && this.$parent.$store
            }
        },
    })
}
// 设计vuex,首先store是一个类
class Store{
    constructor(options){
        // options是vuex的配置,在上面的代码块中可查看
        let state = options.state || {}
        let getters = options.getters || {}
        let mutations = options.mutations || {}
        let actions = options.actions || {}
        
        // 对于this.state,我们肯定是要将其属性设计成响应式的
        // 有两种实现方式,这边只说最简单的,通过vue自带的一个方法来实现
        this.state = vue.observable(state)
        
        // 对于this.gettters,我们在vuex的配置中是通过方法来定义的
        // getters: {
        //    add(state){
        //      return state.a + state.b
        //    }
        //  },
        // 我们在组件中通过$store.getters.add来访问
        // 所以我们得通过属性描述符的get来实现
        this.getters = {}
        for(let key in getters){
            let getter = getters[key]
            Object.defineProperty(this.getters,key,{
                get:()=>{
                    getter(this.state)
                }
            })
        }
        
        // 对于this.mutations,我们在vuex的配置中是通过方法来定义的
        // mutations: {
        //   changeA(state,newValue){
        //     state.a = newValue 
        //   }
        // },
        // 我们在组件中通过$store.commit('changeA',3)来访问
        // 我们先将配置中的mutations放入this.mutations
        this.mutations = {}
        for(let key in mutations){
            let mutation = mutations[key]
            this.mutations[key] = (args)=>{
                return mutation(this.state,args)
            }
        }
        
        this.commit = (mutationName,args)=>{
            return this.mutations[mutationName](args)
        }
        
        // 对于this.actions,我们在vuex的配置中是通过方法来定义的
        // actions: {
        //    changeAAfterOneSec(context,newValue){
        //        setTimeout(() => {
        //            context.commit('changeA',newValue)
        //        }, 1000);
        //    }
        // },
        // 我们在组件中通过$store.disptach('changeAAfterOneSec',3)来访问
        // 我们先将配置中的actions放入this.actions
        this.actions = {}
        for(let key in actions){
            let action = actions[key]
            this.actions[key] = (args)=>{
                return action(this,args)
            }
        }
        
        this.commit = (mutationName,args)=>{
            return this.mutations[mutationName](args)
        }
        
    }
}
function mapState(arr){
    let obj = {}
    for(let i = 0;i<arr.length;i++){
       let item = arr[i]
       obj[item] = () => this.$store.state[item]
    }
    return obj
}
function mapGetters(arr){
    let obj = {}
    for(let i = 0;i<arr.length;i++){
       let item = arr[i]
       obj[item] = () => this.$store.getters[item]
    }
    return obj
}
function mapMutations(arr){
    let obj = {}
    for(let i = 0;i<arr.length;i++){
       let item = arr[i]
       obj[item] = (args)=>{this.$store.mutations[item](args)} 
    }
    return obj
}
function mapActions(arr){
    let obj = {}
    for(let i = 0;i<arr.length;i++){
       let item = arr[i]
       obj[item] = (args)=>{this.$store.actions[item](args)} 
    }
    return obj
}
export default {
    install,
    Store,
    mapState,
    mapGetters,
    mapMutations,
    mapActions
}