让我们实现一个简单的Vuex吧!
「这是我参与2022首次更文挑战的第27天,活动详情查看:2022首次更文挑战」
模拟Vuex
我们用了这么久的Vuex
感觉是不是很好用,但是学会了他的用法,我们也要学学怎么模拟一个简单版本的Vuex
毕竟授人以鱼不如授人以渔。
在进行模拟之前,先让我们回忆一下Vuex
的基础概念
- Vuex 回顾 忘记的同学可以点一下,看一下哈(求赞的眼神)
好了,回顾完毕之后,我们正式进入 Vuex 模拟的阶段
基本结构
Vue.js 的插件要求
首先我们先看看
import Vue from 'vue'
import Vuex from '../myvuex'
Vue.use(Vuex)
这里的Vue.use(Vuex)
是我们需要重点注意到的,之前我们在路由注册 篇章中也有提到
- 传送门:简单版本的VueRouter手写-插件的注册(有你不知道的小知识哦!) 小声逼逼,看官能不能点个赞呢?
Vue.js 的插件应该暴露一个
install
方法。这个方法的第一个参数是Vue
构造器,第二个参数是一个可选的选项对象
Vuex.Store 什么
接着有如下这么一句话,那么由此我们可以得出Store
其实是一个类,那么就很明朗了
export default new Vuex.Store({
...
})
我们需要实现一个install
方法、一个类(Store
)包含以下的字段属性:
- state
- getters
- mutations
- actions
Vuex 如何挂载呢?
Vuex 如何挂载,这是一个问题,但是不要慌,且听我徐徐道来
案例
<template>
<div id="app">
<h1>Vuex - Demo</h1>
count:{{ $store.state.count }} <br>
msg: {{ $store.state.msg }}
<h2>Getter</h2>
reverseMsg: {{ $store.getters.reverseMsg }}
<h2>Mutation</h2>
<button @click="$store.commit('increate', 2)">Mutation</button>
<h2>Action</h2>
<button @click="$store.dispatch('increateAsync', 5)">Action</button>
</div>
</template>
解析
首先我们查看如下的案例,大家会发现,不管我们是调用
- state
- getters
- mutations
- actions
都是通过
$store
.xxx 属性来进行调用,那么我们就需要在注册Vuex
插件的时候,将Store
类注入到Vue
实例的$store
属性上,这样就能实现所有的组件都可以通过this.$store
来进行访问我们的Vuex
参数,从而可以在所有的组件中共享状态。
接下来要进行 手写Vuex
的 install
、store
部分方法
install 方法
install
是一个对于一个Vue
插件,都需要具备的一个方法。在我们实现Vuex
的过程中,我们需要在此方法中,需要将Vue
实例保存起来,方便我们之后的调用。然后我们需要将store
赋值给Vue.prototype.$store
。
如何保存Vue
实例?
在vue
的官方文档中也很明确的说到:
Vue.js 的插件应该暴露一个
install
方法。这个方法的第一个参数是Vue
构造器,第二个参数是一个可选的选项对象 那么我们只需要如此操作即可:
let _Vue = null
function install (Vue) {
_Vue = Vue
}
将store
赋值给Vue.prototype.$store
在将store
赋值给Vue.prototype.$store
后,项目中的所有组件都可以通过this.$store
来获取Vuex
下面的仓库,从而可以在所有组件中共享状态。
我们可以通过混入beforeCreate
来获取Vue
实例,获取选项中的Vue
对象
function install (Vue) {
_Vue = Vue
_Vue.mixin({
beforeCreate () {
// 如果已经注册了,就不需要再次进行注册了
if (this.$options.store) {
_Vue.prototype.$store = this.$options.store
}
}
})
}
到这里 install
方法就结束了
Store 类
需要包含的内容
- state - 响应式的
- getters
- mutations
- actions
- commit 提交 mutations
- dispatch 分发 actions
1. 需要一个构造函数获得参数
构造函数中,需要初始化时的参数
```js
constructor (options) {
const {
state = {},
getters = {},
mutations = {},
actions = {}
} = options
}
```
2. 将state
转换为响应式
然后我们需要将`state`转换为响应式的对象,我们需要调用`observable`对数据进行响应式处理:
```js
this.state = _Vue.observable(state)
```
3. 处理getters
`getters`是一个对象,对象中有一些方法,这些方法都需要接受`state`参数,并且最终都有一个返回值。一般情况下就是对`state`做一些处理后返回,而只有当我们访问了`getters`方法时,才会有有值返回,所以我们可以用`Object.defineProperty`转换`get`
```js
this.getters = Object.create(null)
Object.keys(getters).forEach(key => {
Object.defineProperty(this.getters, key, {
get: () => getters[key](state)
})
})
```
4. mutations、actions 只需要存储到对应的方法中在commit
、dispatch
中获取就好
如此命名主要是不希望外部可以随便访问
this._mutations = mutations
this._actions = actions
5. 实现commit
、dispatch
commit (type, payload) {
this._mutations[type](this.state, payload)
}
dispatch (type, payload) {
this._actions[type](this, payload)
}
总结
最后我们来看几个问题。
问题1
问:使用Vuex只需执行
Vue.use(Vuex)
,并在Vue的配置中传入一个store对象的示例,store是如何实现注入的?
答:
Vue.use(Vuex)
方法执行的是install方法,它实现了Vue实例对象的init方法封装和注入,使传入的store对象被设置到Vue上下文环境的store中。因此在VueComponent任意地方都能够通过‘this.store中。因此在Vue Component任意地方都能够通过`this.store中。因此在VueComponent任意地方都能够通过‘this.store`访问到该store。
问题2
问:在执行dispatch触发action(commit同理)的时候,只需传入(type, payload),action执行函数中第一个参数store从哪里获取的?
答:store初始化时,所有配置的action和mutation以及getters均被封装过。在执行如
dispatch('submitOrder', payload)
的时候,actions中type为submitOrder的所有处理方法都是被封装后的,其第一个参数为当前的store对象,所以能够获取到{ dispatch, commit, state, rootState }
等数据。
结尾
源码中还有一些工具函数,如有兴趣可以打开源码看看,这里不再细述。
我们本章进行模拟的主要目的还是,了解Vuex
的工作基本流程、和一些简单的设计思路。结束!
转载自:https://juejin.cn/post/7067908911793340429