You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
const store = new Vuex.Store({
state,
getters,
mutations,
actions,
modules: {
...
},
});
new Vue({
store
...
})
接下来 Store 的构造函数
vuex 先对构造 store 需要的一些环境变量进行断言:
if (!Vue && typeof window !== 'undefined' && window.Vue) {
// Vue 是全局变量时,自动 install
install(window.Vue)
}
if (process.env.NODE_ENV !== 'production') {
// 在非生产环境下,进行一些断言
// 当不满足参数1为 false 时抛出错误
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.`)
}
2.初始化变量
// store internal state
// 是否在进行提交状态标识
this._committing = false
this._actions = Object.create(null)
// 监听所有的action
this._actionSubscribers = []
this._mutations = Object.create(null)
this._wrappedGetters = Object.create(null)
this._modules = new ModuleCollection(options)
this._modulesNamespaceMap = Object.create(null)
// 监听所有的mutation
this._subscribers = []
//创建一个 Vue 实例, 利用 $watch 监测 store 数据的变化
this._watcherVM = new Vue()
重点看下this._modules = new ModuleCollection(options) 收集 modules 会调用 ModuleCollection,optinons 为 Store 构造函数传入的参数。从函数命名可以看出是模块收集注册的。
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) || {}
}
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
}
}
}
把 Store 类的 dispatch 和 commit 的方法的 this 指针指向当前 store 的实例上. 这样做的目的可以保证当我们在组件中通过 this.$store 直接调用 dispatch/commit 方法时, 能够使 dispatch/commit 方法中的 this 指向当前的 store 对象而不是当前组件的 this.
// 绑定 this 到 store
const store = this
const { dispatch, commit } = this
// 确保 dispatch/commit 方法中的 this 对象正确指向 store
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)
}
dispatch 的功能是触发并传递一些参数(payload)给与 type 对应的 action
commit 的功能是触发并传递一些参数(payload)给与 type 对应的 mutation
store其他重要属性的配置
// 确保 dispatch/commit 方法中的 this 对象正确指向 store
this.strict = strict
// 根 module 的 state
const state = this._modules.root.state
// init root module.
// this also recursively registers all sub-modules
// and collects all module getters inside this._wrappedGetters
installModule(this, state, [], this._modules.root)
// initialize the store vm, which is responsible for the reactivity
// (also registers _wrappedGetters as computed properties)
resetStoreVM(this, state)
// apply plugins
plugins.forEach(plugin => plugin(this))
const useDevtools = options.devtools !== undefined ? options.devtools : Vue.config.devtools
if (useDevtools) {
devtoolPlugin(this)
}
// installModule 方法则会将处理过的 modules 进行注册和安装,
/// installModule 接收5个参数: store、rootState、path、module、hot.
// store 表示当前 Store 实例, rootState 表示根 state, path 表示当前嵌套模块的路径数组
// module 表示当前安装的模块,
// hot 当动态改变 modules 或者热更新的时候为 true
function installModule (store, rootState, path, module, hot) {
const isRoot = !path.length
// 根据 path 或者路径上各层模块的 namespace
const namespace = store._modules.getNamespace(path)
// 根据 path 数组 寻找路径上的模块,把各模块的 namespaced 拼接起来
// register in namespace map
// 在 store 上的 _modulesNamespaceMap 注册有 namesoaced 的模块
// 方便后面根据 namespace 属性 直接获取模块
if (module.namespaced) {
store._modulesNamespaceMap[namespace] = module
}
// set state
// 当 !isRoot为 true 说明 path.length > 0
// 当 !hot 为 true,说明 hot 为 false
if (!isRoot && !hot) {
// 根据path 找到父级模块
// 可知,在父 module 的 state 中通过 path 路径名注册子 state
const parentState = getNestedState(rootState, path.slice(0, -1))
const moduleName = path[path.length - 1]
store._withCommit(() => {
Vue.set(parentState, moduleName, module.state)
})
}
// 给模块绑定上下文在 mapActions 等辅助函数时有用
// 该方法其实是在重写 dispatch 和 commit 函数
// 你是否有疑问模块中的 dispatch 和 commit
// 是如何找到对应模块中的函数的
// 假如模块 A 中有一个名为 add 的 mutation
// 通过 makeLocalContext 函数,会将 add 变成
// a/add,这样就可以找到模块 A 中对应函数了
const local = module.context = makeLocalContext(store, namespace, path)
//给模块上下文绑定处理过 type 的 dispatch,mutation,getters,
module.forEachMutation((mutation, key) => {
const namespacedType = namespace + key
//向 store._mutations[type] 数组添加处理函数
// local 传递,保证 mutation 的第一个参数是模块内部的state === local.state
registerMutation(store, namespacedType, mutation, local) // store._mutations[type] type为namespacedType
})
module.forEachAction((action, key) => {
const type = action.root ? key : namespace + key
const handler = action.handler || action
// local 传递 local.dispatch,local.commit,local.getters,local.state
// type 作用只为找到 store._actions[type] 数组 添加处理函数
registerAction(store, type, handler, local)
// store._actions[type] type为namespacedType
})
module.forEachGetter((getter, key) => {
const namespacedType = namespace + key
registerGetter(store, namespacedType, getter, local)
// 在 store._wrappedGetters[type] (type为namespacedType)
// 向 module 内部的 getter 传递 local.state,local.getters, root state, root getters,
})
// 循环注册子模块
module.forEachChild((child, key) => {
installModule(store, rootState, path.concat(key), child, hot)
})
}
简单提下如何模块的context是怎么处理的吧
function makeLocalContext (store, namespace, path) {
// 是否存在命名空间
const noNamespace = namespace === ''
const local = {
// 根 module 与子 module 都是调用 store.dispatch
// 子 module 的 type 会经过命名空间处理
dispatch: noNamespace ? store.dispatch : (_type, _payload, _options) => {
const args = unifyObjectStyle(_type, _payload, _options)
const { payload, options } = args
let { type } = args
if (!options || !options.root) {
type = namespace + type
// 非生产环境且 actions[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)
},
// 根 module 与子 module 都是调用store.commit
// 子module的type会经过命名空间处理
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)
}
}
// getters and state object must be gotten lazily
// because they will be changed by vm update
Object.defineProperties(local, {
getters: {
get: noNamespace
? () => store.getters
: () => makeLocalGetters(store, namespace) //返回一个新的对象,属性是 localType,value 是 store.getters[namespacedType]
},
state: {
get: () => getNestedState(store.state, path)
// 根据 path 数据返回模块内部的 state
// 在根 state 上一层层取值获取
}
})
return local
}
function resetStoreVM (store, state, hot) {
const oldVm = store._vm
// bind store public getters
//定义 getters 属性
store.getters = {}
// 获取处理的 getters 函数集合
const wrappedGetters = store._wrappedGetters
const computed = {}
// 循环所有处理过的 getters,
// 并新建 computed 对象进行存储 getter 函数执行的结果,
// 然后通过 Object.defineProperty 方法为 getters 对象建立属性
// 使得我们通过 this.$store.getters.xxxgetter 能够访问到 store._vm[xxxgetters]
forEachValue(wrappedGetters, (fn, key) => {
// use computed to leverage its lazy-caching mechanism
// direct inline function use will lead to closure preserving oldVm.
// using partial to return function with only arguments preserved in closure enviroment.
computed[key] = partial(fn, store)
// 等价于 computed[key] = () => fn(store)
Object.defineProperty(store.getters, key, {
get: () => store._vm[key],
enumerable: true // for local getters
})
})
// use a Vue instance to store the state tree
// suppress warnings just in case the user has added
// some funky global mixins
const silent = Vue.config.silent // 先暂存用户配置
Vue.config.silent = true // 取消Vue的所有日志与警告
// 设置新的 vm, 传入 state
// 把 computed 对象作为 _vm 的 computed 属性, 这样就完成了 getters 的注册
store._vm = new Vue({
data: {
$$state: state
},
computed
})
Vue.config.silent = silent // 恢复用户配置
// enable strict mode for new vm
if (store.strict) {
// 严格模式下, 在mutation之外的地方修改 state 会报错
enableStrictMode(store)
}
// 销毁旧的 vm 实例
if (oldVm) {
if (hot) {
// dispatch changes in all subscribed watchers
// to force getter re-evaluation for hot reloading.
store._withCommit(() => {
oldVm._data.$$state = null
})
}
Vue.nextTick(() => oldVm.$destroy())
}
}
上文提到严格模式,是如何控制严格模式的呢
function enableStrictMode (store) {
store._vm.$watch(function () { return this._data.$$state }, () => {
if (process.env.NODE_ENV !== 'production') {
assert(store._committing, `do not mutate vuex store state outside mutation handlers.`)
}
}, { deep: true, sync: true })
// 利用 store 上的 _vm 属性指向的 vue 实例的 watch 时刻观察 store.state 变化。
// 回调函数为断言 store._committing 是否为 true,为 true 说明是 mutation
}
Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。
在 Vue 项目中引入 Vuex,需要采用插件引入方式。
Vue.use(Vuex)
安装 Vuex 插件,会调用插件中提供的 install 方法
applyMixin 方法的主要功能将初始化 Vue 实例时传入的 store 设置到 this 对象的 $store 属性上, 子组件则从其父组件引用$store 属性, 层层嵌套进行设置. 这样, 任何一个组件都能通过 this.$store 的方式访问 store 对象了.
在 Vue 项目中,new 一个 Store 实例,并在创建Vue根实例时传入 store 实例
接下来 Store 的构造函数
2.初始化变量
重点看下
this._modules = new ModuleCollection(options)
收集 modules 会调用 ModuleCollection,optinons 为 Store 构造函数传入的参数。从函数命名可以看出是模块收集注册的。ModuleCollection 主要将传入的 options 对象整个构造为一个 module 对象, 并循环调用 register 为其中的 modules 属性进行模块注册, 使其都成为 module 对象, 最后 options 对象被构造成一个完整的组件树 。ModuleCollection
new Module(rawModule, runtime)
来创建具体的 module。简单提下如何模块的context是怎么处理的吧
resetStoreVM(this, state)
,原注释是初始化store vm,利用Vue的数据响应系统来监听内部变化,同时让store.__wrappedGetters变成绑定成vm的计算属性,响应变化。上文提到严格模式,是如何控制严格模式的呢
其实还有很多没提,比如 mapXXX 等辅助函数是如何实现,如果动态添加插件,这些大家有兴趣可以去探索呢
The text was updated successfully, but these errors were encountered: