You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
const{
plugins =[],
strict =false}=optionslet{
state ={}}=optionsif(typeofstate==='function'){state=state()}// 解构赋值 的方式,将传入构造函数的参数进行内部定义,可以看到这里 state 也可以通过 function 的方式来定义。
到这里位置,整个 store 的初始化工作就算完成了,实现了 store 内部有关 状态(state),逻辑(mutations/actions/getters) 和 调度(commit/dispatch) 的填充。但整个 store 的构造函数并没有结束,在构造函数的最后,store 回答了那个困扰我很久的问题:为什么 getters 能够响应 state 的变化?
追踪状态变更
// location: src/store.js// line: 56// initialize the store vm, which is responsible for the reactivity// (also registers _wrappedGetters as computed properties)resetStoreVM(this,state)// apply pluginsplugins.concat(devtoolPlugin).forEach(plugin=>plugin(this))
一、在Vue中使用Vuex的核心步骤:
Vue.use(Vuex)
;new Vue({ store })
;this.$store.state
、this.$store.getter
得到state,this.$store.commit(mutation)
、this.$store.dispatch(action)[异步]
更新action;二 、Vuex 源码的核心逻辑
插件安装原理
Vue.use(Vuex) 作为研究源码的入口,完成的第一个工作,就是在 Vue 中进行 Vuex 的安装工作。
关于 Vue.use() 的用法,官方文档的解释如下:
可以看出,插件安装的核心原理,就是调用插件所提供的 install 方法。它的源码 主要完成了以下几个工作:
Vuex 插件安装的本质,是调用了 Vue.mixin() 这个方法。这是Vue 为插件作者所提供的 混合机制 ,它在全局注册了一个混合,进而会影响到注册之后所有创建的每个 Vue 实例。
store 作为 Vue 实例的一个 option 被注入到根部组件,而后所有的子组件都从其父组件的 options 中去寻找这个 store 属性,这样层层传递下去,所有的组件就都可以通过 this.$store 来访问全局的 store 了。
store初始化
在对上述的内部状态进行赋值的时候,Vuex 是按照 modules 的层次逻辑逐层展开的。最开始的 demo 中可以看出,Vuex 提供 modules 机制。在没有 modules 的情况下, 所有的 state, mutations, actions, getters 都会”挂载“在同一个节点上,随着系统规模的扩大,整个逻辑会显得非常臃肿,不便于维护。
modules 机制很好地解决了这个问题,不同模块将拥有自己独立的 state, mutations, actions, getters ,模块间公共的部分可以提炼出来挂在“根节点”上,私有的部分作为“子节点”,按照 命名空间 的指定规则依次进行挂载。
需要注意的是,Vuex 内部并不是一个树的结构,树的查找和插入操作复杂度都比较高,为了便于理解,这里用
挂载
这个词来形容整个 modules 的层次构建。实际上 Vuex 是 “扁平化” 处理的,所有的 modules 都有一个path 字段
,来定义modules 的层次结构,所以也可以理解为是一个 flatten 化的 tree
。installModule 方法会根据 namespace 参数,
递归
地对 modules 进行构建。代码的前半段,取出了 module 的私有 state ,以 Vue.set() 的方法,挂载到 parentState 下面。
代码的后半段,对 module 内的 mutations,actions,getters进行注册操作。注意这里的 local 变量,它定义了每个 module 各自独立的 dispatch 和 commit 方法,实质就是,在设置了 namespace 的情况下,这两个方法会在 action / mutation 的 type 前面,加上 module 的 path,来完成正确的调用操作。
注册部分,以 mutations 为例,所有 modules 的 mutations 最终都存储在 store._mutaions 这个在之前预先定义好的私有变量中。
没有 namespace 的情况下,发起 this.$store.commit('increment', payload)操作之后,来自不同 module 的同名 mutations,最终会被同时调用。加上 namespace 之后,就可以进行独立调用:this.$store.commit('A/increment', payload),this.$store.commit('B/increment', payload)。
以如果模块之间涉及 同名 mutations 时,一定要在 module 的定义位置,加上 namespaces: true。
到这里位置,整个 store 的形状基本已经搭建完成了,state,actions,mutations,getters都已按照 modules 的层级关系,递归地完成了初始化。可以理解为:store 内部的 状态,以及对这些状态进行变更的 逻辑 ,都已填充完毕,接下来要做的就是绑定 commit 和 dispatch 这两个核心方法,来完成对逻辑的 调度 。
commit 方法的第一步,调用了unifyObjectStyle() 这个函数,由于 Vuex 的 commit 操作提供两种传参风格,所以要对传入的参数做统一处理。
然后 commit 方法会根据传入的 type 参数,去找到相应 mutation 的回调函数来执行,可以看到执行动作用 this._withCommit() 方法进行了包裹,这里就用到了前面提到的 this._committing 变量。
这个方法保证了所有的 state 变更操作,都会把 this._committing 置为 true ,一旦 state 变更时这个值为 false 时,就说明采用了异常方法进行了状态修改,然后就会进行报错。这个在下文的 严格模式 中会提及。
this._withCommit() 方法的内部,可以看到用了 entry.forEach() ,这就解释了前文提到的,在没有 namespace 的情况下,同名函数都会被执行的原因。
在完成 mutation 的回调之后,会调用 this.subscribers 中注册的所有回调,那些 Vuex 相关插件的订阅回调就会被执行。
到这里位置,整个 store 的初始化工作就算完成了,实现了 store 内部有关 状态(state),逻辑(mutations/actions/getters) 和 调度(commit/dispatch) 的填充。但整个 store 的构造函数并没有结束,在构造函数的最后,store 回答了那个困扰我很久的问题:为什么 getters 能够响应 state 的变化?
追踪状态变更
最后一行很容易理解,就是完成 Vuex 的插件注册,其中 devtoolPlugin 是内置的默认插件, Chrome 的 Vue 扩展中,里面的 Vuex 时光穿梭 功能,就是通过这个插件实现的。
resetStoreVM(this, state)
部分这段代码的关键核心,就是 store._vm 这个变量,它的 本质是一个 Vue 实例 。在这个 Vue 实例中,state 作为 data 传入,而 computed 属性则挂载了所有的 getters!因此,通过 Vue 的响应式原理,Vuex 实现了 state 和 getters 的响应式跟踪。
到这里就可以解释清楚了,之所以 mutation 修改了 state 之后,组件会感知到 state 的变化,getters 也会感知到 state 的变化,是因为 Vuex store 的本质,是构建了一个 Vue 实例,而所有 mutations,actions 所涉及的逻辑操作,都是对这个 Vue 实例进行 data 修改。进而其实可以发现,Vuex 响应式的原理,和 Vue 官方提出的 中央事件总线 其实是一个道理的。
在
resetStoreVM(this, state)
的最后,对严格模式和热重载进行了响应的处理。在 开发环境 下开启严格模式之后,Vuex 会跟踪所有 state 的变化过程,并且这是一种 深度跟踪 ,对复杂对象依旧有效。这么做的目的是,devtoolPlugin 能够记录 state 的状态和变化,并且所有不是通过 mutations 而触发的 state 变更,都会报错。在 生产环境 下,严格模式通常会被关闭,以此来提高性能。
另外,Vuex 提供 mutations / actions / modules 的 热重载 功能,上面代码片段的末尾部分,就是在 热重载 的模式下,强制刷新所有监听者(watchers),并且将上一个状态的 oldVm 销毁,节省内存。
The text was updated successfully, but these errors were encountered: