Vuex的原理解析

779 阅读2分钟

Vuex是什么?

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

image.png

这个状态管理应用包含以下几个部分:

  • state 驱动应用的数据源;
  • view 以声明方式将state映射到视图;
  • actions 响应在view上的用户输入导致的状态变化。

image.png

Vuex和单纯的全局对象不同点:

1.Vuex的状态存储是响应式的,当Vue组件从store中读取状态时,若store中的state发生变化,那么相对于的组件也会相应地得到高校更新。

2.不能直接改变store中的状态,当我们需要去改变store中的状态时,我们的Vuex提供了两种方法,分别是commit和dispatch(异步),这样我们能够方便的跟踪每个状态的变化。

Vuex的基本用法如下:

import Vue from 'vue'
import Vuex from 'vuex'
// Vuex是一个对象 install方法
//Vuex中有一个Store类
//混入到组件中 增添store属性
Vue.use(Vuex)

const store = new Vuex.Store({
    stgate:{
        name:'chen'
    },
    mutations:{   //同步操作方法
        changeName(state,name){ //method=> 同步的更改state  mutation的参数是状态
            state.name = name
        }
    },
    actions:{    //异步操作方法
        syncChangeName({commit},payload){
            setTimeout(() => {
                commit('changeName',payload)
            )
        }
    },
    getters:{   //计算属性
        myName:state.name + '123'
    }
})

现在我们开始整理vuex的原理实现,我们需要做的就是自己完成一个vuex的功能,Store的具体实现:

  • 创建响应式的state,保存mutations,actions和getters
  • 实现commit根据用户传入type执行相对应的mutataion
  • 实现dispatch根据用户传入type执行对应的action,同时传递上下文
  • 实现getters,按照getters定义对state做派生

实现Store类和state方法,代码如下:

let Vue;

class Vuex {
    constructor(option = {}) {
        // 将store中的state变成响应式
        this._vm = new Vue({
            data:{
                $$state:options.state
            }
        })
    }
    get state() {
        return this._vm._data.$$state
    }
    set state(v) {
        //在直接改变state的时候给予警告,禁止直接修改state
        console.error('please use replaceState to reset state')
    }
    
    functioninstall(_Vue){
        Vue = _Vue;
        //通过mixin的方法,在加载组件时,即挂载在Vue上
        Vue.mixin({
            beforeCreate() {
                if(this.$option.store) {
                    //绑定在vue上作为全局对象
                    Vue.prototype.$store = this.$options.store
                }
            }
        
        })
    }
}
// 到处Store类和install方法
export default {Store, install}

实现修改state的commit方法:


class Store {
    constructor(options = {}) {
        //保存用户配置的mutations选项
        this._mutataions = options.mutations || {}
    }
    commit(type, payload) {
        const entry = this._mutations[type];
        if(!entry) {
            return console.error('unkonwn mutation type: $(type)');
            return;
        }
        // 指定上下文为Store实例
        // 传递state给mutataion
        entry(this.state,payload)
    }
}

VUex还提供了一种异步的修改state的dispatch方法:

class Store {
    constructor(options = {}) {
        //保存用户配置的actions选项
        this._actions = options.actions || {}
        
        //绑定commit上下文否则action中调用commit会出问题
        //同时也把action绑了,因为action可以互调
        const store = this;
        const {commit, dispatch } = store;
        this.commit = function boundCommit(type, payload) {
            commit.call(store, type, payload)
        }
        this.action = function boundAction(type, payload) {
            return action.call(store)
        }
    }
    dispatch(type, payload) {
        const entry = this._actions[type]
        if(!entry) {
            return console.error('unkonwn mutation type: $(type)');
            return;
        }
        
        entry(this,payload)
    }
}

getters的实现,代码如下:

class Store {
    constructor(options = {}) {
        this._state = options.state;
        this._getters = options.getters;
        this.getters = {};
        
        this._vm = new Vue({
            data: {
                $$store: state
            },
            computed,
        })
        
        Object.key(_getters).forEach(key => {
            Object.defineProperty(this.getters, key, {
                //处理getters属性 具有缓存的 computed 带有缓存 (多次取值是如果值不变是不会重新取值)
                get: () => options.getters[key](this.state)
            })
        })
        forEachValue(options.getters, (fn, key)) => {
            //将用户的getters 定义在实例上, 计算属性是如何实现缓存
            computed[key] = () => fn(this.state);
            //当取值的时候执行计算属性的逻辑,此时就有缓存功能
            Object.defineProperty(this.getters, key, {
                get: () => fn(this._vm[key])
            })
        }
        
        
    }
    
}

computed具有缓存功能,可以在用户传入的getters的时候,将用户的getters 定义在实例上,computed[key] = () => fn(this.state) ,在取值的时候fn(this._vm[key])执行计算属性的逻辑。

vuex的相关辅助方法:

export function mapState(stateArr) {
    let obj = {};
    for (let i = 0; i < stateArr.length; i++) {
        let stateName = stateArr[i];
        obj[stateName] = function() {
            return this.$store.state[stateName]
        }
    }
    return obj
}

export function mapGetters(gettersArr) {
    let obj = {};
    for (let i = 0; i < gettersArr.length; i++) {
        let gettName = gettersArr[i];
        obj[gettName] = function() {
            return this.$store.getters[gettName]
        }
    }
    return obj
}

export function mapMutations(obj) {
  let res = {};
  Object.entries(obj).forEach(([key, value]) => {
      res[key] = function (...args) {
          this.$store.commit(value, ...args)
      }
  })
  return res;
}

export function mapActions(obj) {
  let res = {};
  Object.entries(obj).forEach(([key, value]) => {
      res[key] = function (...args) {
          this.$store.dispatch(value, ...args)
      }
  })
  return res;
}