1.state
它是唯一数据源,每个应用都只有一个 store 实例。响应式,类似于data。
通过在根实例中注册 store 选项,该 store 实例会注入到根组件下的所有子组件中。所有组件都可以访问得到。
const Counter = {
template: `<div>{{ count }}</div>`,
computed: {
count () {
return this.$store.state.count
}
}
}
相关源码
看一下store的构造函数
let Vue // bind on install
export class Store {
constructor(options = {}) {
// Auto install if it is not done yet and `window` has `Vue`.
// To allow users to avoid auto-installation in some cases,
// this code should be placed here. See #731
if (!Vue && typeof window !== 'undefined' && window.Vue) {
install(window.Vue)
}
if (__DEV__) {
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.`
)
}
const { plugins = [], strict = false } = options
// store internal state
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()
this._makeLocalGettersCache = Object.create(null)
// bind commit and dispatch to self
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)
}
// strict mode
this.strict = strict
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)
}
}
}
new Store的时候如果还未安装,并且已经有全局引入 Vue 的情况下,就会自动安装,但如果已经安装,则无需再次安装。- 然后初始化一些内部变量;主要还执行了installModule(初始化module)以及resetStoreVM(使store“响应式”)。
function resetStoreVM(store, state, hot) {
const oldVm = store._vm
// bind store public getters
store.getters = {}
// reset local getters cache
store._makeLocalGettersCache = Object.create(null)
const wrappedGetters = store._wrappedGetters
const computed = {}
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 environment.
computed[key] = partial(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
store._vm = new Vue({
data: {
$$state: state
},
computed
})
Vue.config.silent = silent
// enable strict mode for new vm
if (store.strict) {
enableStrictMode(store)
}
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())
}
}
- 使用Object.defineProperty方法为每一个getter绑定上get方法,这样我们就可以在组件里访问this.$store.getter.todoList就等同于访问store._vm.todoList。
- 通过new Vue()来实现 state的响应式,谁让人家是专门给vue用的呢。
2.getter
可以认为是 store 的计算属性。就像计算属性一样,getter 的返回值会根据它的依赖被缓存起来,且只有当它的依赖值发生了改变才会被重新计算。 通过属性访问
store.getters.todoList
3.mutation
更改 Vuex 的 store 中的状态的唯一方法是提交 mutation。Vuex 中的 mutation 非常类似于事件:每个 mutation 都有一个字符串的 事件类型 (type) 和 一个 回调函数 (handler)
以相应的 type 调用 store.commit 方法:
mutation: {
increment () {
//...
}
}
store.commit('increment')
相关源码
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]
if (!entry) {
if (__DEV__) {
console.error(`[vuex] unknown mutation type: ${type}`)
}
return
}
this._withCommit(() => {
entry.forEach(function commitIterator(handler) {
handler(payload)
})
})
this._subscribers
.slice() // shallow copy to prevent iterator invalidation if subscriber synchronously calls unsubscribe
.forEach((sub) => sub(mutation, this.state))
if (__DEV__ && options && options.silent) {
console.warn(
`[vuex] mutation type: ${type}. Silent option has been removed. ` +
'Use the filter functionality in the vue-devtools'
)
}
}
- 根据type取出所有type对应的mutation的方法;
- 执行mutation中的所有方法;
- 在commit结束以后则会通知_subscribers中的订阅者操作的mutation对象以及当前的state状态
4.Action
与mutation类似,一般用来出来异步操作。
- Action 提交的是 mutation,而不是直接变更状态。
- Action 可以包含任意异步操作
actions: {
incrementAsync ({ commit }) {
setTimeout(() => {
commit('increment')
}, 1000)
}
}
store.dispatch('increment')
相关源码
dispatch (_type, _payload) {
// check object-style dispatch
const {
type,
payload
} = unifyObjectStyle(_type, _payload)
const entry = this._actions[type]
if (!entry) {
console.error(`[vuex] unknown action type: ${type}`)
return
}
return entry.length > 1
? Promise.all(entry.map(handler => handler(payload)))
: entry[0](payload)
}
与mutation类似,根据type取出所有type对应的action的方法;执行其中方法;
5.module
由于使用单一状态树,当应用变得非常复杂时,状态比较多,维护比较麻烦。Vuex 允许我们将 store 分割成模块(module) 。每个模块拥有自己的 state、mutation、action、getter...
const moduleA = {
state: () => ({ ... }),
mutations: { ... },
actions: { ... },
getters: { ... }
}
const store = createStore({
modules: {
a: moduleA,
...
}
})
store.state.a // -> moduleA 的状态