Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

我看Vuex(一) #47

Open
jyzwf opened this issue Feb 21, 2018 · 0 comments
Open

我看Vuex(一) #47

jyzwf opened this issue Feb 21, 2018 · 0 comments

Comments

@jyzwf
Copy link
Owner

jyzwf commented Feb 21, 2018

之前阅读过 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 这部分

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

1

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

@jyzwf jyzwf changed the title 我看Vuex 我看Vuex(一) Feb 21, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant