store对象的创建
store的传递图
创建语句索引
- 创建vuex的语句为new Vuex.Store({…})
- Vuex的入口文件是index.js,store是index.js导出的store类
- store类是store.js文件中定义的。
Store的构造函数constructor
- 判断vuex是否被注入,就是将vue挂载在window对象上,自动检测window.Vue,如果有挂载,而且没有被注册过,则调用注册方法
if (!Vue && typeof window !== 'undefined' && window.Vue) {
install(window.Vue)
}
- 断言
断言是否被注册,是否支持promise,断言类有没有被正确的实例化,这些断言语句,在vue build出来以后,报错信息会被忽略
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.`)
}
- 支持promise语法
- 已经执行安装函数进行装载
- 在webpack配置的时候,npm build会将process.env.NODE_ENV 设置为true
function assert(condition, msg) {
if(!condition) throw new Error(`[vuex] ${msg}`)
}
- 部分重要的容器
this._committing = false // 表示状态标识,在严格模式下,防止非commit操作下,state被修改
this._actions = Object.create(null) // action函数的数组的对象,保存所有action回调函数。从null中创建对象,object.create(null)没有继承任何原型方法,也就是说他的原型链没有上一层,从而定义纯粹的对象
this._actionSubscribers = [] // 订阅action操作的函数数组,里面的每个函数,将在action函数被调用之间被调用,该功能类似于插件,和主功能无关。
this._mutations = Object.create(null) // mutation函数的数组的对象,保存所有的mutations回调函数
this._wrappedGetters = Object.create(null) //保存getter函数的函数数组对象容器
this._modules = new ModuleCollection(options) // 解析并生成模块树,通过树结构,保存配置文件内容
this._modulesNamespaceMap = Object.create(null) // 保存命名空间的模块对象,以便在辅助函数createNamespacedHelpers中快速定位到待命名空间的模块。
this._subscribers = [] // 订阅mutation操作的函数数组,里面的每个函数,将在commit执行后被调用,类似于插件,和主功能无关。
this._watcherVM = new Vue() // 定义一个vue对象,vue类将在调用vuex安装函数,install 的时候,被传递进来。
简洁版
this._committing = false // 是否在进行提交状态标识
this._actions = Object.create(null) // acitons操作对象
this._mutations = Object.create(null) // mutations操作对象
this._wrappedGetters = Object.create(null) // 封装后的getters集合对象
this._modules = new ModuleCollection(options) // Vuex支持store分模块传入,存储分析后的modules
this._modulesNamespaceMap = Object.create(null) // 模块命名空间map
this._subscribers = [] // 订阅函数集合,Vuex提供了subscribe功能
this._watcherVM = new Vue() // Vue组件用于watch监视变化
ModuleCollection
this._modules = new ModuleCollection(options)
ModuleCollection函数主要是讲传入的options对象构造为一个module对象,并循环调用(this.register[key], rawModul, false)为其中的modules属性进行模块注册,使得其称为module对象后,最后options对象被构成一个完整的组件树,ModuleCollection类还提供了modules的更替功能。
constructor
export default class ModuleCollection {
constructor(rawRootModule) {
this.register([], rawRootModule, false)
}
}
- 形参: rawRootModule,在store中new ModuleCollection(options)传入,而options是store的构造函数参数,就是new Vuex.Store({…})传入的参数
{
state,
getters,
actions,
mutations
}
register
register (path, rawModule, runtime = true) {
if (process.env.NODE_ENV !== 'production') {
assertRawModule(path, rawModule)
}
const newModule = new Module(rawModule, runtime)
if (path.length === 0) {
this.root = newModule
} else {
const parent = this.get(path.slice(0, -1))
parent.addChild(path[path.length - 1], newModule)
}
if (rawModule.modules) {
forEachValue(rawModule.modules, (rawChildModule, key) => {
this.register(path.concat(key), rawChildModule, runtime)
})
}
}
参数解析
- path是注册模块的层级关系数组,保存当前模块及各个祖先模块的名字。[a, b, c, 当前模块] =>
{modules: {a, modules: {b, module:{当前模块} }}}
- rawModule是模块的配置参数,在定义模块的时候,开发者定义的模块配置内容,比如state, getter, actions等等
- runtime标识是否是运行状态,在运行状态下,不能进行某些特定的操作。
assertRawModule
if (process.env.NODE_ENV !== 'production') {
assertRawModule(path, rawModule)
}
- 对模块的配置信息进行断言
- 判断了getters,mtations, actions等配置的数据类型是否正确。
const newModule = new Module(rawModule, runtime),找到module.js文件找到其构造函数
constructor (rawModule, runtime) {
this.runtime = runtime
this._children = Object.create(null) // 定义子模块对象容器
this._rawModule = rawModule // 保存配置参数本身
const rawState = rawModule.state // 从配置参数中获得了state这个参数
this.state = (typeof rawState === 'function' ? rawState() : rawState) || {} // 解析state,如果是函数,则运用工厂模式产生配置对象
}
根据path模块判断当前是否为根模块
- 如果是根模块,则通过this.root进行保存
- 如果不是根模块
- get方法传入的是路径数组,slice(0,-1)是为了去除本模块名,保留所有祖先模块的路径,从而获取夫模块。
- get函数内部,使用了数组的reduce方法
get (path) { return path.reduce((module, key) => { return module.getChild(key) }, this.root) }
getChild (key) { return this._children[key] } addChild (key, module) { this._children[key] = module }
if(rawModule.modules)判断模块配置信息中,有没有子模块的配置
如果有,则递归调用注册函数register本身,传入的参数加上了子模块名的path模块层级数组,rawchildmodule是子模块配置文件,runtime是继承而来的运行标识符
获取dispatch和commit函数,并复写该函数
const store = this
const { dispatch, commit } = this
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值绑定到vuex实例本身上,防止因为this的指向修改而被修改
这两个函数可以通过mapMutations
和mapActions
辅助函数转换为Vue中的普通函数,这时this指向Vue组件,而不是Vuex实例
后续代码
this.strict = strict
const state = this._modules.root.state //根据根模块的state变量索引
installModule(this, state, [], this._modules.root)//安装根模块,设置commit,dispatch函数的重载,根据是否设置命名空间,设置参数前缀,将getter,commit,action放到相对应的容器中保存起来。
resetStoreVM(this, state)// 借助vue的watch功能和computed功能,实现数据的响应式
plugins.forEach(plugin => plugin(this)) // 插件的注册,和主功能无关
const useDevtools = options.devtools !== undefined ? options.devtools : Vue.config.devtools
if (useDevtools) { // vue调试工具的处理
devtoolPlugin(this)
}
installModule(模块安装函数)
参数介绍
- store: store对象实例,new Vuex.store({…})生成的对象
- rootState: 根模块的state对象
- path: 当前模块所处的层级数组
- module: 模块对象
- hot
实例代码
function installModule (store, rootState, path, module, hot) {
const isRoot = !path.length // 判断当前模块是否为根模块
const namespace = store._modules.getNamespace(path) // 获取模块的命名空间 在module.js中有个函数
if (module.namespaced) {
store._modulesNamespaceMap[namespace] = module
}
if (!isRoot && !hot) { //非根目录
const parentState = getNestedState(rootState, path.slice(0, -1)) //寻找父结点的state,形成一个和module树对应的state树
const moduleName = path[path.length - 1]
store._withCommit(() => { //允许在函数执行的时候修改state的值
Vue.set(parentState, moduleName, module.state) //往父State对象中,添加以模块名为key,为模块内state对象为value的变量,借用Vue.set方法,实现数据响应
})
}
const local = module.context = makeLocalContext(store, namespace, path)
module.forEachMutation((mutation, key) => { //对模块的mutation进行注册
const namespacedType = namespace + key
registerMutation(store, namespacedType, mutation, local)
})
module.forEachAction((action, key) => { //对模块的action进行注册
const type = action.root ? key : namespace + key
const handler = action.handler || action
registerAction(store, type, handler, local)
})
module.forEachGetter((getter, key) => {
const namespacedType = namespace + key
registerGetter(store, namespacedType, getter, local)
})
module.forEachChild((child, key) => {
installModule(store, rootState, path.concat(key), child, hot)
})
}
// 获取子模块的state变量的时候,要通过this.$store.state.moduleName.xxx
补充
getNamespace
//从跟模块中出发,根据给出的路径数组,递归每一个层级的模块
getNamespace(path) {
let module = this.root
return path.reduce((namespace, key) => {
module = module.getChild(key)
return namespace + (module.namespaced ? key + "/" : '')
}, '')
}
{
moduleA:{
namespaced:true,
modules:{
moduleB:{
namespaced:false,
modules:{
moduleC:{
namespaced:true,
}
}
}
}
}
}
makeLocalContext创建模块内容
function makeLocalContext (store, namespace, path) {
const noNamespace = namespace === ''
const local = {
dispatch: noNamespace ? store.dispatch : (_type, _payload, _options) => { // 根据namespace进行判断,本模块或祖先模块中是否有命名空间,没有设置直接使用store中的dispatch函数。
const args = unifyObjectStyle(_type, _payload, _options) //判断参数是否为对象,如果是对象则解析,调整参数位置
const { payload, options } = args
let { type } = args
if (!options || !options.root) {
type = namespace + type
if (process.env.NODE_ENV !== 'production' && !store._actions[type]) {
console.error(`[vuex] unknown local action type: ${args.type}, global type: ${type}`)
return
}
}
return store.dispatch(type, payload) //不是发送全局的action函数,即只发送本模块内的action函数·调用store实例的dispatch函数
},
commit: noNamespace ? store.commit : (_type, _payload, _options) => {
const args = unifyObjectStyle(_type, _payload, _options)
const { payload, options } = args
let { type } = args
if (!options || !options.root) {
type = namespace + type
if (process.env.NODE_ENV !== 'production' && !store._mutations[type]) {
console.error(`[vuex] unknown local mutation type: ${args.type}, global type: ${type}`)
return
}
}
store.commit(type, payload, options)
}
}
Object.defineProperties(local, {
//设置拦截器,如果有namespace,就直接访问store对象实例中生成的getters变量,如果有就生成自己的getters
getters: {
get: noNamespace
? () => store.getters
: () => makeLocalGetters(store, namespace)
},
state: {
get: () => getNestedState(store.state, path) //state的获取,则不需要命名空间前缀的识别,而且直接通过getNestedState,从state树结构中取
}
})
return local
}
makeLocalGetters(store, namespace)
function makeLocalGetters (store, namespace) {
const gettersProxy = {}
const splitPos = namespace.length
Object.keys(store.getters).forEach(type => {
if (type.slice(0, splitPos) !== namespace) return //检测各个函数名,判断该函数名的前缀是否与模块的命名空间前缀相同。找到即符合命名前缀的函数。
const localType = type.slice(splitPos) //去除命名空间前缀,能获取纯粹的getter函数名
Object.defineProperty(gettersProxy, localType, {
get: () => store.getters[type],
enumerable: true
})
})
return gettersProxy //其实所有的getter都存放在了Store实例的_wrappedGetters变量中,如模块没有命名空间前缀,则直接存入。否则将getter函数名带上命名空间前缀后,再加入进去
}
state是独立的状态树,有明显的层级关系.getters则全部都放在store实例的getters变量中保存,属于同层级,只是通过函数前缀进行了区分(有设命名空间的话)