-
Notifications
You must be signed in to change notification settings - Fork 442
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 源码分析 #58
Comments
感谢分享 |
确实dva将redux封装的更佳简化,通过reducer和effects实现非常强大的功能 |
你好,这里通过 this.$store来调用dispatch时,this肯定是指向 store的吧? |
图片挂了 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
之前大致分析过了 vue-cli 源码 和 vue-router 源码, 这两个工具也是 vue 生态中比较重要的组件. 而最近因为业务上的需要, 接触到了 vuex 的一些组件源码.
vuex
集中于MVC模式中的Model层, 规定所有的数据操作必须通过action
-mutation
-state change
的流程来进行, 再结合vue
的数据视图双向绑定特性来实现页面的展示更新:现在就具体来分析一下其流程.
目录结构
打开
Vuex
项目, 先了解下其目录结构:Vuex提供了非常强大的状态管理功能, 源码代码量却不多, 目录结构划分也很清晰. 先大体介绍下各个目录文件的功能:
mapGetters
,mapActions
等 APIstore
vuex
开发的一系列工具方法, 如forEachValue
/assert
等入口
在 2.4.0 中,
vuex
提供了 UMD 和 ESM(ES module) 两个构建入口, 分别对应src/index.js
和src/index.esm.js
文件. 在入口文件中, 主要是导出vuex
提供给 Vue 应用的 API:在 Vue 应用中,
vuex
是作为vue
的一个插件为 Vue 应用提供强大的状态管理功能. 因而, 在能够使用vuex
的功能之前, 先要在 Vue 应用的入口文件中安装vuex
:Vue 在安装插件时, 会去调用插件提供的
install
方法:在
src/store.js
文件中, 先声明了一个局部变量Vue
来保存Vue 引用, 该变量有如下作用:Vue.js
作为一个依赖打包vuex
的条件判断Store
中调用 vue 全局 API 的提供者在
install
方法中, 调用了applyMixin
方法:applyMixin
方法的主要功能将初始化 Vue 实例时传入的store
设置到this
对象的$store
属性上, 子组件则从其父组件引用$store
属性, 层层嵌套进行设置. 这样, 任何一个组件都能通过this.$store
的方式访问store
对象了.store对象构造
store
对象构造的源码定义在src/store.js
中, 梳理源码之前, 先大致了解下其构造流程:环境判断
在
store
的构造函数中,vuex
先对构造store
需要的一些环境变量进行断言:assert
函数的定义是在src/util
中:初始化变量
在环境变量判断之后, 在构造函数中会定义一些变量, 这些变量一部分来自
options
, 一部分是内部定义:收集 modules 时, 传入调用 Store 构造函数传入的
options
对象,ModuleCollection
类的定义在src/modules/module-collection.js
中:ModuleCollection
主要将传入的options
对象整个构造为一个module
对象, 并循环调用register
为其中的modules
属性进行模块注册, 使其都成为module
对象, 最后options
对象被构造成一个完整的组件树. 详细源码可以查看 module-collection.js.Module
类的定义在src/modules/module.js
:接着往下看:
上述代码主要是把
Store
类的dispatch
和commit
的方法的this
指针指向当前store
的实例上. 这样做的目的可以保证当我们在组件中通过this.$store
直接调用dispatch/commit
方法时, 能够使dispatch/commit
方法中的this
指向当前的store
对象而不是当前组件的this
.dispatch
的功能是触发并传递一些参数(payload)给与type
对应的action
, 其具体实现如下:而
commit
会将action type
提交给对应的mutation
, 然后执行对应mutation
函数修改module
的状态, 其实现如下:梳理完
dispatch
和commit
, 接着看后面的代码:后续的代码主要是安装 modules、vm 组件设置和安装通过
options
传入的插件以及根据 Vue 全局的devtools
设置, 是否启用devtoolPlugin
插件. 接下来就先分析下 vm 组件部分, 之后再分析安装 modules 的部分.vm 组件设置
resetStoreVM
的定义如下:module 安装
安装
modules
是 vuex 初始化的核心.ModuleCollection
方法把通过options
传入的modules
属性对其进行Module
处理后,installModule
方法则会将处理过的modules
进行注册和安装, 其定义如下:installModule
接收5个参数:store
、rootState
、path
、module
、hot
.store
表示当前Store
实例,rootState
表示根state
,path
表示当前嵌套模块的路径数组,module
表示当前安装的模块,hot
当动态改变modules
或者热更新的时候为true
.registerMutation
该方法是获取
store
中的对应mutation type
的处理函数集合:registerAction
该方法是对
store
的action
的初始化:在调用用户定义的
action handler
时, 给改handler
传入了三个参数: context 对象, payload 和一个回调函数(很少会用到).context
对象包括了store
的commit
和dispatch
方法、当前模块的getters
/state
和rootState
/rootGetters
等属性, 这也是我们能在action
中获取到commit/dispatch
方法的原因.registerGetter
该方法是对
store
的getters
的初始化:子 module 安装
注册完了根组件的
actions
、mutations
以及getters
后, 递归调用自身, 为子组件注册其state
,actions
、mutations
以及getters
等.辅助函数
Vuex 除了提供我们
store
对象外, 还对外提供了一系列以mapXXX
命名的辅助函数, 提供了操作store
的各种属性的一系列语法糖. 辅助函数的定义均在src/helpers.js
中, 由于mapXXX
等函数的实现大同小异, 本文则只挑选常用的mapActions
和mapGetters
进行简单分析.在分析之前, 先看两个函数的实现:
normalizeNamespace
和normalizeMap
.normalizeNamespace
函数的主要功能返回一个新的函数, 在新的函数中规范化namespace
参数, 并调用函数参数fn
.normalizeMap
函数的作用则是将传递给mapXXX
的参数统一转化为对象返回, 例如:mapGetters
该函数会将
store
中的getter
映射到局部计算属性中:mapActions
该方法会将
store
中的dispatch
方法映射到组件的methods
中:总结
[email protected] 的源码分析就暂时到这了. Vuex 的确是一个很强大的状态管理工具, 并且非常灵活, 但有一个问题就是, 如果严格按照单向数据流的方式进行开发, 并参考官方文档给予的项目目录结构, 随着应用的复杂度越来越高, 开发者会写非常多的模板类代码. 这个问题同样出现在 Redux, 因而在 Redux 社区出现了诸如 dva、mirror 这样的解决方案来减少模板类代码的开发, 提高开发效率; 同时, React 社区也出现了更轻巧的状态管理工具, 如statty. 而在 Vuex 社区, 貌似还没有出现的类似的解决方案(如果你知道, 还请 issue 留链接), 因而个人在阅读过 Vuex 的源码之后, 造了一些相关的轮子, 欢迎参考和使用:
当然, 也可以根据项目需要, 采用其它的状态管理方案, 例如 mobx.
The text was updated successfully, but these errors were encountered: