Vuex 是什么?

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

阅读 vuex 源码的思维导图

前言 - 图1

vuex 的文档 对辅助看源码有不小的帮助,不妨在看源码之前仔细地撸一遍文档。

带着问题去看源码

  • global event bus 有何缺陷
  • $store 如何注入到所有子组件
  • mapState 实现
  • mapGetter 如何映射
  • Mutation 同步 && Action 异步
  • dispatch 方法实现
  • module 分割实现 && 局部状态 namespaced
  • 如何调用 vue-devtools
  • 内置 logger 插件实现
  • hotUpdate
  • 时间穿梭功能实现

目录

  1. ├── src
  2. ├── helpers.js 辅助函数
  3. ├── index.esm.js
  4. ├── index.js 入口
  5. ├── mixin.js 混入 vuexInit
  6. ├── module class module
  7. ├── module-collection.js
  8. └── module.js
  9. ├── plugins 插件
  10. ├── devtool.js
  11. └── logger.js
  12. ├── store.js store install
  13. └── util.js 工具函数

入口文件

vuex 的入口文件在 src/index.js

  1. import { Store, install } from './store'
  2. import {
  3. mapState,
  4. mapMutations,
  5. mapGetters,
  6. mapActions,
  7. createNamespacedHelpers
  8. } from './helpers'
  9. export default {
  10. Store,
  11. install,
  12. version: '__VERSION__',
  13. mapState,
  14. mapMutations,
  15. mapGetters,
  16. mapActions,
  17. createNamespacedHelpers
  18. }

index.js 引入了 Store 、install 和一些辅助工具函数,并将引入的变量组装成一个对象向外暴露。
当我们在项目中引入 import Vuex from ‘vuex’ 的之后, Vuex 就是这个组装后默认导出的对象了。
当然我们也可以通过解构的方式:

  1. import { Store, install } from 'vuex'

install 方法

来看一下 install 方法,在 src/store.js 。

  1. export function install(_Vue) {
  2. if (Vue && _Vue === Vue) {
  3. if (process.env.NODE_ENV !== 'production') {
  4. console.error(
  5. '[vuex] already installed. Vue.use(Vuex) should be called only once.'
  6. )
  7. }
  8. return
  9. }
  10. Vue = _Vue
  11. // vuexInit
  12. applyMixin(Vue)
  13. }

install 方法首先判断变量 Vue (store.js 顶部申明的变量) 是否与传入 _Vue 全等,如果全等并且在非生产环境,抛出异常。
随后将传入的 _Vue 赋值给 Vue,这里主要是为了避免重复安装。
然后调用引入的 applyMixin 方法,并将 Vue 作为参数传入。
applyMixin 在 src/mixin.js 作为默认方法导出:

  1. export default function(Vue) {
  2. const version = Number(Vue.version.split('.')[0])
  3. if (version >= 2) {
  4. Vue.mixin({ beforeCreate: vuexInit })
  5. } else {
  6. // override init and inject vuex init procedure
  7. // for 1.x backwards compatibility.
  8. const _init = Vue.prototype._init
  9. Vue.prototype._init = function(options = {}) {
  10. options.init = options.init ? [vuexInit].concat(options.init) : vuexInit
  11. _init.call(this, options)
  12. }
  13. }
  14. /**
  15. * Vuex init hook, injected into each instances init hooks list.
  16. */
  17. function vuexInit() {
  18. const options = this.$options
  19. if (options.store) {
  20. this.$store =
  21. typeof options.store === 'function' ? options.store() : options.store
  22. } else if (options.parent && options.parent.$store) {
  23. this.$store = options.parent.$store
  24. }
  25. }
  26. }

在这个默认导出的函数中,首先会取出传入参数 Vue 的 静态属性 version,根据 version 版本做不同的处理。

2.0 采用 mixin 将 vuexInit 合并到 beforeCreate 生命周期钩子。
1.0 重写 _init 方法 将 vuexInit 合并到 _init 方法中。

2 中处理都调用了 vuexInit,我们来看看 vuexInit:

  1. /**
  2. * Vuex init hook, injected into each instances init hooks list.
  3. */
  4. function vuexInit() {
  5. const options = this.$options
  6. if (options.store) {
  7. this.$store =
  8. typeof options.store === 'function' ? options.store() : options.store
  9. } else if (options.parent && options.parent.$store) {
  10. this.$store = options.parent.$store
  11. }
  12. }

在 vuexInit 方法中,首先判断 options.store,如果为真说明是 root 节点,并且一个三元表达式赋值给 this.$store,如果 store 是 function 就执行将函数返回值赋值给 this.$store ,否则将 options.store 直接赋值。
接着进入 else if,判断如果有父节点,并且父节点有 $store, 就将父节点的 $store 赋值给 this.$store ,所有的 this.$store 指向了同一个对象,这样就保证只有一个全局的 $store 变量。