Vuex 源码解析系列(一)

Vuex 源码阅读系列会分为三篇,前两篇会主要解析 Vuex 初始化中做了什么,最后一篇主要解析各种常用 API。

因为涉及代码较多,更推荐在 PC 浏览。

Vuex 思想

在解读源码之前,先来简单了解下 Vuex 的思想。

Vuex 全局维护着一个对象,使用到了单例设计模式。在这个全局对象中,所有属性都是响应式的,任意属性进行了改变,都会造成使用到该属性的组件进行更新。并且只能通过 commit 的方式改变状态,实现了单向数据流模式。

Vuex 安装

在使用 Vuex 之前,我们都需要调用 Vue.use(Vuex) 。在调用 use 的过程中,Vue 会调用到 Vuex 的 install 函数

install 函数作用很简单

  • 确保 Vuex 只安装一次

  • 混入 beforeCreate 钩子函数,可以在组件中使用 this.$store

export function install (_Vue) {  
  // 确保 Vuex 只安装一次
  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)
}
// applyMixin
export default function (Vue) {  
  // 获得 Vue 版本号
  const version = Number(Vue.version.split('.')[0]) 
  // Vue 2.0 以上会混入 beforeCreate 函数
  if (version >= 2) {
    Vue.mixin({ beforeCreate: vuexInit })
  } else {    // ...}  
  // 作用很简单,就是能让我们在组件中
  // 使用到 this.$store
  function vuexInit () {    
    const options = this.$options    
    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
    }
  }
}

Vuex 初始化(一)

本小节内容主要解析如何初始化 this._modules

export class Store {
  constructor (options = {}) {    
    // 引入 Vue 的方式,自动安装
    if (!Vue && typeof window !== 'undefined' && window.Vue) {
      install(window.Vue)
    }    
    // 在开发环境中断言
    if (process.env.NODE_ENV !== 'production') {
      assert(Vue, `must call Vue.use(Vuex) before creating a store instance.`)
      assert(typeof Promise !== 'undefined', `vuex requires a Promise polyfill in this browser.`)
      assert(this instanceof Store, `store must be called with the new operator.`)
    }    
    // 获取 options 中的属性
    const { 
      plugins = [],
      strict = false
    } = options    
    // store 内部的状态,重点关注 this._modules
    this._committing = false
    this._actions = Object.create(null)    
    this._actionSubscribers = []    
    this._mutations = Object.create(null)  
    this._wrappedGetters = Object.create(null)    
    this._modules = new ModuleCollection(options)    
    this._modulesNamespaceMap = Object.create(null)    
    this._subscribers = []    
    this._watcherVM = new Vue()    
    const store = this
    const { dispatch, commit } = this
    // bind 以下两个函数上 this 上
    // 便于 this.$store.dispatch
    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._modules 的过程,以 以下代码为例

const moduleA = {
  state: { ... },
  mutations: { ... },
  actions: { ... },
  getters: { ... }
}
const moduleB = {
  state: { ... },
  mutations: { ... },
  actions: { ... }
}
const store = new Vuex.Store({
  state: { ... },
  modules: {
    a: moduleA,
    b: moduleB
  }
})

对于以上代码,store 可以看成 root 。在第一次执行时,会初始化一个 rootModule,然后判断 root 中是否存在 modules 属性,然后递归注册 module 。对于 child 来说,会获取到他所属的 parent, 然后在 parent 中添加 module

export default class ModuleCollection {
  constructor (rawRootModule) {    
    // register root module (Vuex.Store options)
    this.register([], rawRootModule, false)
  }
  register (path, rawModule, runtime = true) {    
    // 开发环境断言
    if (process.env.NODE_ENV !== 'production') {
      assertRawModule(path, rawModule)
    }    
    // 初始化 Module
    const newModule = new Module(rawModule, runtime)    
    // 对于第一次初始化 ModuleCollection 时
    // 会走第一个 if 条件,因为当前是 root
    if (path.length === 0) {      
        this.root = newModule
    } else {      
      // 获取当前 Module 的 parent
      const parent = this.get(path.slice(0, -1))      
      // 添加 child,第一个参数是
      // 当前 Module 的 key 值
      parent.addChild(path[path.length - 1], newModule)
    }    
    // 递归注册
    if (rawModule.modules) {
      forEachValue(rawModule.modules, (rawChildModule, key) => {  
        this.register(path.concat(key), rawChildModule, runtime)
      })
    }
  }
}

export default class Module {
  constructor (rawModule, runtime) {    
    this.runtime = runtime    
    // 用于存储 children
    this._children = Object.create(null)    
    // 用于存储原始的 rawModule
    this._rawModule = rawModule    
    const rawState = rawModule.state    
    // 用于存储 state
    this.state = (typeof rawState === 'function' ? rawState() : rawState) || {}
  }
}

最后

以上是第一部分的 Vuex 源码解析,内容不多,热个身先。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值