vuex 源码学习记录

javascript/jquery

浏览数:194

2020-5-25

先看 vuex 源码目录

源码都在 src 目录下,入口文件为 index.js(或 index.esm.js),当用 import Vuex from 'vuex' 引入 vuex 时,入口就是 index.esm.js(esm: es6 module),看看里面

import { Store, install } from './store'
import { mapState, mapMutations, mapGetters, mapActions, createNamespacedHelpers } from './helpers'

export default {
  Store,
  install,
  version: '__VERSION__',
  mapState,
  mapMutations,
  mapGetters,
  mapActions,
  createNamespacedHelpers
}

export {
  Store,
  install,
  mapState,
  mapMutations,
  mapGetters,
  mapActions,
  createNamespacedHelpers
}

首先就是引入 Storeinstall,先看看 install 方法

export function install (_Vue) {
  // Vue 已经存在并且相等,说明已经 Vuex 已经 install 过
  if (Vue && _Vue === Vue) {
    // 非生产环境报错,vuex已经安装,代码继续执行
    if (process.env.NODE_ENV !== 'production') {
      console.error(
        '[vuex] already installed. Vue.use(Vuex) should be called only once.'
      )
    }
    return
  }
  Vue = _Vue
  applyMixin(Vue)
}

上述代码逻辑很简单,执行 Vuex.install 时传入 Vue 构造函数,最后再执行 applyMixin(Vue)

但是,通常情况下,我们引入 Vuex 之后,都是执行 Vue.use(vuex)

import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex)

看看 vue 源码

// src/core/global-api/use.js
/* @flow */
import { toArray } from '../util/index'
export function initUse (Vue: GlobalAPI) {
  Vue.use = function (plugin: Function | Object) {
    const installedPlugins = (this._installedPlugins || (this._installedPlugins = []))
    if (installedPlugins.indexOf(plugin) > -1) {
      return this
    }

    // additional parameters
    const args = toArray(arguments, 1)
    args.unshift(this)
    if (typeof plugin.install === 'function') {
      plugin.install.apply(plugin, args)
    } else if (typeof plugin === 'function') {
      plugin.apply(null, args)
    }
    installedPlugins.push(plugin)
    return this
  }
}

如果传进来的 plugininstall 方法,就执行 install 方法,当然我们也可以直击直接 install 方法,效果是一样的

import Vue from 'vue'
import Vuex from 'vuex'

Vuex.install(Vue)

再来看看 applyMixin 方法,这个方法在 mixin.js

// vuex/src/minx.js
export default function (Vue) {
  const version = Number(Vue.version.split('.')[0]) // 2

  if (version >= 2) {
    // 全局混入了一个 beforeCreate 钩子函数
    Vue.mixin({ beforeCreate: vuexInit })
  } else {
    // override init and inject vuex init procedure
    // for 1.x backwards compatibility.
    const _init = Vue.prototype._init
    Vue.prototype._init = function (options = {}) {
      options.init = options.init
        ? [vuexInit].concat(options.init)
        : vuexInit
      _init.call(this, options)
    }
  }

  /**
   * Vuex init hook, injected into each instances init hooks list.
   */

  function vuexInit () {
    const options = this.$options
    // store 注入
    // 使得每个Vue实例下 都有 $store 这个对象(Store 实例,包含一系列方法和属性),且是同一个对象。
    // 先是判断 options.store 也就是 这个
    // 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
    }
  }
}

看看 Vue 的源码中 Vue.mixin 方法

// src/core/global-api/mixin.js
/* @flow */
import { mergeOptions } from '../util/index'
export function initMixin (Vue: GlobalAPI) {
  Vue.mixin = function (mixin: Object) {
    this.options = mergeOptions(this.options, mixin)
    return this
  }
}

其实就是全局混入了一个 beforeCreate 钩子函数,打印 Vue 实例看看

作者:trustbelieve