likes
comments
collection
share

让我们实现一个简单的Vuex吧!

作者站长头像
站长
· 阅读数 43

「这是我参与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)是我们需要重点注意到的,之前我们在路由注册 篇章中也有提到

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参数,从而可以在所有的组件中共享状态。

接下来要进行 手写Vuexinstallstore部分方法

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 只需要存储到对应的方法中在commitdispatch中获取就好

如此命名主要是不希望外部可以随便访问

this._mutations = mutations
this._actions = actions

5. 实现commitdispatch

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.storeVueComponentthis.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的工作基本流程、和一些简单的设计思路。结束!

让我们实现一个简单的Vuex吧!

转载自:https://juejin.cn/post/7067908911793340429
评论
请登录