一步一步带你走进Vuex原理的世界__Vue.js
发布于 4 年前 作者 banyungong 1838 次浏览 来自 分享
粉丝福利 : 关注VUE中文社区公众号,回复视频领取粉丝福利

Vuex是什么?运行原理是什么?

Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。


在View通过Dispatch触底Action,Action触发Mutation,最后更新State触发View更新,这一流程遵循单向数据流的原则。

图来自Vuex官网👆

Vuex的简单定义和用法

import Vuex from 'Vuex'

import Vue from 'vue'

Vue.use(Vuex)

let store = new Vuex.Store({

    state: {
        num: 1
    },

    getters: {

        numStore(state) {

            return state.num + 1

        }

    },

    mutations: {

        addOne(state, params) {

            state.num = state.num + params.num

        }

    },

    actions: {

        addOneAsync({ commit }, params) {
            setTimeout(() => {
                commit('addOne', params)
            }, 1000)
        }

    }

})

export default store

在View中可以通过this.$store.commit触发mutation,或者通过this.$store.dispatch触发action。这里不会详细介绍Vuex怎么去使用,这篇唠嗑主要讲的Vuex的实现原理,入门的话可以到官网溜一波

    温馨提示:定义好的Vuex就得在Vue实例参数传上Vuex实例
new Vue({
  store,
  render: h => h(App),
}).$mount('#app')

Vuex的原理是怎么样的?六步带你撕开它神秘的面纱

第一步,初始化Vuex文件

在自己感觉看着舒服的微信建一个miniVuex.js文件
let Vue
    
class Store{

    constructor(options) {}
    
}
function install(_Vue){
    
}
    
export default { Store, install }

为什么要定义这Store跟install方法?

Store方法应该大家都很好理解,回想一下Vuex的用法,是不是上来就给它来一个new Vuex.Store?然后传进去一个Object?

import Vuex from 'Vuex'

let store = new Vuex.Store({})

再回看自己定义Store函数,是不是清晰很多了?Store函数接收一个options实参对象

可能大家对于为什么要定义install函数有点困惑(如果开发过Vue插件,或者熟悉Vue.use()Api的大佬,请忽略),我们再回想一下当,导出Vuex的时候,是不是要Vue.use(Vuex)一下?

import Vuex from 'Vuex'

Vue.use(Vuex)

因为当Vue.use(Vuex)Api的时候,Vue.use会将调用插件(这里是Vuex)install函数,然后将Vue传进去,并可以在install函数里通过this可以访问Vue实例。所以在开发插件的时候,install函数是必须要提供的。

第二步,实现install方法

Vuexinstall函数,主要是拿到Vue保存起来,然后,通过Vue.mixin混入将store挂载到Vue.prototype上;至于平时在页面上我们为什么能通过this.$store访问Vuex实例,就是在这里混入进去的;至于使用Vue.mixin混入来挂载Vuex实例Vue.prototype,主要是因为,我只想在new Vue的时候只在beforeCreate钩子里挂载一次就ok,不想每个页面都去重新挂载一次。

具体代码如下👇


let Vue

class Store {
    constructor(options) {}
}

// 新增install实现
function install(_Vue) {

    Vue = _Vue

    Vue.mixin({

        beforeCreate() {

            if (this.$options.store) {

                Vue.prototype.$store = this.$options.store

            }

        }

    })

}

export default { Store, install }

第三步,实现state的双向绑定

实现staet的双向绑定是件非常简单的事,我们只需要利用Vue自身的双向绑定机制就就可以实现;我们主要将new Vuex.Store({state:{}})存放在一个new Vue 实例上就可以。之所以Vuex官网说Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式,就是因为Vuex内部实现双向绑定的时候,使用的是Vue自身的双向绑定机制,将Vue强强的绑在身上。

let Vue

class Store {

    constructor(options) {
    
        //新增代码
        this.state = new Vue({

            data: options.state && options.state || {}

        })
        
    }
}


function install(_Vue) {

    Vue = _Vue

    Vue.mixin({

        beforeCreate() {

            if (this.$options.store) {

                Vue.prototype.$store = this.$options.store

            }

        }

    })

}

export default { Store, install }

第四步,实现mutations和commit

实现mutations只要是将传进来的mutations保存到实例就行,如果不传就默认为一个空对象;在实现commite之前,先来看看commit的用法。

 this.$store.commit(type, params)

commit主要是将传进来的type,找到对应的mutations里面的函数,进行触发,并将this.state传给它的一个参数(因为在定义mutations的时候是不是(可以参数下面第一段示例代码)在第一个参数可以访问state里面的数据?至于为什么可以访问,就是在这个时候传进去的),第二个参数才是额外的参数。

mutations定义示例代码

 mutations: {

        addOne(state, params) {

            state.num = state.num + params.num

        }

    }

具体代码如下👇


let Vue

class Store {

