Vuex 原理解析

前言

Vue 本身具备响应式的状态,组件间也可以通过 props/emit 等方式进行通信,但是,当页面达到一定的复杂程度,多个视图共享同一状态,且当多个视图发出的行为需要变更状态时,单向数据流的简洁性就变得容易被破坏,难以维护。

Vuex 是专门为 Vue 应用程序开发的状态管理模式,是 Flux 架构的一种实现,将状态抽离出来作为 Store 层,当 View 层发起修改,会要求 Store 进行变更,变更之后再通知 View 更新视图,这样组件就可以从繁琐的组件通信解脱出来,只需关注视图层的逻辑。

工作原理

为什么 vuex 只能用于 vue,因为 vuex 本质是一个 vue 插件,依赖于 vue 的响应式系统,使用 vuex 的第一步是通过 Vue.use(Vuex) 应用 vuex,查看源码,有对外暴露 install 方法

export function install (_Vue) {
  if (Vue && _Vue === Vue) return
  Vue = _Vue
  applyMixin(Vue)
}
复制代码

当应用 vuex 时,会调用 applyMixin 方法,下面是核心代码,通过 Vue.mixin 注册一个全局 mixin,在所有组件的 beforeCreate 阶段,都会调用 vuexInit,向组件实例注入 store

new Vue 需要传入 store 实例,根实例执行 vuexInit 时,可以从 options 获取到 store,然后保存在 $store 属性,由于父组件先于子组件执行 beforeCreate,因此子组件执行 vuexInit 时总能够获取到父组件的 $store,然后保存起来,这也是为什么我们可以在任意组件直接通过 this.$store 获取到 store 实例。

export default function (Vue) {
  Vue.mixin({ beforeCreate: vuexInit })

  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
    }
  }
}
复制代码

核心原理

store 实例通过 new Vuex.Store 创建,由此可见 vuex 对外暴露一个 Store 类,下面来看核心模块的实现原理

State

在 vue 中,我们通过 store.state 获取 state,从下面代码可知,store.state 实际上是 this._vm._data.$$state 的代理

export class Store {
  constructor (options = {}) {
    this._modules = new ModuleCollection(options)
    const state = this._modules.root.state
    resetStoreVM(this, state)
  }
  get state () {
    return this._vm._data.$$state
  }
  set state (v) {
    if (__DEV__) {
      assert(false, `use store.replaceState() to explicit replace store state.`)
    }
  }
}
复制代码

创建 Store 实例会调用 resetStoreVM(this, state),下面是核心代码,可以看到 store 内部创建了一个 vue 实例,由此可见,store 内部新建了一个没有 template 的 vue 实例,而 state 就是该实例的响应式属性 $state,对 state 的修改,实际上就跟在普通组件中修改状态没有区别。

function resetStoreVM (store, state, hot) {
  store.getters = {}
  const wrappedGetters = store._wrappedGetters
  const computed = {}
  forEachValue(wrappedGetters, (fn, key) => {
    computed[key] = partial(fn, store)
    Object.defineProperty(store.getters, key, {
      get: () => store._vm[key],
      enumerable: true // for local getters
    })
  })
  store._vm = new Vue({
    data: {
      $$state: state
    },
    computed
  })
}
复制代码

Getters

由上面代码可知,getters 实际上是 computed

小结

Vuex 是一个 vue 插件,非常轻量,这得益于 vue 的响应式系统,Store 内部维护了一个 Vue 实例对象,state/getters 本质是 Vue 实例的 data 和 computed,

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享