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
实例方法,所有列表如下: