Vue3狀態(tài)管理【實(shí)踐總結(jié)】

原文地址:juejin.cn/post/6941918738320982023
關(guān)注公眾號(hào) 前端人,回復(fù)“加群”
添加無(wú)廣告優(yōu)質(zhì)學(xué)習(xí)群
狀態(tài)管理
簡(jiǎn)述
多個(gè)組件,多個(gè)模塊之間共享狀態(tài)是最常見(jiàn)的開(kāi)發(fā)述求,場(chǎng)景之多不勝枚舉,例如全局用戶狀態(tài),修改用戶信息全局響應(yīng)變化等等。
常見(jiàn)的解決方案
簡(jiǎn)單方案基于事件監(jiān)聽(tīng)機(jī)制利用回調(diào)傳參,多處訂閱實(shí)現(xiàn)數(shù)據(jù)的流轉(zhuǎn)。例如官方推薦的mitt事件庫(kù)。優(yōu)勢(shì)簡(jiǎn)單的數(shù)據(jù)事件通信是能滿足的,劣勢(shì)隨著數(shù)據(jù)復(fù)雜性變動(dòng),回調(diào)寫法的代碼閱讀性,整體使用體驗(yàn)下降,使用方式也非常簡(jiǎn)單,具體實(shí)現(xiàn)如下:
//定義組合api事件流
const $emitter = mitt()
/**
* @name: useOnChange
* @msg: 監(jiān)聽(tīng)事件emit
*/
export function useOnChange<T extends Function>(fun: T) {
$emitter.on(EventsEnum.CHANGE, itemMessage => {
fun(itemMessage)
})
}
/**
* @name: useChange
* @msg: 觸發(fā)事件emit
* @param {*}
*/
export function useChange(itemMessage: number) {
$emitter.emit(EventsEnum.CHANGE, itemMessage)
}
// A組件中觸發(fā)事件發(fā)送數(shù)據(jù)
export default defineComponent({
name: 'A',
setup() {
//組件A中發(fā)送數(shù)據(jù)
const handlerClick = (item)=>{
//使用組合api發(fā)送數(shù)據(jù)
useChange(1)
}
}
})
// B組件中監(jiān)聽(tīng)事件獲取數(shù)據(jù)
export default defineComponent({
name: 'B',
setup() {
//回調(diào)中獲取數(shù)據(jù)
useOnChange((mes)=>{
console.log(mes)
})
}
})
// C 組件中監(jiān)聽(tīng)事件獲取數(shù)據(jù)
export default defineComponent({
name: 'C',
setup() {
//回調(diào)中獲取數(shù)據(jù)
useOnChange((mes)=>{
console.log(mes)
})
}
})
基于vue3的響應(yīng)式官方一些簡(jiǎn)單的實(shí)踐
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')
知名狀態(tài)管理庫(kù)Redux,Flux,Vuex,這些都是非常優(yōu)秀的第三方庫(kù)
為什么明明有vuex你還在折騰啥?
先拋出一個(gè)問(wèn)題大家用了element那么久,請(qǐng)問(wèn)知道this.$message是怎么實(shí)現(xiàn)的嗎?如果什么都不管,項(xiàng)目來(lái)了上去就是一套全家桶,做一個(gè)項(xiàng)目和做十個(gè)項(xiàng)目有什么區(qū)別?既然新的機(jī)會(huì)來(lái)了為什么自己寫一下vue3的組件,vue3的狀態(tài)管理? 業(yè)務(wù)與場(chǎng)景在項(xiàng)目初期比較簡(jiǎn)單,沒(méi)有記錄變更、保存狀態(tài)快照、歷史回滾/時(shí)光旅行的訴求,那為什么不自己做一個(gè)狀態(tài)管理呢? 核心實(shí)現(xiàn)功能:狀態(tài)修改單項(xiàng)數(shù)據(jù)流,狀態(tài)改變?nèi)謹(jǐn)?shù)據(jù)響應(yīng),代碼約定,思考一下怎么解決這三個(gè)問(wèn)題?
實(shí)現(xiàn)思路
單項(xiàng)數(shù)據(jù)流,Readonly 狀態(tài)改變數(shù)據(jù)響應(yīng),組合api和響應(yīng)式 代碼約束 使用ts 進(jìn)行接口約定
其他大神的一些實(shí)現(xiàn)
利用provide 還有一些基于reactive等等一些想法
站在巨人的肩膀上
基于一些大神是vue3封裝reduer思路自己也去做了實(shí)現(xiàn)
基礎(chǔ)實(shí)現(xiàn)
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.實(shí)現(xiàn)小型reduer
import { Ref } from 'vue'
import { useModel, useReducer } from './reducer'
// 狀態(tài)接口
export interface State {
oo: string,
xx: string,
cc: string,
}
// 行為接口
export interface Action {
type: 'changeOO' | 'changeXX' | 'changeCC'//指定action
payload: State
}
// 組合函數(shù)使用是 狀態(tài)接口
export type StateType = Readonly<Ref<State>>
// 使用實(shí)例接口
interface Redux {
state: StateType
// 這里不是注釋,只是這樣的語(yǔ)法mark當(dāng)不識(shí)別,保證優(yōu)雅性,實(shí)際使用時(shí)放開(kāi)注釋
//dispatch: <T extends Action>(action: T) => void
}
// 狀態(tài)變更
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 }
}
// 初始化狀態(tài)
function useStore() {
const initialState = {
oo: 'oo',
xx: 'xx',
cc: 'cc',
}
return useReducer(reducer, initialState)
}
// 組合api 函數(shù)可以被任意組件 在任意地方調(diào)用
export function useXXXRedux() {
const redux: Redux = useModel(useStore)
return redux
}
3.調(diào)用實(shí)現(xiàn),在任意組件內(nèi),或者任何組合api內(nèi)部,在哪里調(diào)用都行
export default defineComponent({
name: 'D',
setup() {
//回調(diào)中獲取數(shù)據(jù)
const { state:xxState,dispatch } = useXXXRedux()
//監(jiān)聽(tīng)state變化
watch(xxState, state => {
})
//觸發(fā)狀態(tài)改變
dispatch({type:"changeOO",{payload:{oo:"iii"}}})
})
總結(jié)
缺點(diǎn): 記錄變更、保存狀態(tài)快照、歷史回滾/時(shí)光旅行的訴求 這些是缺失的 優(yōu)點(diǎn):整體代碼是簡(jiǎn)單明了的,無(wú)侵入式 熟練使用第三方庫(kù)是一個(gè)開(kāi)發(fā)者的基礎(chǔ)素養(yǎng)
安排
回復(fù) 資料包領(lǐng)取我整理的進(jìn)階資料包回復(fù) 加群,加入前端進(jìn)階群console.log("文章點(diǎn)贊===文章點(diǎn)在看===你我都快樂(lè)")Bug離我更遠(yuǎn)了,下班離我更近了

評(píng)論
圖片
表情
