Skip to content

Commit

Permalink
Vuex源码解析更新
Browse files Browse the repository at this point in the history
  • Loading branch information
yzsunlei committed Dec 29, 2019
1 parent eeabe01 commit 056d02c
Show file tree
Hide file tree
Showing 4 changed files with 83 additions and 74 deletions.
46 changes: 22 additions & 24 deletions 3.1.Vuex源码结构说明.md
Original file line number Diff line number Diff line change
@@ -1,21 +1,15 @@
---
layout: post
title: 3.1.Vuex简要介绍和源码说明
category: 写教程
tag: vuex
exception:
readtime: 5
---

## vuex简介
* Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。
* 直白一点,vuex就是把组件的共享状态抽取出来,以一个全局单例模式管理的数据仓库,里面提供了对应用的数据存储和对数据的操作,维护整个应用的公共数据。
Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。

直白一点,vuex就是把组件的共享状态抽取出来,以一个全局单例模式管理的数据仓库,里面提供了对应用的数据存储和对数据的操作,维护整个应用的公共数据。

## vuex源码结构
* 我们使用git工具下载最新的vuex源码([https://github.com/vuejs/vuex.git](https://github.com/vuejs/vuex.git))。 目前最新的版本3.1.1,后面的代码分析都使用该版本。
* 我们先大致看一下vuex项目的目录结构:
我们使用git工具下载最新的vuex源码([https://github.com/vuejs/vuex.git](https://github.com/vuejs/vuex.git))。 目前最新的版本3.1.1,后面的代码分析都使用该版本。

我们先大致看一下vuex项目的目录结构:
![vuex 项目目录结构](images/vuex-3-1.png)
* 对目录的一些简要说明如下:

对目录的一些简要说明如下:
├ build // 编译配置目录
├ dist // 打包后的文件目录
├ docs // 文档源码目录
Expand All @@ -24,12 +18,15 @@ readtime: 5
├ src // 功能源码目录
├ test // 代码测试目录
└ types // types类型文件目录
* 其中我们重点要分析的就是src源码目录代码和examples示例目录代码。

其中我们重点要分析的就是src源码目录代码和examples示例目录代码。

## vuex解析要点
* 这里我们先看看vuex项目的src源码目录
这里我们先看看vuex项目的src源码目录
![vuex 项目src源码目录结构](images/vuex-3-2.png)
* 对目录文件的一些简要说明如下:

对目录文件的一些简要说明如下:

src
├ module //模块管理目录
│   ├ module-collection.js //创建组模块树
Expand All @@ -45,14 +42,15 @@ src
├ mixin.js //注入方法,将store挂载到vue实例
├ store.js //核心代码,创建store对象
└ util.js //工具类,deepClone、isPromise等函数
* 我们先大致了解下每个文件的作用说明。

我们先大致了解下每个文件的作用说明。

## 小结
使用过vuex的朋友肯定都知道,vuex的核心其实就是state、getter、mutation、action、module这五大部分。接下来,我们将主要围绕这些核心东西来解析vuex的源码。

## 参考
* [https://vuex.vuejs.org/zh/](https://vuex.vuejs.org/zh/)
* [https://segmentfault.com/a/1190000014363436](https://segmentfault.com/a/1190000014363436)
* [https://www.cnblogs.com/chenmeng2062/p/9337650.html](https://www.cnblogs.com/chenmeng2062/p/9337650.html)
* [https://blog.csdn.net/sinat_17775997/article/details/62231288](https://blog.csdn.net/sinat_17775997/article/details/62231288)
* [https://www.cnblogs.com/caizhenbo/p/7380200.html](https://www.cnblogs.com/caizhenbo/p/7380200.html)
## 相关
- [https://vuex.vuejs.org/zh/](https://vuex.vuejs.org/zh/)
- [https://segmentfault.com/a/1190000014363436](https://segmentfault.com/a/1190000014363436)
- [https://www.cnblogs.com/chenmeng2062/p/9337650.html](https://www.cnblogs.com/chenmeng2062/p/9337650.html)
- [https://blog.csdn.net/sinat_17775997/article/details/62231288](https://blog.csdn.net/sinat_17775997/article/details/62231288)
- [https://www.cnblogs.com/caizhenbo/p/7380200.html](https://www.cnblogs.com/caizhenbo/p/7380200.html)
47 changes: 26 additions & 21 deletions 3.2.Vuex安装使用分析.md
Original file line number Diff line number Diff line change
@@ -1,14 +1,5 @@
---
layout: post
title: 3.2.Vuex安装使用分析
category: 写教程
tag: vuex
exception:
readtime: 10
---

## 使用示例
* 我们先来看看vuex官方给的一个简单示例。
我们先来看看vuex官方给的一个简单示例。

```vuejs
import Vue from 'vue'
Expand Down Expand Up @@ -75,15 +66,20 @@ export default new Vuex.Store({
mutations
})
```
* 首先注册vuex插件

首先注册vuex插件

```vuejs
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
```
* 然后分别定义state、mutations、actions、getters,并导出
* 最后将vuex对象实例挂载到vue的每一个实例上去

然后分别定义state、mutations、actions、getters,并导出

最后将vuex对象实例挂载到vue的每一个实例上去

```vuejs
new Vue({
el: '#root',
Expand All @@ -94,8 +90,10 @@ new Vue({
```

## vuex的安装
* vuex作为vue的一个插件,在使用插件vue.use(vuex)时,会通过vuex对象中的install方法将vuex载入,现在我们来看看install方法的实现。
* install方法在store.js文件中
vuex作为vue的一个插件,在使用插件vue.use(vuex)时,会通过vuex对象中的install方法将vuex载入,现在我们来看看install方法的实现。

install方法在store.js文件中

```vuejs
export function install (_Vue) {
//这里使用全局变量Vue进行判断,避免重复安装
Expand All @@ -112,8 +110,12 @@ export function install (_Vue) {
applyMixin(Vue)
}
```
* 这里只是用全局变量做了下判断,避免重复安装,其重点还在最后调用的applyMixin(Vue)函数上
* 我们来看看applyMixin函数,在mixin.js文件中


这里只是用全局变量做了下判断,避免重复安装,其重点还在最后调用的applyMixin(Vue)函数上

我们来看看applyMixin函数,在mixin.js文件中

```vuejs
export default function (Vue) {
const version = Number(Vue.version.split('.')[0])
Expand Down Expand Up @@ -152,10 +154,13 @@ export default function (Vue) {
}
}
```
* 在安装注入函数时会判断vue的版本,2.x.x以上版本,使用 hook 的形式进行注入,而低于2.x.时使用封装并替换Vue对象原型的_init方法,实现注入。
* 注入的函数实现的功能其实就是当vuex设置到vue对象的$store属性上,便于在任意组件中执行 this.$store 都能找到装载的那个store对象。

在安装注入函数时会判断vue的版本,2.x.x以上版本,使用 hook 的形式进行注入,而低于2.x.时使用封装并替换Vue对象原型的_init方法,实现注入。

注入的函数实现的功能其实就是当vuex设置到vue对象的$store属性上,便于在任意组件中执行 this.$store 都能找到装载的那个store对象。
这里它会判断设置的是根组件还是非根组件,根组件时直接设置到this对象的$store属性上,子组件则从其父组件引用$store属性,层层嵌套进行设置。
* 到此,vuex的安装工作就已经完成了。

到此,vuex的安装工作就已经完成了。

## 小结
* 这一节我们只是简单的看了一下vuex的使用方式,顺便解析了下vuex安装注入到vue实例上的过程。下节我们将进入vuex的核心内容。
这一节我们只是简单的看了一下vuex的使用方式,顺便解析了下vuex安装注入到vue实例上的过程。下节我们将进入vuex的核心内容。
34 changes: 19 additions & 15 deletions 3.3.Vuex核心功能解析.md
Original file line number Diff line number Diff line change
@@ -1,15 +1,8 @@
---
layout: post
title: 3.3.Vuex核心功能解析
category: 写教程
tag: vuex
exception:
readtime: 18
---

## 认识Store构造函数
* 在使用vuex的时,我们实际上是实例化Store类,然后传入一个对象,这个对象包括我们定义好的state、getters、mutations、actions等,当然大点的项目还会有modules。
在使用vuex的时,我们实际上是实例化Store类,然后传入一个对象,这个对象包括我们定义好的state、getters、mutations、actions等,当然大点的项目还会有modules。

这一节我们就来看看Store类在实例化的时候做了哪些事情,首先看下构造函数的代码。

```vuejs
constructor (options = {}) {
// Auto install if it is not done yet and `window` has `Vue`.
Expand Down Expand Up @@ -81,15 +74,19 @@ readtime: 18
}
}
```
* 我们来简要理一理这个构造过程,先是使用assert断言一下运行环境,然后初始化需要用到的变量,再就是模块安装installModule和组件初始化resetStoreVM,最后注册和使用插件。下面我们一一展开。
* 先是assert断言运行环境,这是一种很好的写法。assert函数在util.js文件中。这里断言必须先调用Vue.use(Vuex)。必须提供Promise,应该是为了让Vuex的体积更小,
让开发者自行提供Promise的polyfill,一般我们可以使用babel-runtime或者babel-polyfill引入。最后断言必须使用new操作符调用Store函数。

我们来简要理一理这个构造过程,先是使用assert断言一下运行环境,然后初始化需要用到的变量,再就是模块安装installModule和组件初始化resetStoreVM,最后注册和使用插件。下面我们一一展开。

先是assert断言运行环境,这是一种很好的写法。assert函数在util.js文件中。这里断言必须先调用Vue.use(Vuex)。必须提供Promise,应该是为了让Vuex的体积更小,让开发者自行提供Promise的polyfill,一般我们可以使用babel-runtime或者babel-polyfill引入。最后断言必须使用new操作符调用Store函数。

```vuejs
assert(Vue, `must call Vue.use(Vuex) before creating a store instance.`)
assert(typeof Promise !== 'undefined', `vuex requires a Promise polyfill in this browser.`)
assert(this instanceof Store, `store must be called with the new operator.`)
```
* 然后是一些内部变量的初始化,下面我们先对这些变量的作用进行下说明便于后面的理解。

然后是一些内部变量的初始化,下面我们先对这些变量的作用进行下说明便于后面的理解。

```vuejs
this._committing = false
this._actions = Object.create(null)
Expand All @@ -101,8 +98,10 @@ readtime: 18
this._subscribers = []
this._watcherVM = new Vue()
```

_committing提交状态的标志,在_withCommit中,当使用mutation时,会先赋值为true,再执行mutation,修改state后再赋值为false,
在这个过程中,会用watch监听state的变化时是否_committing为true,从而保证只能通过mutation来修改state

_actions用于保存所有action,里面会先包装一次
_actionSubscribers用于保存订阅action的回调
_mutations用于保存所有的mutation,里面会先包装一次
Expand All @@ -111,7 +110,9 @@ _modules用于保存一棵module树
_modulesNamespaceMap用于保存namespaced的模块
_subscribers用于存储所有对 mutation 变化的订阅者
_watcherVM Vue对象的实例,主要是利用 Vue 实例方法 $watch 来观测变化的

这里提一下this._modules = new ModuleCollection(options)对模块module进行收集的逻辑。实际上是调用了module目录下的ModuleCollection类和Module类。

```vuejs
//module目录下的moduleCollection.js
export default class ModuleCollection {
Expand All @@ -138,8 +139,11 @@ export default class Module {
/** 省略 **/
}
```

这里调用ModuleCollection构造函数,首先进行根module的注册,然后递归遍历所有的module,子module 添加到父module的_children属性上,最终形成一棵树。
* 再就是模块安装installModule函数,这里是module的处理核心,包括处理根module、命名空间、action、mutation、getters和递归注册子module。

再就是模块安装installModule函数,这里是module的处理核心,包括处理根module、命名空间、action、mutation、getters和递归注册子module。

```vuejs
function installModule (store, rootState, path, module, hot) {
const isRoot = !path.length
Expand Down
30 changes: 16 additions & 14 deletions 3.4.Vuex中辅助函数实现说明.md
Original file line number Diff line number Diff line change
@@ -1,12 +1,3 @@
---
layout: post
title: 3.4.Vuex中辅助函数实现说明
category: 写教程
tag: vuex
exception:
readtime: 15
---

## 方便的组件api
为了避免每次都需要通过this.$store来调用api,vuex提供了mapState、mapMutations、mapGetters mapActions、createNamespacedHelpers 等api,
可以很方便的在vue组件中进行使用,下面看看具体的使用方式。
Expand Down Expand Up @@ -75,7 +66,8 @@ export default {
* 注:`...用的是es6中的对象展开式写法。

## 组件api实现解析
* 这几个组件api放在helpers.js文件中,我们先来看看mapState函数的源码。
这几个组件api放在helpers.js文件中,我们先来看看mapState函数的源码。

```vuejs
//normalizeNamespace是进行参数处理,如果存在namespace便加上命名空间
export const mapState = normalizeNamespace((namespace, states) => {
Expand Down Expand Up @@ -106,7 +98,9 @@ export const mapState = normalizeNamespace((namespace, states) => {
return res
})
```

这里我们回顾一下上面mapState的用法:

```vuejs
import { mapState } from 'vuex'
export default {
Expand All @@ -119,7 +113,9 @@ export default {
/** 省略 **/
}
```

经过 mapState 函数处理后的结果:

```vuejs
import { mapState } from 'vuex'
export default {
Expand All @@ -131,9 +127,12 @@ export default {
/** 省略 **/
}
```
* 对比上面的mapState的用例和经过mapState函数处理后的结果,就可以很清楚的知道mapState函数就是在每个state包装一下,使原本需要在this.$store上取state的写法

对比上面的mapState的用例和经过mapState函数处理后的结果,就可以很清楚的知道mapState函数就是在每个state包装一下,使原本需要在this.$store上取state的写法
变成 state => stat.cart 的写法而已。
* 同样的我们再来对比下mapGetters的写法和处理结果

同样的我们再来对比下mapGetters的写法和处理结果

```vuejs
//使用mapGetters的写法
import { mapGetters } from 'vuex'
Expand Down Expand Up @@ -164,9 +163,12 @@ export default {
/** 省略 **/
}
```

好,现在是彻底明白了,mapState、mapGetters函数其实就相当于一种语法糖,将原本需要this.$store来获取数据的写法变成另外一种更加简易的写法。

mapActions、mapMutations的功能大同小异,这里就不再赘写了。

## 小结
* 本节主要就讲mapState、mapMutations、mapGetters、mapActions这几个函数功能的实现,当然还有其他一些辅助函数或工具函数就留下自己去看吧。
* 到此,vuex的源码解析就结束吧。不难,我们再整理下要点:
本节主要就讲mapState、mapMutations、mapGetters、mapActions这几个函数功能的实现,当然还有其他一些辅助函数或工具函数就留下自己去看吧。

到此,vuex的源码解析就结束吧。不难,我们再整理下要点:

0 comments on commit 056d02c

Please sign in to comment.