• 手写Vuex源码


    Vuex原理解析

    Vuex是基于Vue的响应式原理基础,所以无法拿出来单独使用,必须在Vue的基础之上使用。

     

    1.Vuex使用相关解析

    main.js

     
    1 import store form './store' // 引入一个store文件
    2 3  new Vue({
    4      // 在Vue初始化的过程中,注入一个store属性,内部会将这个属性放到每个组件的$store上
    5      store, 
    6  })

     

    store.js

     
     1 import Vuex from 'Vuex'
     2  3  Vue.use(Vuex) 
     4  5  // 通过Vuex中的一个属性 Store 创建一个store的实例
     6  export default new Vuex.Store({
     7      state: { // 单一数据源
     8          age: 10
     9      },
    10      mutations: { // 
    11          // payload 载荷 
    12          syncChange(state,payload) { // 修改状态的方法 同步更改
    13              state.age+= payload
    14          }
    15      },
    16      actions: {
    17          asyncChange({commit}, payload) {
    18              setTimeout(() => {
    19                  commit('syncChange',payload)
    20              },1000)
    21          }
    22      }
    23  })
    24  //mutations中增加异步操作 严格模式下会直接报错,普通模式下不会报错但不合法

     

    2.Vuex原理解析实现

    首先我们要清楚Vuex的定位,它是一个插件。且必须基于之上Vue来使用,为什么这么说呢,因为他的数据响应是基于Vue的。

     

    1.Vuex核心概念

    state 驱动应用的数据源。

    Getter getter的返回值会根据它的依赖被缓存起来,且只有当它的依赖值发生变化了改变才会被重新计算(由此你是不是想到了计算属性,对完全可以这么理解)。

    Mutation 进行Vuex中store状态的更改,也是官方规定更改状态的唯一途径。

    Action 进行异步操作的场所,但是更改数据还是需要commit提交。

    Module 单一状态树对象比较复杂,Vuex允许我们将Store分割成多模块,每个模块拥有自己独立的内容。

     

    2.实现Vuex

    store.js

    先创建一个入口文件

     
     1 import Vue from 'vue'
     2  // import Vuex from 'vuex' 官方的Vuex插件
     3  import Vuex from './vuex/index1' // 自己写的Vuex
     4  5  Vue.use(Vuex) // 默认会执行当前插件的install方法
     6  7  // 通过Vuex中的一个属性 Store 创建一个store的实例
     8  export default new Vuex.Store({
     9    // 定义数据
    10    modules: {
    11      a: {
    12        state: {
    13          age: 'a100'
    14        },
    15        mutations: {
    16          syncChange() {
    17            console.log('a');
    18          }
    19        },
    20      },
    21      b: {
    22        state: {
    23          age: 'b100'
    24        },
    25        mutations: {
    26          syncChange() {
    27            console.log('b');
    28          }
    29        },
    30        modules: {
    31          c: {
    32            state: {
    33              age: 'c100'
    34            },
    35            mutations: {
    36              syncChange() {
    37                console.log('c');
    38              }
    39            },
    40          }
    41        }
    42      }
    43    },
    44    state: {
    45      age: 10
    46    },
    47    mutations: {
    48      syncChange(state, payload) {
    49        state.age += payload
    50      }
    51    },
    52    actions: {
    53      asyncChange({ commit }, payload) {
    54        setTimeout(() => {
    55          commit('syncChange', payload)
    56        }, 1000)
    57      }
    58    },
    59    getters: {
    60      myAge(state) {
    61        return state.age + 20
    62      }
    63    }
    64  })

     

    index1.js

    这边会暴露出一个install方法,Vue.sue()的时候会调用它。还有一个提供实例化的Store类

     
      1 let vue
      2  const install = (_Vue) => {
      3      Vue = _Vue // install方法调用时,会将Vue作为参数传入
      4      
      5      Vue.mixin({ // 全局注册一个混入,影响注册以后的每一个创建的Vue实例。
      6          beforeCreate() {
      7          // 判断当前根实例上有没有store,有的话把根组件的的store属性 放到每个组件的实例上
      8          // 这样每个组件上都能直接实现this.$store去访问store里面的东西
      9              if(this.$option.store) { 
     10                  this.$store = this.$options.store
     11              } else {
     12                  this.$store = this.$parent && this.$parent.$store
     13              }
     14          }
     15      })
     16  }
     17  18  19  class Store { // 用户获取的是这个store类的实例
     20      constructor(options) {
     21          // 创建Vue的实例 保证更新状态可以刷新视图
     22          this.vm = new Vue({
     23              data: {
     24                  state: optons.state
     25              }
     26          })
     27      }
     28      
     29      // es6 的访问器
     30      get state() {
     31          return this.vm.state
     32      }
     33      
     34      this.getters = {}
     35      this.mutations = {}
     36      this.actions = {}
     37      // 1、需要将用户传入的数据进行格式化操作
     38      this.moudules = new ModulesCollections(options)
     39  40      // 2、递归的安装模块
     41      installModule(this,this.state,[], this.modules.root)
     42  43      // 调用
     44      commit = (mutationName, payload) => {
     45          // es7写法 这个里面的this 永远指向当前的store实例
     46          this.mutaions[mutationName].forEach(fn =>fn(payload))
     47      }
     48      
     49      dispath = (actionName, payload) => {
     50          this.actions[actionName].forEach(fn =>fn(payload))
     51      }
     52  }
     53  54  // 定义一个forEach遍历当前对象属性然后执行一个回调函数,后面要用到
     55  const forEach = (obj, callback) => {
     56      Object.keys(obj).forEach(key => {
     57          callback(key, obj[key])
     58      })
     59  }
     60  61  // 格式化用户数据
     62  class ModuleCollection {
     63      constructor(options) {
     64          // 深度将所有的子模块都遍历一遍
     65          this.register([], ooptions)
     66      }
     67      register(path, rootModule) {
     68          let rawModule = {
     69              _raw: rootModule,
     70              _children: {},
     71              state: rootModule.state
     72          }
     73          if(!this.root) {
     74              this.root = rawModule
     75          } else {
     76              // 找到要定义的模块,将这个模块定义他父亲的_children属性里
     77              let parentModule = path.slice(0,-1).reduce((root, current) => {
     78                  return root._children[current]
     79              }, this.root)
     80              parentModule._childen[path[path.length - 1]] = rawModule
     81          }
     82          
     83          // 如果有子模块
     84          if(rootModule.modules) {
     85              forEach(rootModule.modules,(moduleName, module) => {
     86                  this.register(path.concat(moduleName), module)
     87              })
     88          }
     89      }
     90  }
     91  92  // 递归安装模块
     93  function installModule(store, rootState, path, rawModeule) {
     94      // 如果有子模块,安装子模块的状态
     95      if(path.length > 0) {
     96          let parentState = path.slice(0,-1).reduce((root, current) => {
     97              return root[current]
     98          }, rootState)
     99           Vue.set(parentState, path[path.length -1],rawModule.state)
    100      }
    101      
    102      let getters = rawModule._raw.getters // 取用户的getter
    103      if(getters) {
    104          forEach(getters, (getterName, value) => {
    105              Object.defineProperty(store.getters, getterName, {
    106                  get: () => {
    107                      return value(rawModule.state)
    108                  }
    109              })
    110          })
    111      }
    112      
    113      let mutations = rawModule.raw.mutations // 取用户的mutation
    114      if(mutations) {
    115          forEach(mutations, (mutationName, value) => {
    116         let arr = store.mutations[mutationName] || (store.mutaons[mutationName] = [])
    117          })
    118          arr.push((plyload) => {
    119              value(rawModule.state, payload)
    120          })
    121      }
    122      
    123      let actions = rawModule._raw.actions // 取用户的action
    124      if(actions) {
    125          forEach(actions, (actionName, value) => {
    126              let arr = store.actions[actionName] || (store.actions[actionName] = [])
    127              arr.push((payload) => {
    128                  value(store, payload)
    129              })
    130          })
    131      }
    132      
    133      // 递归
    134      forEach(rawModule._childen, (moduleName, rawModule) => {
    135          installModule(store, rootState, path.concat(moduleName),rawModule)
    136      })
    137  }

     

    3.实现步骤总结:

    1、作为插件引入,执行install方法调用Vue.mixin在Vue全局生命周期混入一个方法,将Vuex中定义的数据源挂载到this.$store,即当前组件的实例上。

    2、state 直接new Vue实例,将数据源传入。完成数据源响应式操作。

    3、getters 递归遍历用户传入的getters对象,拿到每个里面每一个函数,通过Object.definedProperty属性处理。当get函数读取compile,触发get调用相应函数(函数内部自动传入当前数据源state作为参数),完成数据响应。

    4、mutations 递归遍历用户传入的mutations 对象,将相同名称下的函数都挂载到当前实例的mutations数组中,完成订阅。commit的时候拿到对应的函数名称进行遍历mutations数组调用对应名称函数,完成发布。

    5、actiosns 操作和mutations一样。

    6、module 是将用户传入的数据进行格式化,格式化好以后执行上面的安装模块的方法。具体查看上方installModule方法的详细操作。

     

  • 相关阅读:
    nginx+keepalived高可用 (主从+双主)
    element ui中table动态列切换时,表格样式变形
    vue中的Swiper使用slideTo提示no function
    element的upload手动submit前动态设置上传请求地址
    使用svg让页面自适应浏览器大小,整体等比缩放
    vue子组件为父组件属性写值
    C#使用SharpZipLib解压多文件的zip压缩文件数据流,保存到本地
    javascript使用正则表达式,从字符串提取内容,多数组解析
    sql server 分组排序
    IE浏览器下错误,不能执行已释放script的代码
  • 原文地址:https://www.cnblogs.com/dingxingxing/p/13387051.html
Copyright © 2020-2023  润新知