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

思考问题

  1. Vuex 只在更实例引入了,那么它是如何在每个子组件中都能使用的?
  2. Vuex 是一个插件,为什么更改了Vuex中的state,会触发视图的跟新?

vuex原理

vuex 是vue的状态管理工具,目的是为了更方便实现多个组件之间的状态共享

vuex的工作原理, Vuex 官方文档 

vuex实现

  1. 新建一个vuex文件,导出一个store对象

    let Vue;
    class Store { }
    const install = (_Vue) => {
    Vue = _Vue;
    } export default {
    // 这方法在use的时候默认会被调用
    install,
    Store
    }
  2. 使用混合,在创建组件之前,将vuex实例挂载到vue的实例上

    Vue.mixin({
    beforeCreate() {
    // 需要拿到store,给每个组件都增加 $store 属性
    // 为什么不直接给Vue.prototype 上增加?是因为可能会new 好多个Vue的实例,在别的实例上不需要store
    if (this.$options && this.$options.store) {
    this.$store = this.$options.store;
    } else {
    // 这里判断一下,如果单独创建了一个实例没有parent
    this.$store = this.$parent && this.$parent.$store;
    }
    }
    });
  3. 获取new Store传入的对象

    class Store {
    constructor(options = {}) {
    // 将用户的状态放入到 store 中
    this.state = options.state; // 获取计算属性
    let getters = options.getters;
    this.getters = {};
    Object.keys(getters).forEach(getterName => {
    Object.defineProperty(this.getters, getterName, {
    get: () => {
    return getters[getterName](this.state);
    }
    });
    });
    }
    }
  4. state的数据变成响应式的数据

    class Store {
    constructor(options = {}) {
    // Vuex 的核心,定义了响应式变化,数据更新之后更新视图
    this._vm = new Vue({
    data() {
    return {
    state: options.state
    };
    }
    });
    }
    // 类的属性访问器
    get state() {
    return this._vm.state;
    }
    }
  5. 通过触发 mutations 更改状态

    // 通过 this.commit() 触发更改
    mutations: {
    syncAdd(state, payload) {
    state.age += payload;
    }
    } // 通过发布订阅的模式
    class Store {
    constructor(options = {}) {
    let mutations = options.mutations;
    this.mutations = {};
    Object.keys(mutations).forEach(mutationName => {
    // 订阅所有的mutations
    this.mutations[mutationName] = (payload) => {
    // 内部的第一个参数是状态
    mutations[mutationName](this.state, payload);
    }
    });
    }
    // 提交更改,会在当前的 store 上找到对应的函数执行
    // 发布
    commit = (mutationName, payload) => { // 保证this
    this.mutations[mutationName](payload);
    }
    }
  6. 内部封装的 forEach, 减少重复代码

    const forEachValue = (obj, fn) => {
    Object.keys(obj).forEach(key => {
    fn(key, obj[key]);
    });
    }; // 对上面的 getters 改造下
    forEachValue(getters, (gettersName, fn) => {
    Object.defineProperty(this.getters, getterName, {
    get: () => {
    return fn(this.state);
    }
    });
    }); // 对上面的mutations 改造下
    forEachValue(mutations, (mutationName, fn) => {
    this.mutations[mutationName] = (payload) => {
    fn(this.state, payload);
    }
    })
  7. 通过触发 action 异步跟新转态

    action 异步提交更改,异步操作完之后提交到mutation中
    例:
    actions: {
    asyncMinus({ commit }, payload) {
    setTimeout(() => {
    commit('syncMinus', payload);
    }, 1000);
    }
    }
    mutations: {
    syncMinus(state, payload) {
    state.age -= payload;
    }
    } // 也是一个发布订阅模式
    class Store {
    constructor(options ={}) {
    let actions = options.actions;
    this.actions = {};
    forEachValue(actions, (actionName, fn) => {
    this.actions[actionName] = (payload) => {
    fn(this, payload);
    }
    });
    }
    dispatch = (actionName, payload) => {
    // 源码里有一个变量,来控制是否是通过mutation 来更新的转态,不是会抛个警告
    this.actions[actionName](payload);
    }

    vuex简单实现

    let Vue;
    const forEachValue = (obj = {}, fn) => {
    return Object.keys(obj || {}).forEach(key => {
    fn(key, obj[key]);
    })
    }
    class Store {
    constructor(options = {}) {
    this._vm = new Vue({
    data() {
    return {
    state: options.state
    }
    }
    }); let getters = options.getters;
    this.getters = {};
    forEachValue(getters, (getterName, fn) => {
    Object.defineProperty(this.getters, getterName, {
    get: () => {
    return fn(this.state);
    }
    });
    }); // mutations
    let mutations = options.mutations;
    this._mutations = {};
    // 订阅
    forEachValue(mutations, (mutationName, fn) => {
    this._mutations[mutationName] = (paylod) => {
    fn(this.state, paylod);
    }
    }); // actions
    let actions = options.actions;
    this._actions = {};
    forEachValue(actions, (actionName, fn) => {
    this._actions[actionName] = (paylod) => {
    fn(this, paylod);
    }
    });
    }
    // 发布
    commit = (mutationName, paylod) => {
    this._mutations[mutationName](paylod);
    }
    dispatch = (actionName, paylod) => {
    this._actions[actionName](paylod);
    }
    get state() {
    return this._vm.state;
    }
    }
    const install = (_Vue) => {
    Vue = _Vue;
    Vue.mixin({
    beforeCreate() {
    if (this.$options && this.$options.store) {
    this.$store = this.$options.store;
    } else {
    this.$store = this.$parent && this.$parent.$store;
    }
    }
    });
    } export default {
    install,
    Store
    };
  8. modules的实现

    主要是将mosules里面的数据格式化成我们想要的格式 js { _modules: { root: state: {__ob__: Observer} _children: {} _rawModule: {modules: {…}, state: {…}, getters: {…}, mutations: {…}, actions: {…} } }

    • 数据格式化
    // 在 Store 中定义modules
    this._modules = new ModuleCollection(options); // 把数据格式化成我们想要的结构 class ModuleCollection {
    constructor(options) { // 模块依赖的收集
    this.register([], options); // 注册模块,将模块注册成树结构
    }
    register(path, rootModule) {
    let module = { // 将模块格式化
    _rawModule: rootModule,
    _children: {},
    state: rootModule.state
    }
    if (path.length === 0) { // 如何是根模块 将这个模块挂载到根实例上
    this.root = module;
    } else {
    // 递归都用reduce方法, 通过_children 属性进行查找
    let parent = path.slice(0, -1).reduce((root, current) => {
    return root._children[current];
    }, this.root);
    parent._children[path[path.length - 1]] = module;
    }
    // 看当前模块是否有modules
    if (rootModule.modules) { // 如果有modules 开始重新注册
    forEachValue(rootModule.modules, (moduleName, module) => {
    this.register(path.concat(moduleName), module);
    })
    }
    }
    }
    • 安装模块
    installModule(this, this.state, [], this._modules.root);
    
      // 安装模块
    const installModule = (store, rootState, path, rootModule) => {
    // 将state 挂载到根上
    if (path.length > 0) {
    let parent = path.slice(0, -1).reduce((root, current) => {
    return root[current];
    }, rootState);
    // vue 不能再对象上增加不存在的属性,否则视图不会更新
    // parent.path[path.length - 1] = rootModule.state;
    Vue.set(parent, path[path.length - 1], rootModule.state);
    }
    // getters
    let getters = rootModule._rawModule.getters;
    if (getters) {
    forEachValue(getters, (getterName, fn) => {
    Object.defineProperty(store.getters, getterName, {
    get() {
    // 让getters 执行,将自己的状态传入
    return fn(rootModule.state); // 将对应的函数执行
    }
    });
    });
    }
    // mutations
    let mutations = rootModule._rawModule.mutations; // 拿到每个模块里的mutations
    if (mutations) {
    forEachValue(mutations, (mutationName, fn) => {
    let mutations = store._mutations[mutationName] || [];
    mutations.push((paylod) => {
    fn.call(store, rootModule.state, paylod);
    // 发布,让所有的订阅依次执行
    store._subscribes.forEach(fn => fn({ type: mutationName, paylod }, rootState))
    });
    store._mutations[mutationName] = mutations;
    });
    }
    // actions
    let actions = rootModule._rawModule.actions; // 拿到每个模块里的mutations
    if (actions) {
    forEachValue(actions, (actionName, fn) => {
    let actions = store._actions[actionName] || [];
    actions.push((paylod) => {
    fn.call(store, store, paylod);
    });
    store._actions[actionName] = actions;
    });
    }
    // 循环挂载儿子
    forEachValue(rootModule._children, (moduleName, module) => {
    installModule(store, rootState, path.concat(moduleName), module);
    });
    }

    vuex完整实现

    let Vue;
    const forEachValue = (obj = {}, fn) => {
    return Object.keys(obj || {}).forEach(key => {
    fn(key, obj[key]);
    });
    }
    class ModuleCollection {
    constructor(options) { // 模块依赖的收集
    this.register([], options); // 注册模块,将模块注册成树结构
    }
    register(path, rootModule) {
    let module = { // 将模块格式化
    _rawModule: rootModule,
    _children: {},
    state: rootModule.state
    }
    if (path.length === 0) { // 如何是根模块 将这个模块挂载到根实例上
    this.root = module;
    } else {
    // 递归都用reduce方法, 通过_children 属性进行查找
    let parent = path.slice(0, -1).reduce((root, current) => {
    return root._children[current];
    }, this.root);
    parent._children[path[path.length - 1]] = module;
    }
    // 看当前模块是否有modules
    if (rootModule.modules) { // 如果有modules 开始重新注册
    forEachValue(rootModule.modules, (moduleName, module) => {
    this.register(path.concat(moduleName), module);
    })
    }
    }
    }
    // 安装模块
    const installModule = (store, rootState, path, rootModule) => {
    // 将state 挂载到根上
    if (path.length > 0) {
    let parent = path.slice(0, -1).reduce((root, current) => {
    return root[current];
    }, rootState);
    // vue 不能再对象上增加不存在的属性,否则视图不会更新
    // parent.path[path.length - 1] = rootModule.state;
    Vue.set(parent, path[path.length - 1], rootModule.state);
    }
    // getters
    let getters = rootModule._rawModule.getters;
    if (getters) {
    forEachValue(getters, (getterName, fn) => {
    Object.defineProperty(store.getters, getterName, {
    get() {
    // 让getters 执行,将自己的状态传入
    return fn(rootModule.state); // 将对应的函数执行
    }
    });
    });
    }
    // mutations
    let mutations = rootModule._rawModule.mutations; // 拿到每个模块里的mutations
    if (mutations) {
    forEachValue(mutations, (mutationName, fn) => {
    let mutations = store._mutations[mutationName] || [];
    mutations.push((paylod) => {
    fn.call(store, rootModule.state, paylod);
    // 发布,让所有的订阅依次执行
    store._subscribes.forEach(fn => fn({ type: mutationName, paylod }, rootState))
    });
    store._mutations[mutationName] = mutations;
    });
    }
    // actions
    let actions = rootModule._rawModule.actions; // 拿到每个模块里的mutations
    if (actions) {
    forEachValue(actions, (actionName, fn) => {
    let actions = store._actions[actionName] || [];
    actions.push((paylod) => {
    fn.call(store, store, paylod);
    });
    store._actions[actionName] = actions;
    });
    }
    // 循环挂载儿子
    forEachValue(rootModule._children, (moduleName, module) => {
    installModule(store, rootState, path.concat(moduleName), module);
    });
    }
    class Store {
    constructor(options = {}) {
    this._vm = new Vue({
    data() {
    return {
    state: options.state
    }
    }
    });
    this.getters = {};
    this._mutations = {};
    this._actions = {};
    this._subscribes = [];
    this._modules = new ModuleCollection(options); // 把数据格式化成一个想要的数据结构 // 递归将结果分类
    // this 整个store
    // this.state 当前的根状态,把模块中的状态放在根上
    // [] 是为了递归的初始值
    // this._modules.root 是为了从跟模块开始安装
    installModule(this, this.state, [], this._modules.root); // plugins
    if (!Array.isArray(options.plugins)) {
    throw new TypeError('plugins is not Array');
    }
    options.plugins.forEach(fn => fn(this));
    }
    // 发布
    commit = (mutationName, paylod) => {
    this._mutations[mutationName].forEach(fn => fn(paylod));
    }
    dispatch = (actionName, paylod) => {
    this._actions[actionName].forEach(fn => fn(paylod));
    }
    // 订阅所有的plugins
    subscribe = (fn) => {
    this._subscribes.push(fn);
    }
    get state() {
    return this._vm.state;
    }
    }
    const install = (_Vue) => {
    Vue = _Vue;
    Vue.mixin({
    beforeCreate() {
    if (this.$options && this.$options.store) {
    this.$store = this.$options.store;
    } else {
    this.$store = this.$parent && this.$parent.$store;
    }
    }
    });
    } export default {
    install,
    Store
    };

最新文章

  1. 开始使用 Fresco
  2. 在Ext JS 6中添加本地化包
  3. C++ 栈和队列
  4. 安装Office时出现windows installer服务不能更新一个或多个受保护的windows文件错误的解决方法
  5. Validform表单验证的完美解决方案,推荐给大家
  6. Controller 里面就只应该存放这些不能复用的代码(转)
  7. 201521123037 《Java程序设计》第8周学习总结
  8. Hibernate问题浅析
  9. 在Debian9服务器上安装最新版Python
  10. kolla queens on centos7.5 -all in one
  11. oracle权限管理学习
  12. java反射出字段信息和值
  13. 【Java】浅谈HashMap
  14. Linux slab分配器【转】
  15. python 获取中文文件名的输出
  16. Mac下显示和隐藏隐藏文件的命令
  17. configServer的高可用
  18. PAT——1069. 微博转发抽奖
  19. javastscript获取光标位置
  20. 微信小程序 js逻辑

热门文章

  1. mybatis实现增删改
  2. 微信小程序下拉刷新时有部分区域不随着下拉移动
  3. uCOS2014.1.7
  4. [hdu1402]大数乘法(FFT模板)
  5. CI与CD之Docker上安装Jenkins
  6. vue 在main.js里使用vue实例
  7. myeclipse 创建maven web项目
  8. FF按钮点击后表单提交
  9. chmod指令用法
  10. python3.x 基础三:字符集问题