乐趣区

关于vuex:简单实现Vuex

github,blog

Vuex

Vuex集中式 存储管理利用的所有组件的状态,并以相应的规定保障状态以 可预测 的形式发生变化。

装置 Vuex

vue add vuex

外围概念

  • state:状态、数据
  • mutations:更改状态的函数
  • action:异步操作
  • store:蕴含以上概念的容器

状态 – state

state保留利用状态

export default new Vuex.Store({
  state: {counter: 0}
})
<h1>
  {{$store.state.counter}}
</h1>

状态变更 – mutations

mutations用于批改状态

export default new Vuex.Store({
  mutations:{add(state){state.counter++}
  }
})
<h1 @click="$store.commit('add')">
  {{$store.state.counter}}
</h1>

派生状态 – getters

state 派生进去新状态,相似计算属性

export default new Vuex.Store({
  getters:{doubleCounter(state){return state.counter * 2;}
  }
})
<h1>
  {{$store.getters.doubleCounter}}
</h1>

动作 – actions

增加业务逻辑,相似于controller

export default new Vuex.Store({
  actions:{add({commit}){setTimeout(() => commit('add'), 1000);
    }
  }
})
<h1 @tap="$store.dispatch('add')">
  {{$store.state.counter}}
</h1>

Vuex 原理解析

任务分析

  • 实现插件

    • 实现 Store 类

      • 维持一个响应式状态 state
      • 实现 commit()
      • 实现 dispatch()
      • 实现 getters
    • 挂载 $store

创立新的插件

Vue2.x 我的项目中的 src 门路下,复制一份 store 文件,重命名为ou-store

而后在 ou-store 门路下新建一个 ou-vuex.js 文件,并将 index.js 文件中的 Vuex 引入改为ou-vuex.js

import Vuex from './ou-vuex'

同时将 main.js 中的 router 引入也批改一下。

import router from './ou-vuex'

创立 vue 的插件

回头看一下 store/index.js,首先是应用Vue.use() 注册了 Vuex,而后再实例化了Vuex.Store 这个类,因而 Vuex 这个对象里含有一个 install 办法以及一个 Store 的类。

import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex)

export default new Vuex.Store({...})

因而咱们来创立一个新的 Vuex 插件。

let Vue;    // 保留 Vue 的构造函数,插件中须要用到

class Store {}

function install(_Vue) {Vue = _Vue;}

export default {Store, install};

挂载$store

let Vue;    // 保留 Vue 的构造函数,插件中须要用到

class Store {}

function install(_Vue) {
    Vue = _Vue;

    Vue.mixin({beforeCreate() {
            // 挂载 $store
            if(this.$options.store){Vue.prototype.$store = this.$options.store;     // vm.$store}
        }
    })
}

export default {Store, install};

实现响应式保留 state 数据

因为 state 是一个对象,咱们能够应用 new Vue()state转换为一个响应式数据进行保存起来。

其次,咱们不能显式去保留这个 state,裸露给里面,因而咱们能够应用getset去保留。

class Store {
    /*
    * options:
    *   state
    *   mutations
    *   actions
    *   modules
    *   getters
    * */
    constructor(options = {}) {
        // data 响应式解决
        this._vm = new Vue({   
            data: {$$state: options.state    // 通过 this._vm._data.$$state 或 this._vm.$data.$$state 获取}
        });
    }

    // 获取 state
    get state() {return this._vm._data.$$state;}

    // 不可设置 state
    set state(v) {console.error('please use replaceState to reset state');
    }
}

实现 commit 办法

当咱们应用 commit 办法时,都是 $store.commit(type,payload),第一个参数即mutationstype值,第二个是 payload 负载,而对应 mutation 办法的参数为 statepayload,因而咱们来实现:

class Store {constructor(options = {}) {
        this._vm = new Vue({
            data: {$$state: options.state}
        });

        // 保留用户配置的 mutations 选项
        this._mutations = options.mutations;
    }

    get state() {return this._vm._data.$$state;}

    set state(v) {console.error('please use replaceState to reset state');
    }


    commit(type, payload) {
        // 获取 type 对应的 mutation
        const entry = this._mutations[type]
        if(!entry) {console.error(`unknown mutation type : ${type}`);
            return ;
        }

        // 传递 state 和 payload 给 mutation
        entry(this.state, payload)
    }
}

实现 dispatch 办法

dispatch办法跟 commit 办法大同小异,不同之处在于 dispatch 调用的是 action 异步函数,而 action 的参数为 contextpayloadpayload咱们能够通过 dispatch 的参数获取到,而 context 执行上下文其实就是实例中的this

action 是用来解决异步函数的,因而咱们须要对 dispatch 办法进行 this 绑定;同时,action办法中有可能会调用到 commit 办法,因而咱们也须要对 commit 办法进行 this 绑定。

class Store {constructor(options = {}) {
        this._vm = new Vue({
            data: {$$state: options.state}
        });

        // 保留用户配置的 mutations 选项和 actions 选项
        this._mutations = options.mutations;
        this._actions = options.actions;

        // 将 commit 和 dispatch 绑定 this,this.commit = this.commit.bind(this);
        this.dispatch = this.dispatch.bind(this);
    }

    get state() {return this._vm._data.$$state;}

    set state(v) {console.error('please use replaceState to reset state');
    }


    commit(type, payload) {const entry = this._mutations[type]
        if(!entry) {console.error(`unknown mutation type : ${type}`);
            return ;
        }

        entry(this.state, payload)
    }

    dispatch(type, payload) {
        // 获取用户编写的 type 对应的 action
        const entry = this._actions[type];
        if(!entry) {console.error(`unknown action type : ${type}`)
        }
        // 异步后果解决经常须要返回 Promise
        return entry(this, payload)
    }
}

实现 getters 派生状态

当咱们定义 getters 状态时,实际上是定义了一个function

getters: {doubleCounter(state) {return state.counter * 2;}
},

而应用 getters 中某一个派生状态时,实际上是失去一个值,也就是这个 function 的返回值。

<h4>double count: {{$store.getters.doubleCounter}}</h4>

这其实就有点像对象中的 get 属性,因而咱们能够应用 Object.defineProperty() 来实现getters

class Store {constructor(options = {}) {
        this._vm = new Vue({
            data: {$$state: options.state}
        });

        this._mutations = options.mutations;
        this._actions = options.actions;

        this.commit = this.commit.bind(this);
        this.dispatch = this.dispatch.bind(this);

        // 初始化 getters,默认为一个空对象
        this.getters = {};

        // 遍历 options.getters
        for (const key in options.getters) {
            const self = this;
            Object.defineProperty(
                this.getters,
                key,   // key 名
                {get() {
                          // 调用对应的函数,第一个参数为 state,将后果返回
                        return options.getters[key](self._vm._data.$$state)   
                    }
                }
            )
        }

    }
}
退出移动版