之前看vuex 的 3.x 版本源码,现在看下4.x版本源码有哪些不同?还是针对核心源码

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

  • 官网示例代码
import { createApp } from 'vue'
import { createStore } from 'vuex'

// 创建一个新的 store 实例
const store = createStore({
  state () {
    return {
      count: 0
    }
  },
  mutations: {
    increment (state) {
      state.count++
    }
  }
})

const app = createApp({ /* 根组件 */ })

// 将 store 实例作为插件安装
app.use(store)

createStore

export function createStore (options) {
  return new Store(options)
}

createStore函数很简单,直接返回一个Sotre示例。

Sotre 类

app.use会调用传入参数对象的install函数,所以先来分析install函数

install函数

export const storeKey = 'store'
install (app, injectKey) {
    // 设置sotre实例到应用范围中的所有组件
    app.provide(injectKey || storeKey, this)
    // 添加$store属性作为全局的property,相当于Vue.prototype.$store
    app.config.globalProperties.$store = this
    // 忽略
    const useDevtools = this._devtools !== undefined
    ? this._devtools
    : __DEV__ || __VUE_PROD_DEVTOOLS__

    if (useDevtools) {
        addDevtools(app, this)
    }
}

install函数首先使用Vue的依赖注入provide来挂载sotre实例,为什么要这么做?因为在setup方法中无法访问this

构造函数

export class Store {
   constructor (options = {}) {
      // 还是跟之前一样逐步分析      
   }
}
if (__DEV__) {
    assert(typeof Promise !== 'undefined', `vuex requires a Promise polyfill in this browser.`)
    assert(this instanceof Store, `store must be called with the new operator.`)
}

1.当前环境不支持Promise,报错:vuex 需要 Promise polyfill。

2.Store 函数必须使用 new 操作符调用。

const {
    plugins = [],
    strict = false,
    devtools
} = options

从定义的options取出plugins,strictdevtools.

// store internal state
this._committing = false
// 用来存放处理后的用户自定义的actoins
this._actions = Object.create(null)
// 用来存放 actions 订阅
this._actionSubscribers = []
// 用来存放处理后的用户自定义的mutations
this._mutations = Object.create(null)
// 用来存放处理后的用户自定义的 getters
this._wrappedGetters = Object.create(null)
// 模块收集器,构造模块树形结构
this._modules = new ModuleCollection(options)
// 用于存储模块命名空间的关系
this._modulesNamespaceMap = Object.create(null)
// 订阅
this._subscribers = []
// 用来存放生成的本地 getters 的缓存
this._makeLocalGettersCache = Object.create(null)
this._devtools = devtools

这一块跟vuex@3.x几乎一模一样。就删除了_watcherVM

// bind commit and dispatch to self
const store = this
const { dispatch, commit } = this
this.dispatch = function boundDispatch (type, payload) {
    return dispatch.call(store, type, payload)
}
this.commit = function boundCommit (type, payload, options) {
    return commit.call(store, type, payload, options)
}

dispatchcommit函数绑定thisstore实例。

// 严格模式,默认是false
this.strict = strict
// 根模块的state
const state = this._modules.root.state

// init root module.
// this also recursively registers all sub-modules
// and collects all module getters inside this._wrappedGetters
installModule(this, state, [], this._modules.root)

// initialize the store state, which is responsible for the reactivity
// (also registers _wrappedGetters as computed properties)
resetStoreState(this, state)

// apply plugins
// 插件:把实例对象 store 传给插件函数,执行所有插件。
plugins.forEach(plugin => plugin(this))

installModule函数,初始化根模块,并且递归遍历所有子模块,并且收集所有模块的gettter放置在_wrappedGetters中。

resetStoreState函数, 初始化 store.state 响应式,并且注册 _wrappedGetters 作为 它的computed`的属性。

installModule

这里跟vuex@3.x版本逻辑几乎一模一样,忽略。

resetStoreState(重要)

import { reactive, watch } from 'vue'
export function resetStoreState (store, state, hot) {
  // 旧的state
  const oldState = store._state

  // bind store public getters
  store.getters = {}
  // reset local getters cache
  store._makeLocalGettersCache = Object.create(null)
  const wrappedGetters = store._wrappedGetters
  const computedObj = {}
  forEachValue(wrappedGetters, (fn, key) => {
    // use computed to leverage its lazy-caching mechanism
    // direct inline function use will lead to closure preserving oldState.
    // using partial to return function with only arguments preserved in closure environment.
    // 没看懂,后续再分析如何使得getter也变成响应式
    computedObj[key] = partial(fn, store)
    Object.defineProperty(store.getters, key, {
      // TODO: use `computed` when it's possible. at the moment we can't due to
      // https://github.com/vuejs/vuex/pull/1883
      get: () => computedObj[key](),
      enumerable: true // for local getters
    })
  })
  // 建立响应式store (重点)
  store._state = reactive({
    data: state
  })

  // enable strict mode for new state
  if (store.strict) {
    enableStrictMode(store)
  }

  if (oldState) {
    if (hot) {
      // dispatch changes in all subscribed watchers
      // to force getter re-evaluation for hot reloading.
      store._withCommit(() => {
        oldState.data = null
      })
    }
  }
}

resetStoreState是利用vuereactiveAPI来使得store对象变成响应式。

总结

Vuex@3.xVuex@4.x的主要流程几乎差不多,但实现上也有不小的差异,比如使state变成响应式数据。


看见了
876 声望16 粉丝

前端开发,略懂后台;


« 上一篇
diff
下一篇 »
vue-router