3-7、Vue 状态管理-核心源码讲解

状态管理:本质对一个[全局唯一、具有响应式]变量的管理
因为是全局的,那为了流转/使用上的不混乱/冲突等,所以会对其制定流转规则,让变化变得可预测

Vuex

状态流转:actions => mutations => state
state:数据的存储
mutations:唯一的对数据的[同步]操作(增删改查)入口,可以做简单的业务处理
actions:复杂业务逻辑处理(axios 等)的入口,并只能调用 mutations

Vuex 3.x

官方文档:https://v3.vuex.vuejs.org/zh/
使用:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex)

const store = new Vuex.Store({
state: {
count: 0
},
mutations: {
increment (state) {
state.count++
}
}
})

原理

  • 其中的Store本质是类,所以才new Vuex.Store({ /*...*/ })
  • 通过Vue.use(Vuex),调用Vuexinstall方法
    • install方法里面写了Vue.mixin({ beforeCreate: vuexInit })
      • 所以Vuex的注入是通过Vue.mixin()混入beforeCreate方法实现
    • 并且在vuexInit内,定义了this.$store,实现$store的挂载
  • 通过调用new Vue来实现Vuex的响应式,并且this.$store.state的获取是通过get实现的
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
export class Store {
constructor (options = {}) {
// ......
resetStoreVM(this, state)
},

// ......

get state () {
return this._vm._data.$$state
}
// ......
}

function resetStoreVM(store, state)
// ......

// ⭐️ 实现 Vuex 的响应式
store._vm = new Vue({
data: {
$$state: state
},
computed
}

// ......
})

Vuex 4.x

官方文档:https://vuex.vuejs.org/zh/
使用:

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
import { createApp } from 'vue'
import { createStore } from 'vuex'
/*
function createStore (options) {
return new Store(options)
}
*/

// 创建一个新的 store 实例
const store = createStore({
state () {
return {
count: 0
}
},
mutations: {
increment (state) {
state.count++
}
}
})

const app = createApp({ /* 根组件 */ })

// 将 store 实例作为插件安装
app.use(store)

原理

  • 引入的createStore本质是个生成实例的函数
  • 通过app.use(store),调用store 实例install方法
    • install方法里面写了vue.provide(storeKey, this)
      • 所以Vuex的注入是通过vue.provide() 实现的
      • 并且还定义了vue.config.globalProperties.$store = this,实现$store的挂载
  • 通过借用Vuereactive实现Vuex的响应式,并且this.$store.state的获取是通过get实现的
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
import { reactive } from 'vue'

export class Store {
constructor (options = {}) {
// ......
resetStoreState(this, state)
},

install (vue, injectKey) {
vue.provide(injectKey || storeKey, this)
vue.config.globalProperties.$store = this
}

// ......

get state () {
return this._state.data
}
// ......
}

function resetStoreState(store, state)
// ......

// ⭐️ 实现 Vuex 的响应式
store._state = reactive({
data: state
})

// ......
})

Pinia

官方文档:https://pinia.vuejs.org/zh/introduction.html

原理

跟 Vuex 4.x 差不多的
使用了vueprovide/injectvue.config.globalProperties.$storereactive

其他补充

观察者 & 发布订阅

观察者:不存在一个中心,事件流:A => B
A 干了 xxx,B 观察到了,就直接再行动。
Vue 就是观察者模式,通过拦截数据的读取操作,收集依赖;拦截数据的设置操作,触发依赖

发布订阅:存在一个中心,事件流为:A => 中心 => B
A 干了 xxx,然后通知事件中心,事件中心再去通知对应的订阅者(B),B 再行动
EventBus 就是发布订阅

面试题

手写发布订阅(简单版)

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
class EventBus {
constructor() {
this.dep = new Map();
}

on(name, fn) {
let events = this.dep.get(name);

if (!events) events = new Set();

events.add(fn);

this.dep.set(name, events);
}

emit(name, payload) {
let events = this.dep.get(name);

if (!events) {
throw new Error(`${name} is not registered`);
}

events.forEach((cb) => cb(payload));
}
}

const eb = new EventBus();

eb.on("log", (value) => {
console.log("[ log value ] >", value);
});

setTimeout(() => {
eb.emit("log", "hello");
}, 5000);

3-7、Vue 状态管理-核心源码讲解
https://mrhzq.github.io/职业上一二事/前端面试/前端八股文/3-7、Vue 状态管理-核心源码讲解/
作者
黄智强
发布于
2024年1月13日
许可协议