Store实例方法
Vuex.Store实例为我们提供一些可以操作状态的方法,本篇文章就对这些方法逐个讲解。
# 访问器属性
get state() {
return this._vm._data.$$state
}
// 根state不能直接修改,需要使用replaceState方法来替换
set state(v) {
if (process.env.NODE_ENV !== 'production') {
assert(false, `Use store.replaceState() to explicit replace store state.`)
}
}
2
3
4
5
6
7
8
9
10
此段代码比较简单,当我们访问store.state时,实际上访问的是store._vm._data.$$state,其中$$state是根据被转化过的具有嵌套的结构的state对象。在Vuex中,根state不能直接修改,需要使用replaceState方法来替换。
# replaceState
方法声明: store.replaceState(state: Object)
方法定义:
replaceState(state) {
this._withCommit(() => {
this._vm._data.$$state = state
})
}
2
3
4
5
用于替换store的根状态,这个通常用于状态合并或时光旅行调试。
# commit
commit(_type, _payload, _options) {
// check object-style commit
// 统一调用风格
const {
type,
payload,
options
} = unifyObjectStyle(_type, _payload, _options)
const mutation = { type, payload }
const entry = this._mutations[type] // 获取对应的mutation处理函数
if (!entry) { // 未设置的mutation
if (process.env.NODE_ENV !== 'production') {
console.error(`[vuex] unknown mutation type: ${type}`)
}
return
}
// 设置_committing为true
this._withCommit(() => {
// 遍历所有的mutation执行
entry.forEach(function commitIterator(handler) {
handler(payload)
})
})
// 执行mutation订阅列表
this._subscribers.forEach(sub => sub(mutation, this.state))
// vue-devtools上silent选项废弃的提醒
if (
process.env.NODE_ENV !== 'production' &&
options && options.silent
) {
console.warn(
`[vuex] mutation type: ${type}. Silent option has been removed. ` +
'Use the filter functionality in the vue-devtools'
)
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
commit方法用于提交mutation。commit方法有两种调用方式
commit(type: string, payload?: any, options?: object)commit(mutation: Object, options?: object)
两种调用方式都会调用unifyObjectStyle方法,将参数统一为{type: string, payload: any, options: object}。然后根据type获取对应的mutation处理函数,执行以下操作:
- 如果函数不存在则输出警告信息
- 如果存在,则将
store._committing设置为true,遍历得到的mutation处理函数,传入payload执行(由上一篇文章分析,我们知道,mutation处理函数最终存放在一个数组里) - 遍历
mutation订阅列表(store._subscribers),传入当前的mutation信息(type和payload)以及改变后的state执行
unifyObjectStyle定义和说明如下:
function unifyObjectStyle(type, payload, options) {
// type是一个包含type属性的对象
if (isObject(type) && type.type) {
options = payload // 第二个参数作为options
payload = type // 第一个参数作为完整的payload
type = type.type // type.type做为type
}
// type只能是string类型
if (process.env.NODE_ENV !== 'production') {
assert(typeof type === 'string', `Expects string as the type, but found ${typeof type}.`)
}
// 返回规范化后的参数对象
return { type, payload, options }
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# dispatch
dispatch(_type, _payload) {
// check object-style dispatch
// 统一调用风格
const {
type,
payload
} = unifyObjectStyle(_type, _payload)
const action = { type, payload }
const entry = this._actions[type] // 获取action处理函数
if (!entry) { // 未定义的action
if (process.env.NODE_ENV !== 'production') {
console.error(`[vuex] unknown action type: ${type}`)
}
return
}
// 执行action订阅列表
this._actionSubscribers.forEach(sub => sub(action, this.state))
// 多个action处理函数返回一个Promise,否则返回处理结果(也可能是Promise)
return entry.length > 1
? Promise.all(entry.map(handler => handler(payload)))
: entry[0](payload)
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
dispatch用于分发action。dispatch方法同样有两种调用方式:
dispatch(type: string, payload?: any, options?: Object): Promise<any>dispatch(action: Object, options?: Object): Promise<any>
参数的处理方式与commit的处理方式一致,根据type获取对应的action处理函数后,执行以下操作:
- 如果函数不存在则输出警告信息
- 遍历
action订阅列表(store._actionSubscribers),传入当前的action信息(type和payload)以及改变后的state执行 - 以
payload为参数调用所有的action处理函数,如果处理函数的长度大于1则使用Promise.all组合所有的处理函数并返回,否则返回第一个处理函数的执行结果(也是Promise)
提示
在注册action时,所有的action已经被包装并转为了Promise,点击这里查看
# subscribe 和 subscribeAction
方法声明:
subscribe(handler: Function, options?: Object): Function subscribeAction(handler: Function, options?: Object): Function
subscribe和subscribeAction分别用于订阅mutation和action,通常是给Vuex插件使用的。
// 订阅mutation
subscribe(fn) {
return genericSubscribe(fn, this._subscribers)
}
// 订阅actions
subscribeAction(fn) {
return genericSubscribe(fn, this._actionSubscribers)
}
2
3
4
5
6
7
8
9
10
两个方法都调用了通用的订阅函数genericSubscribe,该函数定义如下:
// 通用的订阅函数
function genericSubscribe(fn, subs) {
if (subs.indexOf(fn) < 0) { // 同一个订阅函数只订阅一次
subs.push(fn)
}
// 返回一个取消订阅的新函数
return () => {
const i = subs.indexOf(fn)
if (i > -1) {
subs.splice(i, 1)
}
}
}
2
3
4
5
6
7
8
9
10
11
12
13
函数很简单,就是给定的fn不在订阅列表subs中,则添加到订阅列表中,也就是说,同一个订阅函数只订阅一次。最后返回一个取消订阅的新函数,该函数会从订阅列表subs中移除给定的fn。
所以两个订阅方法,最终就是分别将订阅函数添加到_subscribers和_actionSubscribers中,并返回一个取消订阅的函数。
# watch
方法声明: watch(fn: Function, callback: Function, options?: Object): Function
watch方法用于响应式地侦听fn的返回值,当值改变时调用回调函数。
watch(getter, cb, options) {
if (process.env.NODE_ENV !== 'production') { // 只接收函数
assert(typeof getter === 'function', `store.watch only accepts a function.`)
}
return this._watcherVM.$watch(() => getter(this.state, this.getters), cb, options)
}
2
3
4
5
6
watch函数的第一个参数getter仅接收函数类型并分别以store的state作为其第一个参数,getter作为第二个参数。options选项则为传递给Vue的vm.$watch方法的选项参数。watch执行后会返回一个可以取消监听的函数。
# _withCommit
_withCommit是一个私有方法,用于修改store._committing的值,从而按约束来修改state。该外部不应该调用:
_withCommit(fn) {
const committing = this._committing
this._committing = true
fn()
this._committing = committing
}
2
3
4
5
6
其核心的逻辑就是执行fn函数前,先将_committing的值设置为true,执行完fn函数后,将_committing恢复为原来的值。
# registerModule
方法声明: registerModule(path: Array<string> | string, rawModule: Module, options?: Object)
registerModule方法用于动态注册一个模块,使用动态模块注册可以给Vuex带来更大的灵活性,一般Vuex插件就可以通过该方法来使用Vuex的状态管理,比如vuex-router-sync (opens new window)插件就是通过动态注册模块将vue-router和vuex结合在一起,实现应用的路由状态管理。其定义如下:
registerModule(path, rawModule, options = {}) {
if (typeof path === 'string') path = [path]
// 不能用于动态注册根模块
if (process.env.NODE_ENV !== 'production') {
assert(Array.isArray(path), `module path must be a string or an Array.`)
assert(path.length > 0, 'cannot register the root module by using registerModule.')
}
// 动态注册模块
this._modules.register(path, rawModule)
// 安装模块
installModule(this, this.state, path, this._modules.get(path), options.preserveState)
// reset store to update getters...
// 重置vm实例
resetStoreVM(this, this.state)
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
在注册动态模块时,首先会保证注册的不是根模块,即path.length > 0(因为Vuex是将根模块设置成不可变的),所以该方法只能用于注册子模块。注册步骤如下:
- 将
rawModule添加到_modules中,此过程从this._modules.root中查找到其父模块,然后添加到父模块的_children中,并添加到_rawModule中。 - 安装当前模块,即注册
mutations、actions和getters,安装时会递归安装子模块 - 重置
vm实例,即重新更新getters和state的响应式数据
与实例化Store时安装模块不同,注册模块时,此处调用installModule最后一个参数会设置为option.preserveState,该参数默认为false,当将其设置为true时表示保留之前的state。在installModule函数中有如下代码:
function installModule(store, rootState, path, module, hot) {
// ....
if (!isRoot && !hot) { // 非根模块且非热更新时,设置模块状态
const parentState = getNestedState(rootState, path.slice(0, -1))
const moduleName = path[path.length - 1]
store._withCommit(() => {
Vue.set(parentState, moduleName, module.state)
})
}
// ...
}
2
3
4
5
6
7
8
9
10
11
12
此时,action、mutation、getter将会被添加到store中而store.state将不会被替换。一般在服务端渲染中,某些需要保留旧state的场景就会将该值设置为true。
# unregisterModule
方法声明: unregisterModule(path: string | Array<string>)
unregisterModule方法用于卸载一个动态模块,其定义如下:
unregisterModule(path) {
if (typeof path === 'string') path = [path]
if (process.env.NODE_ENV !== 'production') {
assert(Array.isArray(path), `module path must be a string or an Array.`)
}
this._modules.unregister(path) // 卸载
this._withCommit(() => {
const parentState = getNestedState(this.state, path.slice(0, -1))
Vue.delete(parentState, path[path.length - 1])
})
// 重置vm实例
resetStore(this)
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
根据传入的path,找到_modules中的对应模块,然后调用_modules.unregister方法卸载该模块,同时会删除state中对应的属性。在调用this._modules.unregister(path)时会判断模块的runtime属性是否为true(动态注册),只有为true时才会执行卸载操作。
# hasModule
方法声明: hasModule(path: Array<string> | string): boolean
这个是Vuex 3.2.0新增的方法,用于判断是否注册了某个模块。通过查看3.2.0对应的源码,其定义如下:
// store.js
hasModule (path) {
if (typeof path === 'string') path = [path]
if (process.env.NODE_ENV !== 'production') {
assert(Array.isArray(path), `module path must be a string or an Array.`)
}
return this._modules.isRegistered(path)
}
2
3
4
5
6
7
8
9
10
// module/module-collection.js
isRegistered (path) {
const parent = this.get(path.slice(0, -1))
const key = path[path.length - 1]
return parent.hasChild(key)
}
2
3
4
5
6
7
8
// module/module.js
hasChild (key) {
return key in this._children
}
2
3
4
代码非常简单,就是先根据给定的path,找到父模块,然后判断父模块的_children属性中是否有对应的子模块。
# hotUpdate
方法声明: hotUpdate(newOptions: Object)
hotUpdate方法用于热更新,其执行后会热替换新的action、mutation、module和getter。例子查看这里 (opens new window)。定义如下:
hotUpdate(newOptions) {
this._modules.update(newOptions)
resetStore(this, true)
}
2
3
4
- 首先会执行
this._modules.update(newOptions),此操作会递归更新模块,更新时只会更新actions、mutation和getters,不会更新state。 - 然后调用
resetStore方法,第二个参数为true会透传给installModule和resetStoreVm,从而会触发hot相关逻辑。
以上就是所有的Store实例方法,所有列表如下:
