我看Vuex(一)

2,117 阅读3分钟

之前阅读过 redux 的源码,vuex 同样也是一个状态管理工具,之前开发 vue 应用的时候,用到的 vuex 也比较多,所以自然对 vuex 颇有兴趣,最主要的原因是 我爱学习,(不好意思,脸掉地上了),,哈哈哈哈哈哈

images

(其实过年这几天也挺无聊的。。。

接下来我们来回归正题

default

Vuex 是如何引入到 Vue 中的

我们都知道,在 Vue 中使用 Vuex 必须通过 Vue.use(Vuex) 方法来使用,所以先来瞄一瞄 Vue 对 Vuex 做了什么不可告人的事,惊恐.gif

来到 vue 源码下的 core/global-api/use.js

Vue.use = function (plugin: Function | Object) {
        // 检测该插件是否已经被安装
        if (plugin.installed) {
            return
        }
        // use 支持传入一个选项对象,如
        // Vue.use(MyPlugin, { someOption: true })
        const args = toArray(arguments, 1)  //获取可选对象
        
        args.unshift(this)  // 将Vue 构造器传入,当插件的第一个参数
        if (typeof plugin.install === 'function') {
            // install执行插件安装
            plugin.install.apply(plugin, args)
        } else if (typeof plugin === 'function') {
            plugin.apply(null, args)
        }
        plugin.installed = true  // 防止再次安装
        return this
    }

所以,vue 对插件还是挺温柔的,只用了它的一个安装函数进行插件的安装。好,我们来看看 vuex 的 install 函数 在 store.js 的最下面,我们看到了它的身影

function install(_Vue) {
  if (Vue && _Vue === Vue) {
    // 给出警告:说明重复安装
    return
  }
  Vue = _Vue
  applyMixin(Vue)
}

这里我们也可以看到 vuex 自己内部也做了一个限制,防止重复安装,最后该函数调用了 applyMixin(Vue)

咻一下,我们来到 mixin.js,该函数也很简单,先是获取 Vue 的版本号,如果版本是 2.0 以上版本,就使用 Vue.mixin 来在所有组件中混入 beforeCreate 生命钩子,对应的处理函数是 vuexInit,反之就向后兼容 1.x 版本

function vuexInit() {
        // this -> vm
        const options = this.$options
        // store injection
        if (options.store) { // 获取传入new Vue({store}) 里面的 store,并注册为 vm 的 $store 属性,这样我们就能在实例中使用 this.$store 访问 store 对象了
            this.$store = typeof options.store === 'function' ?
                options.store() :
                options.store
        } else if (options.parent && options.parent.$store) {
            // 子组件从其父组件引用 $store 属性
            this.$store = options.parent.$store
        }
    }

至此,前菜已经上齐了

主角 Store 闪亮登场

835ab1e97a

store.js,别看长长一窜,有500多行,其实看进去了,你会感觉,也没啥可怕嘛 该文件主要由一个 Store 类和一些辅助函数构成,我们先来看 这个构造类

该构造函数中首先是进行一些必要的判断,如浏览器环境下自动安装、是否已经安装了 Vue、是否支持 Promise,是否已经实例化了 Store 构造函数,其中用到了 assert 断言函数

// 构造函数
    constructor(options = {}) {
        // .......
    }

// util.js
   function assert (condition, msg) {
    if (!condition) throw new Error(`[vuex] ${msg}`)
  }

然后是从传入的options 中提取出 pluginsstrict,并做一些变量的初始化

// store internal state
    // 表示提交状态,作用是保证对 Vuex 中 state 的修改只能在 mutation 的回调函数中,而不能在外部随意修改state
    this._committing = false
    // 用户定义的 actions
    this._actions = Object.create(null)
    // action 订阅者
    this._actionSubscribers = []
    // // 用户定义的 mutations
    this._mutations = Object.create(null)
    // 用户定义的 getters
    this._wrappedGetters = Object.create(null)
    // 收集用户定义的 modules
    this._modules = new ModuleCollection(options)
    // 模块命名空间map
    this._modulesNamespaceMap = Object.create(null)
    // 存储所有对 mutation 变化的订阅者,当执行commit时会执行队列中的函数
    this._subscribers = []
    // 创建一个 Vue 实例, 利用 $watch 监测 store 数据的变化
    this._watcherVM = new Vue()

接着后面是对 dispatch 和 commit 函数中的 this 的重新绑定

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)
    }

这样做是因为在组件中通过 this.$store 直接调用 dispatch/commit 方法时, 能够使 dispatch/commit 方法中的 this 指向当前的 store 对象而不是当前组件的 this。
我们知道 new Vue 时会把传入的对象的中的 this 绑定为 vm,例如 computed 属性,里面我们写如下代码时,会把计算属性里面的 this 绑定为当前的 vm 实例

 computed: {
    // 计算属性的 getter
    reversedMessage: function () {
      // `this` 指向 vm 实例
      return this.message.split('').reverse().join('')
    }
  }

( 上面这段如有不妥,欢迎指出,谢谢 ^_^

接着就是 安装模块,vm 组件设置,传入插件以及 devtoolPlugin 插件的设置了

    this.strict = strict

    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 vm, which is responsible for the reactivity
    // (also registers _wrappedGetters as computed properties)
    resetStoreVM(this, state)

    // apply plugins
    plugins.forEach(plugin => plugin(this))

    if (Vue.config.devtools) {
      devtoolPlugin(this)
    }

接下来我们先不讲 dispatchcommit 是怎么实现的,先来重点关注下 modules 这部分,毕竟前面已经 new ModuleCollection(options),先来后到嘛,,哈哈

由于现在的篇幅也算可以了,怕大家看到长篇大论头疼,所以我们转移阵地。

1

(如有不当,欢迎指出,谢谢 ^_^

原文地址: 我看Vuex(一)