前言
现在前端开发中vuejs是大部分前端工程师的首选,只要不是很小的项目都会引用vuex来进行状态管理。最近新开展的项目比较复杂,使用vuex的地方也比较多。在此过程中也遇到了不少问题。如今有时间正好研究下vuex框架源码,深入了解下他的底层实现。
为什么要使用vuex
在当前前端的spa模块化项目中不可避免的是某些变量需要在全局范围内引用,此时父子组件的传值,子父组件间的传值,兄弟组件间的传值成了我们需要解决的问题。虽然vue中提供了props(父传子)commit(子传父)兄弟间也可以用localstorage和sessionstorage。但是这种方式在项目开发中带来的问题比他解决的问题(难管理,难维护,代码复杂,安全性低)更多。vuex的诞生也是为了解决这些问题,从而大大提高我们vue项目的开发效率。(信不信由你=-=)我接触vue项目的这两年,虽然一直在用vuex(用的还算熟练),但是还没有深入的去了解过他的架构原理,最近正好有空,就来深入学习下。
vuex的整体架构主体
源码解析(工作原理)
- 根state的存储位置 当你在使用vuex时有没有想过你在代码中设置的state被vuex如何处理了呢
const store = new Vuex.Store({ state: { count: 0 }, mutations: { increment (state) { state.count++ } } })
我们设置在跟state中的属性会被vuex存储在根元素中
this._modules = new ModuleCollection(options)//初始化 const state = this._modules.root.state//获取定义的state
vuex初始化时先去获取定义在state属性中的值new ModuleCollection(options)进行模块收集(重新组装我们定义在store中的相关属性): 最终形成一棵module树
export default class ModuleCollection { constructor (rawRootModule) { // register root module (Vuex.Store options) this.register([], rawRootModule, false) } get (path) { return path.reduce((module, key) => { return module.getChild(key) }, this.root) } getNamespace (path) { let module = this.root return path.reduce((namespace, key) => { module = module.getChild(key) return namespace + (module.namespaced ? key + '/' : '') }, '') } update (rawRootModule) { update([], this.root, rawRootModule) } register (path, rawModule, runtime = true) { if (process.env.NODE_ENV !== 'production') { assertRawModule(path, rawModule) } const newModule = new Module(rawModule, runtime) if (path.length === 0) { this.root = newModule } else { const parent = this.get(path.slice(0, -1)) parent.addChild(path[path.length - 1], newModule) } // register nested modules if (rawModule.modules) { forEachValue(rawModule.modules, (rawChildModule, key) => { this.register(path.concat(key), rawChildModule, runtime) }) } } unregister (path) { const parent = this.get(path.slice(0, -1)) const key = path[path.length - 1] if (!parent.getChild(key).runtime) return parent.removeChild(key) } }
- commit(=>mutations)时做了什么
const { dispatch, commit } = this
//初始化先绑定commit
this.commit = function boundCommit(type, payload, options) { return commit.call(store, type, payload, options) }
绑定之后注册mutation(commit的属性)
function registerMutation(store, type, handler, local) { const entry = store._mutations[type] || (store._mutations[type] = []) entry.push(function wrappedMutationHandler(payload) { handler.call(store, local.state, payload) }) }
commit(_type, _payload, _options) { console.log('================commit=====================') console.log(_type) console.log(_payload) console.log(_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 } //在执行mutation的时候,会将_committing设置为true,执行完毕后重置,在开启strict模式时,会监听state的变化,当变化时_committing不为true时会给出警告 this._withCommit(() => { //迭代传入的commit数组 entry.forEach(function commitIterator(handler) { handler(payload) }) }) this._subscribers.forEach(sub => sub(mutation, this.state)) //当开发环境是抛出警告(如果commit的属性不存在) 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' ) } }
3.dispatch(=>actions)做了啥(初始化同commit)
//dispatch定义位置 this.dispatch = function boundDispatch(type, payload) { return dispatch.call(store, type, payload) }
绑定dispatch
function registerAction(store, type, handler, local) { const entry = store._actions[type] || (store._actions[type] = []) entry.push(function wrappedActionHandler(payload, cb) { let res = handler.call(store, { dispatch: local.dispatch, commit: local.commit, getters: local.getters, state: local.state, rootGetters: store.getters, rootState: store.state }, payload, cb) //将res函数转为异步 promise if (!isPromise(res)) { res = Promise.resolve(res) } if (store._devtoolHook) { return res.catch(err => { store._devtoolHook.emit('vuex:error', err) throw err }) } else { return res } }) }
注册actions
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 } try { this._actionSubscribers .filter(sub => sub.before) .forEach(sub => sub.before(action, this.state)) } catch (e) { if (process.env.NODE_ENV !== 'production') { console.warn(`[vuex] error in before action subscribers: `) console.error(e) } } const result = entry.length > 1 ? Promise.all(entry.map(handler => handler(payload))) : entry[0](payload) return result.then(res => { try { this._actionSubscribers .filter(sub => sub.after) .forEach(sub => sub.after(action, this.state)) } catch (e) { if (process.env.NODE_ENV !== 'production') { console.warn(`[vuex] error in after action subscribers: `) console.error(e) } } return res }) }
其他
vuex和全局变量的区别:(借用博客)
1,【响应式】vuex的状态存储是响应式的,当Vue组件从store中读取状态的时候,若store中的状态发生变化,那么相应的组件也会得到高效更新。
2,【不能直接改变store】不能直接改变store的变化,改变store中状态的唯一途径是commit mutation。方便于跟踪每一个状态的变化。