通常我们使用vuex,都是通过下载vuex插件,自己创建一个store目录,在里面创建一个js文件,在文件中引入vuex,vue.use我们的vuex从而通过实例化一个vuex,定义五大核心从而实现一个基本的vuex。

那我们如何不下载插件,自己创建文件自己通过插件的形式,实现一个vuex呢?

我们需要知道,state是一个响应式的数据它应该怎么去实现呢,并且vuex是一个单向数据流,我们应该怎么讲单向数据流转起来。在修改state的数据时,我们只能通过mutations才可以修改state,因此我们自己定义vuex插件,需要注意很多事项。

实现思路如下:

1、创建Store类,实现install方法

定义store类,是为了我们vue使用插件的时候,我们初始化实例将我们的我们通过定义的类可以获取到我们传递的对象,我们可以获取到实例化Store这个对象的时候传递的参数

install方法作用时什么呢?我们主要是通过install方法将我们的vue实例上挂载一个$store方法,这样我们在获取store中的属性以及方法的时候会很方便。

let Vue;
// 创建store类
class Store {
    constructor(options) {
        // options: 实例化Store这个对象的时候传递的参数
        console.log(options);
    }
}
//定义一个install方法
function install(_Vue) {
    Vue = _Vue;
    //通过vue混入的方式,将下面的代码延迟到vue实例创建完毕以后执行
    Vue.mixin({
        //vue的生命周期
        beforeCreate() {
            //判断$options上面有store这个属性,咋们才去执行。避免不必要的渲染
            // console.log(this, 'this');
            if (this.$options.store) {
                Vue.prototype.$store = this.$options.store
            }
        }
    })
}

export default { Store, install }

2、初始化state

我们需要尽可能将state保护起来,我们可以使用 $ $state为我们的存储数据的对象

由于$$state数据是不会被代理的因此我们使用 $ $state这样数据更加安全

并且我们通过代理设置访问器的方式来限制访问,通过定义get和set方法来读取以及设置值,由于state不能直接修改,因此我们在set方法中不进行业务操作

let Vue;
// 创建store类
class Store {
    constructor(options) {
        // options: 实例化Store这个对象的时候传递的参数
        console.log(options);
         //为了尽可能把state数据给保护起来
        this._vm = new Vue({
            data: {
                //$$state这个数据是不会被代理的
                $$state: options.state
            },
            computed
        })
        console.log(this, 'this');
    }
     //代理(设置访问器的方式来限制访问)
    get state() {//读取值
        return this._vm._data.$$state;
    }
    set state(v) {//设置值
        console.error('不能直接修改state,请使用mutations来修改state');
    }
}

3、实现commit以及dispatch方法

由于我们要实现单向数据流,因此要实现这两种方式,因为只有通过这两种方式可以更改state当中的数据。

它们都接收两个参数,一个为事件类型一个是接收到的数据commit(type,payload){}

这里我们在constructor中需要保存我们的方法,mutations,actions,commit以及dispatch

这里获取的是我们在store/index.js传入的一个对象,里卖弄是我们保存的五大核心。

commit我们需要获取到mutations的type值,存在某个函数就执行我们定义的方法

dispatch同理

let Vue;
// 创建store类
class Store {
    constructor(options) {
        // options: 实例化Store这个对象的时候传递的参数
        console.log(options);
        // 1. 保存选项
        this._mutations = options.mutations || {};
        this._actions = options.actions || {};

        // 绑定this
        this.commit = this.commit.bind(this)
        this.dispatch = this.dispatch.bind(this)
    }
     //commit方法
     commit(type, payload) {
         console.log(this);
         // type: 事件类型; payload: 载荷信息
         const entry = this._mutations[type];
         // console.log(entry);
         // 如果当前的entry函数存在就执行
         entry && entry(this.state, payload)
    }
     //dispatch方法
     dispatch(type, payload) {
         // type: 事件类型; payload: 载荷信息
         const entry = this._actions[type];
         // console.log(entry);
         // 如果当前的entry函数存在就执行
         entry && entry(this, payload)
    }
}

4、实现getters

getters相当于是一个计算属性,我们在this上挂载一个属性为我们实例化store传递的getters值(wrappedGetters)、声明一个计算属性为一个{}(const computed={})、并且再定义一个值为我们的真正页面获取的getters值、最后声明一个store为我们的this(vuex实例对象)

我们需要通过遍历所有的getters,将我们实例化Store传入的getters中的所有方法的key值拿来遍历,这里key值获取到的是实例化Store对象里面的方法名。我们通过方法的键名来找到它的方法声明为一个变量。

将我们在constructor中声明的computed计算属性,通过设置它的键值为它所对应的方法的结果,这里可以理解为在computed中存放的是getters中方法对应的计算以后的结果。

通过object.defineProperty将我们定义的getters对象上添加属性,属性为我们遍历的key值,并且有一个get方法,get方法返回的是我们上方定义computed返回的结果。这里我们怎么理解呢,我们都知道,object.defineproperty是添加一个属性给一个对象或者修改一个对象的原有属性,它是将数据响应式,因此我们给getters添加属性为我们遍历的实例化Store传入的方法键值,并且我们如果调用getters方法的键值时就会触发get方法返回我们computed对应的上方计算属性返回的方法对应返回的值。这下就很好理解了

//实现计算属性
let Vue;
// 创建store类
class Store {
    constructor(options) {
        // options: 实例化Store这个对象的时候传递的参数
        console.log(options);
        this.wrappedGetters = options.getters || {};
        // 声明一个计算属性
        const computed = {}
        this.getters = {}

        // 保存this store和options是相同的
        const store = this;
        // 循环遍历所有wrappedGetters中的所有的key(函数名)
        Object.keys(this.wrappedGetters).forEach(key => {
            // 把vuex中的getters中的所有函数取出来了
            const fn = store.wrappedGetters[key];
            computed[key] = () => {
                // 让计算属性的返回值是getters的执行结果
                return fn(store.state)
            }
            // Object.defineProperty来定义属性
            Object.defineProperty(store.getters, key, {
                get() {
                    // 返回的就是计算属性 computed 的计算结果
                    return store._vm[key]
                }
            })
        })
         //为了尽可能把state数据给保护起来
        this._vm = new Vue({
            data: {
                //$$state这个数据是不会被代理的
                $$state: options.state
            },
            computed
        })

    }

}

这样我们实现了一个基本的vuex,使用的时候呢我们就直接在main.js文件引入我们的store,挂载在vue实例中。

获取state中的属性通过$store.state.属性名获取

获取getters中的属性通过$store.getters.方法名获取

触发mutations方法通过$store.commit('方法名',传递的数据)

触发actions方法通过$store.dispatch('方法名',传递的数据)

Logo

前往低代码交流专区

更多推荐