Abiel1024 / blog

Abiel's blog
8 stars 1 forks source link

vuex源码分析一(文件结构与组件注入) #10

Open Abiel1024 opened 6 years ago

Abiel1024 commented 6 years ago

在项目中一直有用到vuex,觉得vuex是真的好用。十分好奇其中的原理是怎么样的,所以就拿源码,对应着网上的文章进行学习。对应的源码地址: vuex源码

源码分析技巧: 网上推荐的方法都是把源码拷到本地,然后就开始看源码,呃。。。。,反正我看了之后是一脸懵逼。可以将源码拷贝到自己的项目中,通过import的方式去引入他(通常我们通过依赖去引入)。这样的好处就是能在他的代码中修改。在控制台输出,打断点...,结合自己项目的使用情况,你就会发现好理解多了。不然光看代码,你很难分析他传经来的参数到底是什么?就会导致后面的分析都会异常的困难。

源码分析

目录结构

image src 目录下的文件并不多,module下主要是module的两个构造函数,plugins下面是两个vuex内置插件,还有对应的几个js,整个源码加起来1000行差不多吧(看去年的文章还只有500-600行),虽然量也不是特别大,但是想要完全看明白还是需要花一点时间的。

入口文件

看源码一般是从入口开始,因为我们用的时候也是会通过import的方式去引入。所以入口文件就相当于是桥梁,通过入口文件,去了解插件提供的内容。Vuex 源码的入口是 src/index.js。

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

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

这里可以一目了然地看到 Vuex 对外暴露的 API。其中, Store 是 Vuex 提供的状态存储类,通常我们使用 Vuex 就是通过创建 Store 的实例(核心部分);install是第三方 Vue 插件的调用方法,即在我们调用Vue.use()时,实际上就是调用了 install 的方法并传入 Vue 的引用;map*的方法是辅助的一些减少我们代码量的工具。

install

在项目中,引入vuex之后就会调用install 方法,然后再是new一个store实例。所以先介绍install方法,install是在store.js底部,先看代码:

export function install (_Vue) {
  if (Vue && _Vue === Vue) {
    if (process.env.NODE_ENV !== 'production') {
      console.error(
        '[vuex] already installed. Vue.use(Vuex) should be called only once.'
      )
    }
    return
  }
  Vue = _Vue
  applyMixin(Vue)
}

vue执行.use方法时,会传入对应的vue引用,在store.js头部,定义了一个局部变量Vue ,这里的判断主要是保证 install 方法只执行一次,如果重复执行就报错,如果第一次执行就把_Vue引用赋值给Vue,在store的其他地方也能够使用。最后执行了applyMixin方法。

applyMixin方法是在mixin.js中

export default function (Vue) {
  const version = Number(Vue.version.split('.')[0])

  if (version >= 2) {
    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 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 的实例注入一个 $store 的属性。当Vue版本大于2,调用mixin的方法,传进去vuexInit方法,通过父传子的方式,让所有的组件都注入$store。(版本小于2的现在应该都自动忽略了吧 哈哈哈) image 图片来自美团点评技术团队 这样我们在所有的组件中都能通过this.$store来访问到Vuex 的各种数据和状态。

把分析的文章分开,是想以后自己看的时候更清晰一点,如果是一篇文章的话,内容会很多,看起来会很乱。分开的话,一篇对应一部分,这样也可以循序渐进。这一篇相对来说比较简单,所以看起来也比较轻松。