Vuex

3/16/2022 vue

# 1. 聊聊Vuex是什么?

查看答案
  • Vuex 是专门为 Vue.js 设计的状态管理库,从使用的角度其实就是一个Js库
  • 它采用集中式的方式存储需要共享的数据,还提供了模块的机制
  • 它的作用是进行状态管理,解决复杂组件通信,数据共享
  • Vuex 也集成到 Vue 的官方调试工具 devtools extension,提供了时光旅行、历史回滚、状态快照、导入导出等高级调试功能
  • 一般适用于中型较大的单页应用程序,可以帮我们解决多个视图依赖同一状态,不同视图的行为变更同一状态的问题。

# 2. Redux 和 Vuex 的设计思想

查看答案

不管是 Vue,还是 React,都需要管理状态(state),比如组件之间都有共享状态的需要。
父子组件之间,兄弟组件之间共享状态,往往需要写很多没有必要的代码。
对于状态管理的解决思路就是:把组件之间需要共享的状态抽取出来,遵循特定的约定,统一来管理,让状态的变化可以预测。

# 3. Vuex核心概念

说说Store,State,Getter,Mutation,Actions,Module都是什么?

查看答案
  • Store:仓库,Store是使用Vuex应用程序的核心,每个应用仅有一个Store。
  • State:状态,保存在Store中,State也是唯一的,称为单一状态树,这里的状态是响应式的。
  • Getter:相当于Vuex中的计算属性,它内部可以对计算的结果进行缓存,只有当依赖的状态发生改变的时候,才会重新计算。
  • Mutation:状态的变化必须要通过提交Mutation来完成。
  • Actions:与Mutation类似,不同的是可以进行异步的操作,内部改变状态的时候都需要改变Mutation。
  • Module:模块,应用变得很复杂的时候,Vuex允许我们将Store分割成模块,每个模块拥有自己的State,Mutation,Actions,Getter,甚至是嵌套的子模块。

# 4. 不在组件中直接修改状态而是通过调用store的actions来修改的好处是?

查看答案

这样可以记录store中state的变更,还可以根据变更记录实现更高级的时光旅行和历史回滚功能以便于调试。

# 5. 说说actions和mutations的实现机制和区别是什么?

查看答案

一般来说,我们标准是将同步修改的代码用mutations实现,将异步修改的代码用actions实现。
但是其底层原理上:mutations里面也是可以写异步代码的。

  • mutations是通过commit触发的,源码中没有限制异步操作。

  • 每次commit执行完mutations中回调的时候,控制浏览器插件进行跟踪,如果你浏览器没有Vue的插件,devtool是一个undefined,如果你在mutation中使用了异步操作,devtool的钩子会先于mutations回调执行,导致无法捕捉store的变化。

  • 本身不使用浏览器进行调试的时候,mutations里是可以包含异步操作的,只是影响使用插件来定位bug。

  • 而actions中,返回了一个promise对象,本质是执行了代码最后还是用commit提交内容。只是把commit里,devtool和mutations的回调同步处理了。

# 6. 为什么 Vuex 的 mutation 和 Redux 的 reducer 中 不能做异步操作?

查看答案

每次commit执行完mutations中回调的时候,控制浏览器插件进行跟踪,如果你浏览器没有Vue的插件,devtool是一个undefined,如果你在mutation中使用了异步操作,devtool的钩子会先于mutations回调执行,导致无法捕捉store的变化。

我们如果正在debug一个app并观察devtool中的mutation日志,每一条 mutation 被记录,devtools 都需要捕捉到前一状态和后一状态的快照。而异步操作的回调函数无法在mutation触发的时候立马拿到结果,所以改变不可追踪。

# 7. Vuex开启严格模式之后有什么不同?

查看答案
  1. 如果没有使用mutations修改state,会直接报错
  2. 建议在开发模式下开启严格模式,在生产环境下关闭,因为严格模式会深度检测状态树,会影响性能。

# 8. 怎么实现一个简单的Vuex?

