Vuex用法及手写核心原理

115 阅读3分钟

一、 Vuex用法

Vuex核心包括state、mutatios、actions、getters四个部分。

Vuex.Store 构造器选项

state

  • 类型: Object | Function

    Vuex store 实例的根 state 对象。

    如果你传入返回一个对象的函数,其返回的对象会被用作根 state。这在你想要重用 state 对象,尤其是对于重用 module 来说非常有用。

mutations

  • 类型: { [type: string]: Function }

    在 store 上注册 mutation,处理函数总是接受 state 作为第一个参数(如果定义在模块中,则为模块的局部状态),payload 作为第二个参数(可选)。

actions

  • 类型: { [type: string]: Function }

    在 store 上注册 action。处理函数总是接受 context 作为第一个参数,payload 作为第二个参数(可选)。

getters

  • 类型: { [key: string]: Function }

    用于对state中的值进行二次加工,作用类似于Vue中的Computed

参考:Vuex官网

二、Vuex的核心

  • 首先创建store.js文件
import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex)

export const store = new Vuex.Store({
    state: {
        count: 0
    }
})

  • 然后在main的js中引入并注册store
new Vue({
  store,
  render: h => h(App),
}).$mount('#app')
  • 此时便可在页面中使用vuex中存储的数据
<template>
  <div id="app">
    <div>{{ $store.state.count }}</div>
  </div>
</template>

如图: image.png

  • 上述为vuex中数据的使用,下面可以可以开始使用vuex的一些内置方法,将store.js中代码修改为如下所示。
import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex)

export const store = new Vuex.Store({
    state: {
        count: 0
    },
    actions: {
        increment(context, a) {
                context.commit('increment', a)
        }
    },
    mutations: {
        increment(state, num) {
            state.count += Number(num)
        }
    },
    getters: {
        countResult: state => {
            return state.count + '元'
        }
    }
})

  • 此时我们可以通过action中的方法来触发 mutatios中的方法来修改state中的值,也可以通过getters来获取对state二次处理后的值。
<template>
  <div id="app">
    <div>{{ $store.state.count }}</div>
    <div>{{ $store.getters.countResult }}</div>
    <button @click="add">点击</button>
  </div>
</template>

<script>
export default {
  name: 'App',
  mounted() {
    console.log(this.$store.getters.countResult)
  },
  methods: {
    add() {
      this.$store.dispatch('increment', 10)
    }
  },
}
</script>

  • 结果如图:

image.png

  • 以上为Vuex的基本用法,接下来我们来手写其核心部分。

    1. 首先,我们根据Vuex的使用方式Vue.use(Vuex)可知,Vuex是Vue的插件。所以,我们需要知道如何自定义插件。
    1. 根据文章可知,插件可以为函数,或者为具有install方法的对象。Vue在注册插件的时候会调用插件函数或者插件的install函数。
Vue.use = function(plugin){
	const installedPlugins = (this._installedPlugins || (this._installedPlugins = []));
	if(installedPlugins.indexOf(plugin)>-1){
		return this;
	}
	<!-- 其他参数 -->
	const args = toArray(arguments,1);
	args.unshift(this);
	if(typeof plugin.install === 'function'){
		plugin.install.apply(plugin,args);
	}else if(typeof plugin === 'function'){
		plugin.apply(null,plugin,args);
	}
	installedPlugins.push(plugin);
	return this;
}
  • 如上所示,Vue的use方法会根据插件类型来进行不同方式的调用。

3.所以Vuex应为函数或具有install方法的对象。我们新建文件夹myVuex.js。

const install = () =>{}
const myVuex = {
    install
}
export default myVuex

  1. 此时myVuex具有install方法,所以是可以被注册为插件的。install方法可以说是初始化Vuex的方法。我们知道在组件中使用Vuex需要通过this.store来获取。所以我们需要为Vue实例注册store来获取。所以我们需要为Vue实例注册store,所以我们需要在install中为Vue实例挂载$store。
const install = (Vue) => {
    Vue.mixin({
        beforeCreate() {
            if (this.$options && this.$options.store) {
                this.$store = this.$options.store
            } else {
                this.$store = this.$parent && this.$parent.$store
            }
        },
    })
}
  • 使用混入,在每一个组件初始化时调用beforeCreate钩子来挂载$store。
  • 根组件在main.js中通过new Vue({ store, render: h => h(App), }).$mount('#app')已经挂载。
  1. 根据我们在store.js中创建store实例是通过new Vuex.Store来创建。所以我们可知Store为挂载在Vuex上的类。所以我们可以为我们的vuex挂载Store。
const install = (Vue) => {
 Vue.mixin({
        beforeCreate() {
            if (this.$options && this.$options.store) {
                this.$store = this.$options.store
            } else {
                this.$store = this.$parent && this.$parent.$store
            }
        },
    })
}
class Store {
    constructor(config) {
     
    }    
}
const myVuex = {
    install,
    Store,
}
  1. 根据store.js创建实例可以看出,初始化时会给Store传入state、actions、mutations、getters等配置。所以我们可以在Store类的constructor中获取配置参数,然后进行逐步分析。
  2. 首先是state,state为数值,由于Vuex的state是具有响应式的,所以我们需要通过new Vue实例来借用Vue中的data会被自动注册为响应书数据来实现state的响应式。注册响应式后我们需要通过this.$store.vm.state来获取state,这是不规范的,Vuex中并不是这样取值,所以我们需要使用get函数,当get state值时,返回this.vm.state。
constructor(config) {
        this.vm = new Vue({
            data: {
                state: config.state
            }
        })
    }
    get state() {
        return this.vm.state
    }

9.然后我们来分析getters,getters的调用为this.$store.getters.a。所以Store需要具有getters属性,而且getters属性是对state的二次处理。如下所示。

constructor(config) {
        this.vm = new Vue({
            data: {
                state: config.state
            }
        })
        let getters = config.getters || {}
        this.getters = {}
        Object.keys(getters).forEach(getterName => {
            Object.defineProperty(this.getters, getterName, {
                get: () => {
                    return getters[getterName](this.state)
                }
            })
        })
    }

10.接下来是actions,我们需要通过dispatch来调用actions,然后通过actions来调用commit方法来出发mutations进而修改state。所以Stote类中还需要dispatch方法来调用actions

  • 首先我们来对actios进行注册
constructor(config) {
        this.vm = new Vue({
            data: {
                state: config.state
            }
        })
        // 注册actions
        this.actions = config.actions
        // 注册mutations
        this.mutations = config.mutations
        
        let getters = config.getters || {}
       
        this.getters = {}
        Object.keys(getters).forEach(getterName => {
            Object.defineProperty(this.getters, getterName, {
                get: () => {
                    return getters[getterName](this.state)
                }
            })
        })
    }
  • 然后我们注册dispatch方法来调用actions中的方法。
dispatch(){
 // 第一个参数为所要调用的action名称,args中为其他所有参数
        const [method, ...args] = arguments
        // actions方法中第一个参数为this.state
        this.actions[method](this, args)
    }
  • mutations与actions同理,不同的是mutation方法中接收的第一个参数为state
 commit() {
        const [method, ...args] = arguments
        this.mutations[method](this.state, args)
    }

以上是对Vuex用法及核心原理的介绍与实现,如有错误还望批评指正,是和大家分享知识也是对自己学习的总结,感谢阅读。