Open dwqs opened 7 years ago
感谢分享
确实dva将redux封装的更佳简化,通过reducer和effects实现非常强大的功能
这样做的目的可以保证当我们在组件中通过 this.$store 直接调用 dispatch/commit 方法时, 能够使 dispatch/commit 方法中的 this 指向当前的 store 对象而不是当前组件的 this
你好,这里通过 this.$store来调用dispatch时,this肯定是指向 store的吧?
图片挂了
之前大致分析过了 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
中:总结
Vuex@2.4 的源码分析就暂时到这了. Vuex 的确是一个很强大的状态管理工具, 并且非常灵活, 但有一个问题就是, 如果严格按照单向数据流的方式进行开发, 并参考官方文档给予的项目目录结构, 随着应用的复杂度越来越高, 开发者会写非常多的模板类代码. 这个问题同样出现在 Redux, 因而在 Redux 社区出现了诸如 dva、mirror 这样的解决方案来减少模板类代码的开发, 提高开发效率; 同时, React 社区也出现了更轻巧的状态管理工具, 如statty. 而在 Vuex 社区, 貌似还没有出现的类似的解决方案(如果你知道, 还请 issue 留链接), 因而个人在阅读过 Vuex 的源码之后, 造了一些相关的轮子, 欢迎参考和使用:
当然, 也可以根据项目需要, 采用其它的状态管理方案, 例如 mobx.