查看答案
  1. 搭建框架模块,模块需要导出一个install方法和Store类
  2. 实现install函数
    • 接收Vue构造函数作为参数
    • 通过Vue.mixin函数中的beforeCreate拿到Vue的实例
    • 判断Vue实例的options中是否有store,如果有就给Vue的原型上挂载$store
  3. 实现Store类
    • constructor
      • 接受一个参数options,解构出来state,getters,mutations,actions
      • 将state进行响应式处理(_Vue.observable)
      • 定义一个this.getters的空对象,遍历所有的getters的key,用Object.defineProperty将key注册到this.getters上,修改每一个get属性,将getters的值返回
      • 定义this._mutations为Store的私有属性
      • 定义this._actions为Store的私有属性
    • commit
      • 接受两个参数,一个是方法名称type,一个是方法参数payload
      • 通过方法名称,在this._mutations中找到对应的方法,传入this.state和参数并调用
    • dispatch
      • 接受两个参数,一个是方法名称type,一个是方法参数payload
      • 通过方法名称,在this._actions中找到对应的方法,传入context和参数并调用
具体实现代码
let _Vue = null
class Store {
  // 构造函数接收一个参数是对象
  constructor (options) {
    // 这里对对象进行解构,并且赋默认值为空对象,避免没有传当前属性
    const {
      state = {},
      getters = {},
      mutations = {},
      actions = {}
    } = options
    // 将state属性进行响应式处理
    this.state = _Vue.observable(state)
    // 对getters属性进行处理
    // getters是一个对象,对象中有一些方法,这些方法都需要接收state参数,并且最终都有返回值,这些方法都是获取值,所以可以使用Object.defineProperty将这些方法转换成get访问器
    // 1. 先定义一个this.getters让外部可以直接访问,然后初始化成一个没有原型对象的空对象
    this.getters = Object.create(null)
    // 2. 遍历所有的getters的key,把对应的key注册到this.getters对象中,定义一个get属性,返回key对应的getters中方法的执行结果,并传入state
    Object.keys(getters).forEach(key => {
      Object.defineProperty(this.getters, key, {
        get: () => getters[key](state)
      })
    })
    // 内部属性是私有属性,标识下划线_,不希望外部访问
    // 对mutations属性进行处理
    this._mutations = mutations
    // 对actions属性进行处理
    this._actions = actions
  }

  // 在commit方法中获取_mutations
  // 接收两个参数,第一个参数是type,方法名称,第二个参数是payLoad,调用方法的参数
  commit (type, payload) {
    //通过type找到this._mutations中的方法并调用,传入参数payload
    this._mutations[type](this.state, payload)
  }

  // 在dispatch方法中获取_actions
  // 实现方式和commit一样
  dispatch (type, payload) {
    // 第一个参数是context,这里简单模拟就传入this,这个里面就有我们需要的state,commit等
    // 第二个参数是payload
    this._actions[type](this, payload)
  }
}

// install接收一个参数,Vue构造函数,后面在Store类中还要使用构造函数,所以在全局定义一个_Vue
function install (Vue) {
  _Vue = Vue
  // 1. 创建Vue实例传入的store对象注入到Vue原型上的$store,在所有组件中用this.$store都可以获取到Vuex的仓库,从而共享状态
  // 2. 这里我们获取不到Vue的实例,所以这里通过混入beforeCreate来获取Vue实例,从而拿到选项中的store对象
  _Vue.mixin({
    beforeCreate () {
      // 这里的this就是Vue的实例
      // 首先判断当前Vue的实例的options中是否有store,当创建根实例的时候,会把store注入到Vue的实例上,如果是组件实例,并没有store选项就不需要做这件事情
      if (this.$options.store) {
        // 给Vue的原型上挂载$store
        _Vue.prototype.$store = this.$options.store
      }
    }
  })
}

export default {
  Store,
  install
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67

# 9. Vuex插件 TODO

更新时间: 2022-03-30 00:38