-
Notifications
You must be signed in to change notification settings - Fork 3
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Vuex源码解析 #9
Labels
Comments
Open
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Vuex 源码解析
Vuex是什么?
Vuex使用方式
接下来按照使用方式的步骤进行分析。
Vuex.install——安装Vuex
src/index.js
根据Vue.use()用法说明,
Vue.use(Vuex)
执行时会调用Vuex.install()
,内容如下:src/store.js
再看
applyMixin(Vue)
src/mixin.js
执行
Vue.use(Vuex)
目的就是在每个Vue实例初始化时,往beforeCreate
生命周期中加入vueInit
函数。在“注入store”这一步:
执行Vue实例的
beforeCreate
生命周期,vueInit
函数被执行,运行时this
指向当前vue实例上下文,因此this.$options
指向当前vue组件配置对象传入的
store
可以被options.store
访问到,此时执行将
options.store
内容赋值给this.$store
,即完成了store
的注入。而
options.store
不存在时,即没有给当前组件配置store
时,vueInit
会尝试访问父组件已经注入完成的$store
,若存在则赋值给当前组件this.$store
,实现组件树内共享store
。至此,Vuex的安装、注入看完了,接下来具体看创建Store。
Vuex.Store(options)——创建Store
实例化过程
上面省略过后的实例化过程中,主要做了五件事:
window.Vue
存在),可以省略手动调用Vue.use(Vuex)
commit
、dispatch
指向当前Store实例installModule
resetStoreVM
关于第三点“绑定commit、dispatch指向当前Store实例”,可以这么理解:
若未绑定
commit
方法的this
指向,此时变量ct
值为未绑定this
指向的commit
函数,当执行ct('addVal')
时,this
会指向当前(全局)作用域上下文window
,ct
函数内部使用this
就不会按预期地指向store
。因此需要对commit
、dispatch
进行绑定。resetStoreVM
Vuex.Store
的基础是state
,在Store
类中定义为:定义的是一个取值器和一个赋值器。state是一个伪属性,没有真实值,当尝试直接修改state时,
修改不会生效,而是执行赋值器方法,提示使用
store.replaceState()
方法。当读取
state
时,读取的实际上是this._vm._data.$$state
,this._vm
在resetStoreVM()
中赋值,下面具体看resetStoreVM源码resetStoreVM函数的关键部分是
其中
state
是resetStoreVM
的入参,computed
对应store._wrappedGetters
,并在store.getters
中定义取值器,获取_vm
的计算结果。这就是专用的原因:数据响应更新是借由Vue实现的。
需要指出一处特殊的地方:这里的配置对象中
data
是一个对象,而非通常的函数。这仅仅是因为不需要对Vue实例进行复用,直接赋值对象性能较好。
另一个需要理解的地方是:
this._vm._data.$$state
从_data
中访问$$state
。_data
是Vue实例_vm
的真实值(见Vue源码),而_vm.$$state
是对_vm._data.$$state
取值的伪属性(见Vue源码)。同样是因为直接取真实值性能较好。至此,可以明确
this.$store.state
访问的结果是由state
、_wrappedGetters
构成的Vue实例的状态。接下来看
state
是如何赋值的。ModuleCollection
Vuex.Store构造函数实例化过程中可以找到state初始赋值的位置:
赋予的值为
this._modules.root.state
,语义上理解为“根模块的状态”。this._modules
是一个“模块集合”类的实例,具体看ModuleCollection
内容:src/module/module-collection.js
可以看出模块集合ModuleCollection中的模块Module内容,是在Module类中定义的。
进一步查看Module类的源码:
src/module/module.js
可见
Module
类负责存储runtime
标识并提供
namespaced
的布尔值取值器namespaced
、actions
、mutations
、getters
)的方法actions
、mutations
、getters
的方法回过头看
ModuleCollection
类:上面源码中
path
是指嵌套模块的路径,形如a/b/c
这样的嵌套模块,对应的path
值为[a, b, c]
。可见
ModuleCollection
类负责存储root
并提供
get(path)
:根据路径,获取根模块中某个模块getNamespace(path)
:根据路径,获取模块命名空间update()
:递归地更新根模块及其后代的原始模块register()
:递归地注册模块及其嵌套模块,注册模块是指将传入的原始模块存储为Module
实例unregister(path)
:取消注册模块,即将指定路径的模块从父级的子项索引中移除现在可以明确在src/store.js中
初始赋值的
state
是指,ModuleCollection
实例根模块的状态。这个状态仅包括根模块,不含嵌套模块,这样的state
传递给resetStoreVM
不足以实现嵌套模块的数据响应。下面看
installModule()
内容。installModule
src/store.js
installModule()
递归地完成了模块的安装,其中就包括state
嵌套赋值。store._withCommit()
是定义在Store
类上的方法(见源码):该方法修改
store._committing
的目的只有一个其中,
assert
在第一个参数为假值时,才会抛出错误。src/util.js
即Vuex在严格模式下,当
this._data.$$state
被观察到发生变化时,会在开发环境给出警告,而通过_withCommit
包装后执行函数,则不会发出警告。同理,Vuex
mutations
中状态变化不会发出警告,也是调用了_withCommit
方法下面就来分析
Store.commit()
方法:commit
源码位置
首先,使用
unifyObjectStyle()
函数对入参进行转换,用以支持对象风格的提交方式。然后,通过
this._withCommit()
包装执行入参type
指定的mutation函数,可以避免严格模式警告。最后,触发
subscribers
订阅器,这些订阅器是通过subscribe() API添加的。类似的,
Store.dispatch()
方法也很简单:dispatch
源码位置
与
commit
不同的是,dispatch
中执行的_actions
是Promise实例,是在installModule()
中创建的源码位置
registerAction()源码位置
至此,Vuex常用部分的源码已经解析完成,仍有一些相对独立部分的源码没有提到,请自行了解。
总结
用思维导图总结一下本文解析的源码内容:
在阅读过源码后,重新回答Vuex官网上提出的这个问题:什么情况下我应该使用 vuex?
什么情况下我应该使用 Vuex?
当你需要在Vue组件中管理共享状态时,就可以使用Vuex。
Vuex不仅提供了基础的状态管理、提交改动、分发事件,还提供了以嵌套模块、命名空间的方式来分治模块的模块化方案,更进一步提供了本文没有提及的插件(plugins)、监听、订阅、动态注册等功能。
即使你只用到了Vuex其中的一部分能力,Vuex提供的丰富能力意味着良好的扩展性,这对项目维护而言是有益的。
那为什么还需要思考这个该不该用的问题呢?因为这是一个技术选型问题,主要影响因素不在于Vuex技术本身,而在于项目本身。该不该用的问题,应当换一种问法:
我的回答是:
性价比考量
项目的状态管理是否复杂,或预期能够复杂到值得引入压缩后超过9KB的Vuex?开发时间有没有压力?
有没有其他选择
提供状态管理的库不止Vuex一种,是否有更熟悉、更亲睐的其他方案?比如团队内部自研库。
性能问题
Vuex源码多处使用递归,嵌套的模块越多,嵌套层级越深,性能越差,这种情况下不得不考虑不使用Vuex的更高性能状态管理方案。
The text was updated successfully, but these errors were encountered: