继上面讲完contructor函数,我们继续来讲后面的内容
get state () {
return this._vm._data.$$state
}
set state (v) {
if (process.env.NODE_ENV !== 'production') {
assert(false, `Use store.replaceState() to explicit replace store state.`)
}
}
get和set方法都有es6里边的语法,get语法是将对象属性绑定到查询该属性时将被调用的函数,而set语法是查询对象属性将被调用的函数。当state属性发生变化时,就会返回state的值,set用于检测在我们设置state的时候是否是生产环境,如果不是生产环境,就进行断点。
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 (process.env.NODE_ENV !== 'production') {
console.error(`[vuex] unknown mutation type: ${type}`)
}
return
}
this._withCommit(() => {
entry.forEach(function commitIterator (handler) {
handler(payload)
})
})
this._subscribers.forEach(sub => sub(mutation, this.state))
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'
)
}
}
commit函数接受三个参数,_type表示mutaion的类型,_payload表示额外的参数,options表示一些配置。在函数中有函数叫unfyObjectStyle(),来看看他的代码
function unifyObjectStyle (type, payload, options) {
if (isObject(type) && type.type) {
options = payload
payload = type
type = type.type
}
if (process.env.NODE_ENV !== 'production') {
assert(typeof type === 'string', `Expects string as the type, but found ${typeof type}.`)
}
return { type, payload, options }
}
这个函数很简单,就是在type为对象的情况下,对传进来的参数进行各自获取。继续看commit函数,根据unifyObjectStyle()处理后的type去找到mutations,withCommient的代码如下
_withCommit (fn) {
const committing = this._committing
this._committing = true
fn()
this._committing = committing
}
这个函数最重要的部分在于设置this._committing = true为什么这么做呢。是因为我们在观测state的状态的时候,我们可以通过看this._commiting的值来确定state修改是否错误,如果为false那么是不是状态修改就出现了问题呢。
那commit后面的部分就容易理解了,使用this._withCommit方法提交mutation。
this._subscribers是什么内容呢,这在我们上一篇提过是用来存储所有对 mutation 变化的订阅者。遍历this._subscribers进行回调。
options && options.silent
这儿我也不太懂黄轶老师说的是静默模式:静默模式就是用户不干扰的情况下,计算机自我处理的情况。
下面是是dispatch的代码
dispatch (_type, _payload) {
// check object-style dispatch
const {
type,
payload
} = unifyObjectStyle(_type, _payload)
const action = { type, payload }
const entry = this._actions[type]
if (!entry) {
if (process.env.NODE_ENV !== 'production') {
console.error(`[vuex] unknown action type: ${type}`)
}
return
}
this._actionSubscribers.forEach(sub => sub(action, this.state))
return entry.length > 1
? Promise.all(entry.map(handler => handler(payload)))
: entry[0](payload)
}
前面几行代码跟commit函数非常相似,取值,然后找到对应action。
this._actionSubscribers
跟前面commit方法不同的是最后用的是这段代码:
return entry.length > 1
? Promise.all(entry.map(handler => handler(payload)))
: entry[0](payload)
如果获取到的action的数量不止一个那么就进行Promise.all()遍历每个action进行回调函数回调,前几天看到一道面试题如果一个进程需要消耗8s,
另外一个进程需要消耗3秒,如何才能让两个进行的时间最短,解决方法就是Promise.all([a,b])注意里边的必须是一个数组,这样才能让两个进程同时进行。如果长度为1,则调用entry0
subscribe (fn) {
return genericSubscribe(fn, this._subscribers)
}
subscribeAction (fn) {
return genericSubscribe(fn, this._actionSubscribers)
}
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)
}
}
}
来看看genericSubscribe都做了什么,接受两个参数,就是对subs进行调整,如果subs里边没有fn,那么向subs.push(fn)。返回的是删除fn的subs。那么上面subscribe和subscribeAction就是对传入的fn然后经过
genericSubscribe()函数进行处理
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)
}
watch作用是响应式的检测一个getter方法的返回值,当值反生变化的时候,发生回调。
函数首先首先断言watch的getter必须是一个方法,接着利用了vue的实例对象的this._watcherVM的watch方法
replaceState (state) {
this._withCommit(() => {
this._vm._data.$$state = state
})
}
用于修改state,调用 this._withCommit 方法修改 Store 的 rootState
registerModule
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...
resetStoreVM(this, this.state)
}
字面意思能看出动态注册Module的意思,动态的意思是什么呢。就是有些异步操作,我们需要使用这个函数就能够动态注册module了。
这儿有一个函数resetStoreVM,我们来看看他的实现方式
function resetStoreVM (store, state, hot) {
const oldVm = store._vm
// bind store public getters
store.getters = {}
const wrappedGetters = store._wrappedGetters
const computed = {}
forEachValue(wrappedGetters, (fn, key) => {
// use computed to leverage its lazy-caching mechanism
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
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())
}
}
这个函数的意思在于监听数据的变化,首先进行store.getters的初始化。forEachValue循环所有处理过的getters,并且新建computed对象进行存储,通过Object.defineProperty。然后为store.getter定义属性key,enumerable设置为true,表示可枚举。
前存储先前的Vue.config.slient,Vue.config.slient 设置为true是用于取消所有的警告和提醒。
store._vm = new Vue({
data: {
$$state: state
},
computed
})
这个就很简单了,创建一个Vue的实例,然后把state放到data里边去,computed里边的值用getters填充。
if (store.strict) {
enableStrictMode(store)
}
如果是严格模式的话,则调用enableStrictMode()
下面是enableStrictMode的代码
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 })
}
这个函数做了什么了,看assert写的意识就是,如果设置了严格模式,就不允许用户在mutaion以外的方式进行修改state
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())
}
如果oldVm存在,并且hot为True的时候,将原有的vm中的state设置为空,所有原来的getters都会重新计算一遍,为什么会这样呢,这在前面我们创建一个vue的实例的时候,将getters作为了computed,我们知道在Vue中,值一旦发生变化,那么computed就会重新变化。
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])
})
resetStore(this)
}
跟函数registerModule相似,用于动态卸载module。讲讲这里的this._withCommit的作用,
把当前的模块的state对象从父state上删除,最后调用resetStore函数,下面是resetStore函数
function resetStore (store, hot) {
store._actions = Object.create(null)
store._mutations = Object.create(null)
store._wrappedGetters = Object.create(null)
store._modulesNamespaceMap = Object.create(null)
const state = store.state
// init all modules
installModule(store, state, [], store._modules.root, true)
// reset vm
resetStoreVM(store, state, hot)
}
这个方法的作用就是重置store, installModule就是重新安装_aciton,_mutations,_wrappedGetters这些属性,resetStoreVM然后重置store的_vm对象。
hotUpdate (newOptions) {
this._modules.update(newOptions)
resetStore(this, true)
}
函数的作用就是热加载新的action和mutation。先更新modules,然后更新Store。
vuex里边最重要的class Store就讲完了。我们在讲讲其他重要的函数。
export function deepCopy (obj, cache = []) {
// just return if obj is immutable value
if (obj === null || typeof obj !== 'object') {
return obj
}
// if obj is hit, it is in circular structure
const hit = find(cache, c => c.original === obj)
if (hit) {
return hit.copy
}
const copy = Array.isArray(obj) ? [] : {}
// put the copy into cache at first
// because we want to refer it in recursive deepCopy
cache.push({
original: obj,
copy
})
Object.keys(obj).forEach(key => {
copy[key] = deepCopy(obj[key], cache)
})
return copy
}
这个函数的原理就是构造一个新的对象,遍历原对象或者数组,递归调用deepCopy。这儿加了一个cache,为什么呢。这是为了提高性能,比如说在Facinaciate中,我们就可以使用这种方法来提高性能,不用每计算一次,又重新计算已经计算过的东西
这次就讲到这里,下次再讲其他重要的函数
下次我决定不采用这种什么函数都讲清楚的方式了。这样工程量实在是太多了,下次的源码分析将会以我总结的方式来学习源码