状态管理
简述
- 多个组件,多个模块之间共享状态是最常见的开发述求,场景之多不胜枚举,例如全局用户状态,修改用户信息全局响应变化等等。
常见的解决方案
- 简单方案基于事件监听机制利用回调传参,多处订阅实现数据的流转。例如官方推荐的mitt事件库。优势简单的数据事件通信是能满足的,劣势随着数据复杂性变动,回调写法的代码阅读性,整体使用体验下降,使用方式也非常简单,具体实现如下:
//定义组合api事件流
const $emitter = mitt()
/**
* @name: useOnChange
* @msg: 监听事件emit
*/
export function useOnChange<T extends Function>(fun: T) {
$emitter.on(EventsEnum.CHANGE, itemMessage => {
fun(itemMessage)
})
}
/**
* @name: useChange
* @msg: 触发事件emit
* @param {*}
*/
export function useChange(itemMessage: number) {
$emitter.emit(EventsEnum.CHANGE, itemMessage)
}
复制
// A组件中触发事件发送数据
export default defineComponent({
name: 'A',
setup() {
//组件A中发送数据
const handlerClick = (item)=>{
//使用组合api发送数据
useChange(1)
}
}
})
// B组件中监听事件获取数据
export default defineComponent({
name: 'B',
setup() {
//回调中获取数据
useOnChange((mes)=>{
console.log(mes)
})
}
})
// C 组件中监听事件获取数据
export default defineComponent({
name: 'C',
setup() {
//回调中获取数据
useOnChange((mes)=>{
console.log(mes)
})
}
})
复制
- 基于vue3的响应式官方一些简单的实践
const store = {
debug: true,
state: Vue.reactive({
message: 'Hello!'
}),
setMessageAction(newValue) {
if (this.debug) {
console.log('setMessageAction triggered with', newValue)
}
this.state.message = newValue
},
clearMessageAction() {
if (this.debug) {
console.log('clearMessageAction triggered')
}
this.state.message = ''
}
}
复制
const appA = Vue.createApp({
data() {
return {
privateState: {},
sharedState: store.state
}
},
mounted() {
store.setMessageAction('Goodbye!')
}
}).mount('#app-a')
const appB = Vue.createApp({
data() {
return {
privateState: {},
sharedState: store.state
}
}
}).mount('#app-b')
复制
- 知名状态管理库Redux,Flux,Vuex,这些都是非常优秀的第三方库
为什么明明有vuex你还在折腾啥?
- 先抛出一个问题大家用了element那么久,请问知道this.$message是怎么实现的吗?如果什么都不管,项目来了上去就是一套全家桶,做一个项目和做十个项目有什么区别?既然新的机会来了为什么自己写一下vue3的组件,vue3的状态管理?
- 业务与场景在项目初期比较简单,没有记录变更、保存状态快照、历史回滚/时光旅行的诉求,那为什么不自己做一个状态管理呢?
- 核心实现功能:状态修改单项数据流,状态改变全局数据响应,代码约定,思考一下怎么解决这三个问题?
实现思路
- 单项数据流,Readonly
- 状态改变数据响应,组合api和响应式
- 代码约束 使用ts 进行接口约定
其他大神的一些实现
- 利用provide
- 还有一些基于reactive等等一些想法
站在巨人的肩膀上
- 基于一些大神是vue3封装reduer思路自己也去做了实现
- 基础实现
/*
* @Description:Reducer
* @version: 1.0.0
* @Author: 吴文周
* @Date: 2021-02-26 13:45:46
* @LastEditors: 吴文周
* @LastEditTime: 2021-03-02 14:52:33
*/
import { readonly, ref } from 'vue'
// 全局缓存
const map = new WeakMap()
export function useModel(hook: Function) {
if (!map.get(hook)) {
const ans = hook()
map.set(hook, ans)
}
return map.get(hook)
}
export function useReducer(reducer: Function, initialState = {}) {
const state = ref(initialState)
const dispatch = <T>(action: T) => {
state.value = reducer(action, state.value)
}
return {
state: readonly(state),
dispatch,
}
}
export function useStore(reducer: Function, initialState?: any) {
return useReducer(reducer, initialState)
}
复制
2.实现小型reduer
/*
* @Description:xxx全局状态
* @version: 1.0.0
* @Author: 吴文周
* @Date: 2021-02-26 13:53:09
* @LastEditors: 吴文周
* @LastEditTime: 2021-03-20 16:10:59
*/
import { Ref } from 'vue'
import { useModel, useReducer } from './reducer'
// 状态接口
export interface State {
oo: string,
xx: string,
cc: string,
}
// 行为接口
export interface Action {
type: 'changeOO' | 'changeXX' | 'changeCC'//指定action
payload: State
}
// 组合函数使用是 状态接口
export type StateType = Readonly<Ref<State>>
// 使用实例接口
interface Redux {
state: StateType
// 这里不是注释,只是这样的语法mark当不识别,保证优雅性,实际使用时放开注释
//dispatch: <T extends Action>(action: T) => void
}
// 状态变更
function reducer(action: Action, state: State) {
switch (action.type) {
case 'changeOO':
state.oo = action.payload.oo
break
case 'changeXX':
state.xx = action.payload.xx
break
case 'changeCC':
state.cc = action.payload.cc
break
}
return { ...state }
}
// 初始化状态
function useStore() {
const initialState = {
oo: 'oo',
xx: 'xx',
cc: 'cc',
}
return useReducer(reducer, initialState)
}
// 组合api 函数可以被任意组件 在任意地方调用
export function useXXXRedux() {
const redux: Redux = useModel(useStore)
return redux
}
复制
3.调用实现,在任意组件内,或者任何组合api内部,在哪里调用都行
export default defineComponent({
name: 'D',
setup() {
//回调中获取数据
const { state:xxState,dispatch } = useXXXRedux()
//监听state变化
watch(xxState, state => {
})
//触发状态改变
dispatch({type:"changeOO",{payload:{oo:"iii"}}})
})
复制
总结
- 缺点: 记录变更、保存状态快照、历史回滚/时光旅行的诉求 这些是缺失的
- 优点:整体代码是简单明了的,无侵入式
- 熟练使用第三方库是一个开发者的基础素养