恋の歌的logo
Auto
归档 标签

Vuex@next源码解析 - module篇

发表于2020-10-26 13:04
更新于2023-02-13 18:28
分类于编程
总字数1.2K
阅读时长 ≈5 分钟

前言 🔗

本篇主要写Vuex中模块最基本的的一个单位,Module – 模块对象

module.js 🔗

javascript
export default class Module {
  
  constructor (rawModule, runtime) {}

  get namespaced () {}

  addChild (key, module) {}
  removeChild (key) {}
  getChild (key) {}
  hasChild (key) {}
  
  update (rawModule) {}

  forEachChild (fn) {}
  forEachGetter (fn) {}
  forEachAction (fn) {}
  forEachMutation (fn) {}
}

不用因为这么多方法感到退却,因为其实这些方法都是一些非常简单的方法,放下心往👇看就完事~

  • constructor 构造器函数
  • addChild 添加一个子模块Module对象
  • removeChild 移除一个子模块Module对象
  • getChild 得到一个子模块Module对象
  • hasChild 是否拥有一个子模块Module对象
  • update 更新本模块
  • forEachChild 遍历每一个子模块
  • forEachGetter 遍历本模块每一个getter
  • forEachAction 遍历本模块每一个action
  • forEachMutation 遍历本模块每一个mutation

看起来很多,其实对于xxxChild这几个方法,颇有点增删改查的味道😂

后四个forEachXXX函数很明显为遍历函数

constructor 🔗

javascript
// Base data struct for store's module, package with some attribute and method
export default class Module {
  constructor (rawModule, runtime) {
    this.runtime = runtime
    // Store some children item
    this._children = Object.create(null)
    // Store the origin module object which passed by programmer
    this._rawModule = rawModule
    const rawState = rawModule.state

    // Store the origin module's state
    this.state = (typeof rawState === 'function' ? rawState() : rawState) || {}
  }
}

构造器函数表明了将会往对象上挂载什么属性

this.runtime 表示是否是运行时注册的模块,前面说到的注册根root模块时

ModuleCollection的构造器中执行了this.register([], rawRootModule, false)

这里第三个参数为false也就表明了根模块是不可以卸载的

this._children 本模块包含的子Module对象

this._rawModule 用于创建模块的参数(就是用于传进来的options),比如现在有如下store

javascript
const store = createStore({
  state: {
    val: 24
  },
  modules: {
    m1: {
      state: {
        val: 124
      }
    }
  }
})

那么对于根rootm1,他们的_rawModule,应该是这个传入的配置对象什么部分?

可能有人会认为都是整个配置对象,但其实不是,因为在ModuleCollection类中的register方法中

javascript
// register nested modules
if (rawModule.modules) {
  forEachValue(rawModule.modules, (rawChildModule, key) => {
    this.register(path.concat(key), rawChildModule, runtime)
  })
}

这里通过工具函数forEachValue来来遍历传入的配置参数的modules(如果存在的情况下),每次回调的rawChildModule为本模块的一个配置对象

比如此时遍历到了modules下的m1,此时两者分别为

javascript
rawChildModule = {
  state:{
    val: 124
  }
}

key = 'm1'

那么对于m1,他的_rawModulerawChildModule一样,就为

javascript
_rawModule = {
  state:{
    val: 124
  }
}

而对于根root模块,他的_rawModule,就是整个传进来的参数对象

javascript
_rawModule = {
  state: {
    val: 24
  },
  modules: {
    m1: {
      state: {
        val: 124
      }
    }
  }
}

我们可以在控制台展开看看

发现基本符合预期,但是根root模块的state是合并的,在这个文件中并没有相关的操作,哪又是在进行合并的呢?

没错,之前store篇说过,是store.js文件中的installModule方法

Store类的构造器函数中,调用了installModule,如下

javascript
const state = this._modules.root.state

installModule(this, state, [], this._modules.root)

然后在installModule中,有一段判断

javascript
// set state
if (!isRoot && !hot) {
  const parentState = getNestedState(rootState, path.slice(0, -1))
  const moduleName = path[path.length - 1]
  store._withCommit(() => {
    if (__DEV__) {
      if (moduleName in parentState) {
        console.warn(
          `[vuex] state field "${moduleName}" was overridden by a module with the same name at "${path.join('.')}"`
        )
      }
    }
    // 挂载state
    parentState[moduleName] = module.state
  })
}

此时的rootState就是this._modules.root.state

也就是非根模块的时候,通过getNestedState取到父模块,然后把当前模块的state挂载到父模块的state

this.state 也就是本模块的状态,通过_rawModule.state来获取

这里做了个判断,也就是我们传入的state属性不一定是要一个对象,也可以是一个函数返回一个对象

addChild 🔗

javascript
export default class Module {
  addChild (key, module) {
    this._children[key] = module
  }
}

非常简单,我觉得初学者都能看懂🤣,就是往_children属性上挂载上传入的Module对象而已

removeChild 🔗

javascript
export default class Module {
  removeChild (key) {
    delete this._children[key]
  }
}

通过delete删除对应keyModule对象

getChild 🔗

javascript
export default class Module {
  getChild (key) {
    return this._children[key]
  }
}

通过_children属性返回对应的Module对象

hasModule 🔗

javascript
export default class Module {
  hasChild (key) {
    return key in this._children
  }
}

通过in操作符号判断模块名key是否存在_children属性中

update 🔗

javascript
export default class Module {
  update (rawModule) {
    this._rawModule.namespaced = rawModule.namespaced
    if (rawModule.actions) {
      this._rawModule.actions = rawModule.actions
    }
    if (rawModule.mutations) {
      this._rawModule.mutations = rawModule.mutations
    }
    if (rawModule.getters) {
      this._rawModule.getters = rawModule.getters
    }
  }
}

通过传入的rawModule配置来更新本模块的_rawModule

可以看到,覆盖了namespacedgettersmutationsactions

但是没有覆盖state没有覆盖state!!没有覆盖state!!!

重要的话讲三遍好吧,这个API可以追溯到store.hotUpdate这个方法上,热更新时用到,可以不用太在意

forEachXXX 🔗

javascript
export default class Module {
  forEachChild (fn) {
    forEachValue(this._children, fn)
  }
  
  forEachGetter (fn) {
    if (this._rawModule.getters) {
      forEachValue(this._rawModule.getters, fn)
    }
  }

  forEachAction (fn) {
    if (this._rawModule.actions) {
      forEachValue(this._rawModule.actions, fn)
    }
  }

  forEachMutation (fn) {
    if (this._rawModule.mutations) {
      forEachValue(this._rawModule.mutations, fn)
    }
  }
}

这四个函数非常简单,依赖了工具函数forEachValue

每个函数对特定的对象进行属性以及对应值的遍历

后记 🔗

这个文件写完基本上核心代码就写完了

接下来会整体改进这几篇文章的细节,使大家更容易懂

#Vue
#Vuex
#JavaScript
哦呐该,如果没有评论的话,瓦达西...