咱们都知道在用 vue 的时候,简单的父子通讯和 EventBus 已经不能知足咱们的要求了,嵌套层级过多和难以追踪改变是两个较为主要的问题😵,这个时候能够用 vuex 来解决,想必你们都用过,因此今天跟你们分享的是 vuex 的简单实现,真的是超简单,就几行代码(文章结尾有连接),带你领略 vuex 的精髓,而且在最后会有几个问题答疑(好比时光穿梭、本地持久化等)帮你们巩固一下 vuex。javascript
vuex 是基于 vue 的状态管理工具,通俗点讲讲就是变量共享,你要知道 this.$store
本质是个对象,你们能够看下下面这张图,看看 this.$store
究竟是个啥(只看有标号的行便可)👇: css
import Vue from 'vue';
import Vuex from 'vuex';
Vue.use(Vuex); // 这是插件固定写法,没有什么为何,官网写的很清楚,这样写 vue 会自动调用插件的 install 方法
const store = new Vuex.Store({
state: {...},
mutations: {...},
actions: {...},
getters: {...},
})
new Vue({
el: '#app',
store
})
复制代码
很显然,Store 里面大体就传入四个参数,目前咱们也只须要这些参数就够了,四个参数又能够分为两类,一类是获取数据,一类是修改数据,就像下面这张图画的同样👇: html
$store.state
和
$store.getters
,组件要修改数据能够经过
$store.actions
和
$store.mutations
(固然最终都是 mutations,这也使得便于追踪状态的改变),如此一来,造成了闭环,也符合咱们所说的单向数据流的思想,只有一个地方能改数据,不能遍地开花,否则就乱套了。另外 store 也是个单例模式的例子,全部的组件都共用一个全局的 store,每一个组件的
$store
都是同一个东西,这点也很好理解。
有了上面这些概念,接下来咱们就简单用几行代码来简单实现一下吧。首先先写个简单的小框架,就像下面这样(官网上有说明插件的编写方式):vue
<script src="https://cdn.bootcss.com/vue/2.6.11/vue.js"></script>
<script> const Vuex = {} Vuex.install = function (Vue) { // 这个 Vue 是 vue 提供给咱们的 console.log('install 方法开始执行') } Vue.use(Vuex) </script>
复制代码
ok,而后咱们在 install 里面写 vuex 的代码便可,如今咱们只须要声明一个 store 对象而且赋予 store 两个属性(这里以 state 和 mutations 举例),这个我用很简单的代码演示一下你们就清楚了👇:java
Vuex.install = function (Vue) {
console.log('install 方法开始执行')
const store = {} // 声明一个对象
store.state = new Vue({ // 赋予 state 属性,用来获取值
data () {
return {
msg: '哈哈'
}
}
})
store.mutations = { // 赋予 mutations 属性,用来修改值
SETMSG(value) {
store.state.msg = value
}
}
}
复制代码
上面的代码中要注意的是 state 实际上是利用了 vue 的响应式原理,使用了 new Vue()
,由此 state 就变成响应式的了,这是 vue 的特性。注意这里单纯的使用 Object.defineProperty
来定义 state 是不行的,是没法更新视图的,由于它并无被 vue 进行依赖收集,因此说 vuex 是强依赖于 vue 的。还有就是一开始咱们也要把 state 的数据写全,否则后面添加的也是无响应更新的,你们若是有用到过 $set
应该能体会到为何有时候数据变了,视图没更新那种感受😬。git
👌,如今咱们已经有了 store,接下来就是把它挂到每一个组件下面了,这里咱们使用 Vue.mixin 的混入方式,代码以下:github
Vue.mixin({ // 也是固定写法:每一个组件都会执行下面这个生命周期
beforeCreate () {
this.$store = store // 因而每一个组件都会有 this.$store,而且都指向同一个 store
}
})
复制代码
固然用 Vue.prototype.$store = store
也能达到一样的效果,事实上,vue-router 也是一样的方式咱们才能在每一个实例中用 this.$router 来调用。另外若是要说 mixin 和 prototype 这两种挂载方式的区别,我就想到两小点😯:vue-router
不知道你们还知道其余缘由吗,欢迎在下面留言。vuex
好了,至此,咱们大概就写完了一个简单的 demo,如今写个例子来测试一下:api
let v1 = new Vue({
el: '#component1',
computed: {
data() {
return this.$store.state.msg
}
}
})
let v2 = new Vue({
el: '#component2',
methods: {
change() {
this.$store.mutations.SETMSG('这是 mutations 触发的值')
}
}
})
console.log(v1, v2)
复制代码
下面是测试的结果:
Vuex.install = function (Vue) {
...
store.commit = function(mutationName, value) {
store.mutations[mutationName] && store.mutations[mutationName](value)
}
...
}
...
let v2 = new Vue({
el: '#component2',
methods: {
change() { // 改一下调用方式,结果是同样的
// this.$store.mutations.SETMSG('这是 mutations 触发的值')
this.$store.commit('SETMSG', '这是 mutations 触发的值')
}
}
})
复制代码
最终效果是同样的,这里就不展现了。 深吸一口气,目前为止咱们已经实现超简版的 vuex,接下来是几个问题答疑🤔。
这个东西其实和咱们平时写的计算属性一毛同样,state 和 getters 的关系比如 data 和 computed,你们细品一下。
咱们知道 vuex 中修改 state 就一个通道,就是执行 commit,但其实你不经过 commit 也是能改的,那怎么知道它是经过 commit 修改的呢?就是在执行 commit 的时候加个标志位 _committing
,执行 commit 的时候将 _committing
设置为 true,_committing
为 true 才能修改 state,而其余方式修改的 state 并不会修改 _committing
标志位,这样一来就能判断是否是经过 commit 修改的。 若是你在 vuex 中打开了严格模式,任何非 mutation 更改都会抛出错误。
这一类辅助函数本质就是语法糖,这里咱们以 mapState 举例子,咱们回顾一下用法:
import {mapState} from 'vuex'
export default{
computed:{
...mapState(['msg','user'])
}
}
复制代码
而后咱们在页面中就能用 this.msg 访问,其实调用的仍是 this.$store.state.msg,只不过写起来简单点。下面咱们看下怎么简单实现,很显然,这里 mapState 是一个函数,接收一个数组:
function mapState (list) {
let obj = {}
list.forEach(stateName => {
obj[stateName] = () => this.$store.state[stateName]
})
return obj
}
复制代码
👏是的,就这么点代码,其余 map 辅助函数也是也同样的道理。
这是 devtoolPlugin 提供的功能,由于开发的时候全部 state 的改变都有记录,“时光穿梭”的功能其实就是可以让咱们回到或去到某一状态,实际上就是将当前的 state 替换为记录中的某个 state,咱们看下 vuex 的 store 中就为咱们提供了一个这样的函数 replaceState(真的是直接替换😂),具体代码以下👇:
replaceState (state) {
this._withCommit(() => { // 这里面就是上面说到的 _commiting 标识
this._vm.state = state
})
}
复制代码
下面是两个个小截图,但愿可以帮助你理解:
若是硬要说 vuex 中最核心的一个点的话,那就是利用了 vue 的响应式,我想这是最为主要的。最后但愿本篇文章可以对你有所帮助,不知道写的清不清楚😁,也祝你们百毒不侵,开开心心上班,回见👋。
ps: 最简版 vuex 代码地址、精装版 vuex 代码地址