    constructor(options) {

        this.state = new Vue({

            data: options.state && options.state || {}

        })
        //新增代码
        this.mutations = options.mutations && options.mutations || {}

    }
    // 新增代码
    commit = (type, params) => {

        this.mutations[type]&&this.mutations[type](this.state, params)

    }

}

function install(_Vue) {

    Vue = _Vue

    Vue.mixin({

        beforeCreate() {

            if (this.$options.store) {

                Vue.prototype.$store = this.$options.store

            }

        }

    })

}

export default { Store, install }

至于commit为什么要使用箭头函数,这里做一个解释,因为在actions里面异地再提交一下commit(如下面示例代码),那么会导致this中的上下文产生变化,不再指向Store实例就报一个mutations of undefined的错误(如下面图片);为了避免这种情况出现,使用了箭头函数。

示例代码

actions: {

        addOneAsync({ commit }, params) {
            setTimeout(() => {
                commit('addOne', params)
            }, 1000)
        }

    }
    

报错提示

第五步,实现getters

getters的原理,也不难理解,但是实现就要手动通过(Object.defineProperty)去监听,它的那个getter别使用了,再触发对应的getter,getters是只读,所以在Object.defineProperty监听的时候,只需要提供get(有传的前提在才会进行Object.defineProperty监听,不传的话,什么也不敢),然后把this.state传给它的一个参数(因为在定义getter的时候,可以通过第一个参数访问state,可参考下面的示例)。

温馨提示:上面的getters是指整个大的getters,getter是指的getters里面小的各个键值对(比如下面示例中的numStore),我怕有同学会不知道,还是提一下

定义getters示例

getters: {

        numStore(state) {

            return state.num + 1

        }

    }

具体实现代码👇


let Vue

class Store {

    constructor(options) {

        this.state = new Vue({

            data: options.state && options.state || {}

        })

        this.mutations = options.mutations && options.mutations || {}
        //新增代码
        options.getters && this.initGetters(options.getters)

    }
    //新增代码
    initGetters(getters) {

        this.getters = {}

        Object.keys(getters).forEach(key => {

            Object.defineProperty(this.getters, key, {

                get: () => {

                    return getters[key](this.state)

                }

            })

        })

    }

    commit = (type, params) => {

        this.mutations[type]&&this.mutations[type](this.state, params)

    }

}


function install(_Vue) {

    Vue = _Vue

    Vue.mixin({

        beforeCreate() {

            if (this.$options.store) {

                Vue.prototype.$store = this.$options.store

            }

        }

    })

}

export default { Store, install }

第六步,实现actions和dispatch

actionsdispatch的实现跟mutationscommit非常类似;实现actions只要是将传进来的actions保存到实例就行,如果不传就默认为一个空对象;在实现dispatch之前,先来看看dispatch的用法。

 this.$store.dispacth(type, params)

dispacth主要是将传进来的type,找到对应的actions里面的函数,进行触发,并将一个Object(里面包含commit,dispatch,state)传给它的一个参数(因为在定义actions的时候是不是在第一个参数可以访问这些(commit,dispatch,state…可以参数下面第一段示例代码)?至于为什么可以访问,就是在这个时候传进去的),第二个参数才是额外的参数。

示例代码

actions: {
        addOneAsync({ commit,dispatch,state }, params) {
            setTimeout(() => {
                commit('addOne', params)
            }, 1000)
        }
    }

具体实现代码👇


let Vue

class Store {

    constructor(options) {

        this.state = new Vue({

            data: options.state && options.state || {}

        })

        this.mutations = options.mutations && options.mutations || {}
        //新增代码
        this.actions = options.actions && options.actions || {}

        options.getters && this.initGetters(options.getters)

    }

    initGetters(getters) {

        this.getters = {}

        Object.keys(getters).forEach(key => {

            Object.defineProperty(this.getters, key, {

                get: () => {

                    return getters[key](this.state)

                }

            })

        })

    }

    commit = (type, params) => {

        this.mutations[type](this.state, params)

    }
    //新增代码
    dispatch(type, params) {

        this.actions[type] && this.actions[type]({
            commit: this.commit,
            state: this.state,
            dispatch: this.dispatch
        }, params)

    }

}


function install(_Vue) {

    Vue = _Vue

    Vue.mixin({

        beforeCreate() {

            if (this.$options.store) {

                Vue.prototype.$store = this.$options.store

            }

        }

    })

}

export default { Store, install }

好了,以上那段也是整个miniVuex的完整代码了,里面我们实现了state,getters,mutations,actions,commit,dispatch;至于怎么使用,就跟之前怎么用Vuex一样,把import的路径换成我们自己的miniVuex路径

使用例子:


import Vuex from './miniVuex'

import Vue from 'vue'

Vue.use(Vuex)

let store = new Vuex.Store({

    state: {},

    getters: {},

    mutations:{},

    actions: {}
    
})

export default store

** 有错误的地方,欢迎大佬指出;有解释不够充分的地方,欢迎大佬补充… **

版权声明:著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。 作者: Napoleon 原文链接:https://juejin.im/post/6869521421278806029

回到顶部