一文讲解Vuex实现原理

前言

 对于Vuex相信大家是常用,那么Vuex作为Vue生态中最重要的一部分,专为Vue.js应用的状态管理模式,每一个 Vuex 应用的核心就是 store(仓库)。“store” 基本上就是一个容器,它包含着你的应用中大部分的状态 ( state ),并且Vuex 的状态存储是响应式的。当 Vue 组件从 store 中读取状态的时候,若 store 中的状态发生变化,那么相应的组件也会相应地得到高效更新。改变 store 中的状态的唯一途径就是显式地提交 (commit) mutation。这样可以方便地跟踪每一个状态的变化。

原理分析

Vuex装载分析

 这个问题实际就是问到Vuex的store是如何装载到组件中的,首先利用了Vue的插件机制使用Vue.use(Vuex)来去安装Vuex插件,那么此时会调用vuex的install方法,当调用install时此时会利用mixin机制在beforeCreate阶段去执行vuexInit。核心源码如下:

Vue.mixin({
    
     beforeCreate: vuexInit });

 我们可以发现在beforeCreate阶段调用了vuexInit方法,我们分析一下vuexInit方法。

  /**
   * Vuex init hook, injected into each instances init hooks list.
   */

  function vuexInit () {
    
    
    const options = this.$options
    // store injection
    if (options.store) {
    
    
      this.$store = typeof options.store === 'function'
        ? options.store()
        : options.store
    } else if (options.parent && options.parent.$store) {
    
    
      this.$store = options.parent.$store
    }
  }

 分析如上代码:将初始化Vue根组件时传入的store设置到this对象的$store属性上,子组件从其父组件引用$store属性,层层嵌套进行设置。在任意组件中执行 this.$store 都能找到装载的那个store对象。

Vuex中的state与getter

 vuex的Store 会划分出 state 和 getters 两个数据区。getter是从store的state中派生出的状态。那么首先我们先看我们是如何访问state的?

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

 当我们使用this.$store.state.xxx去获取xxx属性时,实际获取的是store挂载到_vm中store._vm.data.$$state中的数据。
 state是如何挂载上去的?我们在Store constructor找到了核心函数resetStoreVM,观察resetStoreVM的核心代码,其主要做的事情是初始化了一个vue实例_vm,由于vue的data是响应式的,所以,$$state也是响应式的,那么当我们 在一个组件实例中 对state.xxx进行 更新时,基于vue的data的响应式机制,所有相关组件的state.xxx的值都会自动更新,视图自然也会自动更新,核心代码如下:

  store._vm = new Vue({
    
    
    data: {
    
    
      $$state: state
    },
    computed
  })

 上面所介绍的是state,那么接下来我们介绍一下getter,其核心源码也是在
resetStoreVM中,核心源码如下:

  forEachValue(wrappedGetters, (fn, key) => {
    
    
    // use computed to leverage its lazy-caching mechanism
    // direct inline function use will lead to closure preserving oldVm.
    // using partial to return function with only arguments preserved in closure environment.
    computed[key] = partial(fn, store)
    Object.defineProperty(store.getters, key, {
    
    
      get: () => store._vm[key],
      enumerable: true // for local getters
    })
  })

 until.js的部分源码如下:

/**
 * forEach for object
 */
export function forEachValue (obj, fn) {
    
    
  Object.keys(obj).forEach(key => fn(obj[key], key))
}
export function partial (fn, arg) {
    
    
  return function () {
    
    
    return fn(arg)
  }
}

 对wrappedGetters 进行处理,让getter 存储至computed对象上,对getter对象的属性进行数据劫持,当触发get时,返回store._vm[key],最后将computed挂载到vue实例上,当做计算属性。

  store._vm = new Vue({
    
    
    data: {
    
    
      $$state: state
    },
    computed
  })

总结

  • vuex利用了vue的mixin机制,混合 beforeCreate 钩子 将store注入至vue组件实例上,并注册了 vuex store的引用属性 $store。
  • vuex的state是借助vue的响应式data实现的。getter是借助vue的计算属性computed特性实现的。

Vue源码系列文章:

猜你喜欢

转载自blog.csdn.net/liu19721018/article/details/125570495