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 源码解析,内容不多,热个身先。