基于Vue2.x風(fēng)格的Vuex的使用方式不適合Vue3了
我們都知道Vuex是專(zhuān)門(mén)為Vue開(kāi)發(fā)的一套狀態(tài)管理方式,換個(gè)角度來(lái)看呢,就是給Vue2.x的不足之處打補(bǔ)丁的方式。
像 getter、action都是為了實(shí)現(xiàn)代碼復(fù)用而采取的方式,就連module也是因?yàn)椴环奖銌为?dú)編寫(xiě)管理模塊而設(shè)計(jì)的。
而在Vue3的環(huán)境下,以及沒(méi)有這些不足了,那么我們對(duì)于Vuex的使用方式,也不用拘泥于以前的風(fēng)格。
我們大膽創(chuàng)新一下。
適合Vue3的特點(diǎn)的使用方式
首先可以去掉 getter和action了,完全沒(méi)有用處。不說(shuō)原理了,我們來(lái)看個(gè)實(shí)例。
假設(shè)我們要做一個(gè)彈窗的模態(tài)窗口實(shí)現(xiàn)添加和修改的功能,表單在一個(gè)單獨(dú)的組件里面,其他組件有按鈕,可以設(shè)置“發(fā)文”和“編輯”的指令。
需求比較常見(jiàn)吧,我們來(lái)設(shè)計(jì)一下。
state
export default createStore({
state: {
// 表單彈窗的狀態(tài)
formState: {
editState: 'add', // 編輯狀態(tài),add:添加;update:修改;show :只讀顯示
id: 0, // 0:添加;其他:要修改或者顯示的數(shù)據(jù)的ID
isOpen: false, // 是否打開(kāi)彈窗(模態(tài))
model: {} // 這里只是演示異步加載,實(shí)際項(xiàng)目中并不適合這么做
}
},
mutations: {
// 改變表單彈窗的狀態(tài)
setFormState(state, form) {
// 可以設(shè)置單獨(dú)的屬性,這里偷懶了。
Object.assign(state.formState, form)
}
},
modules: {
}
})
- formState
在state里面定義一個(gè)對(duì)象,對(duì)象的屬性是我們需要的各種狀態(tài),這里為了演示異步加載,設(shè)置了model屬性,其實(shí)并不需要這么設(shè)置。所以大家不要糾結(jié)這個(gè)設(shè)置不符合業(yè)務(wù)邏輯了。
這個(gè)對(duì)象會(huì)自動(dòng)轉(zhuǎn)換成reactive形式,所以會(huì)自帶響應(yīng)性,也就是說(shuō)我們不需要在外面套個(gè)computed了。
同理getter的設(shè)計(jì)也是沒(méi)用必要。
- setFormState
遵照只能在 mutations 修改狀態(tài)的設(shè)定,我們定義了這個(gè)方法,以便實(shí)現(xiàn)修改狀態(tài)的功能。
好了,vuex內(nèi)部的設(shè)計(jì)就是這樣,下面我們開(kāi)始設(shè)計(jì)可以復(fù)用的管理類(lèi)。
可以復(fù)用的狀態(tài)管理類(lèi)
我們建立一個(gè)map-form.js文件,里面寫(xiě)共用代碼。
const mapForm = () => {
// 返回可讀可寫(xiě)的表單彈窗狀態(tài)
const formState = () => {
return store.state.formState
}
// 返回只讀的表單彈窗狀態(tài)
const getFormState = () => {
return readonly(store.state.formState)
}
// 打開(kāi)彈窗
const openForm = () =>{
store.commit('setFormState', { isOpen: true})
}
// 關(guān)閉彈窗
const closeForm = () =>{
store.commit('setFormState', { isOpen: false})
}
// 設(shè)置添加數(shù)據(jù)
const addData = () =>{
store.commit('setFormState', {
isOpen: true,
id: 0,
editState: 'add'
})
}
// 設(shè)置修改狀態(tài)
const updateData = (id) =>{
store.commit('setFormState', {
isOpen: true,
id: id,
editState: 'update'
})
}
// 設(shè)置只讀狀態(tài)
const showData = (id) =>{
store.commit('setFormState', {
isOpen: true,
id: id,
editState: 'show'
})
}
// 異步獲取數(shù)據(jù)
const loadData = (id) =>{
return new Promise((resolve, reject) => {
setTimeout(() => {
store.commit('setFormState', {
model: {
title: '假裝異步獲取了數(shù)據(jù)'
}
})
resolve('成功了!')
}, 1000);
})
}
return {
formState, // 可讀寫(xiě)狀態(tài)
getFormState, // 只讀狀態(tài)
openForm, // 打開(kāi)彈窗
closeForm, // 關(guān)閉彈窗
addData, // 添加新紀(jì)錄并且打開(kāi)彈窗
updateData, // 修改記錄并且打開(kāi)彈窗
showData, // 顯示數(shù)據(jù)并且打開(kāi)彈窗
loadData // 異步加載數(shù)據(jù)
}
}
export default mapForm
去掉“魔數(shù)”
這樣封裝一下,就沒(méi)有字符串的形式的函數(shù)調(diào)用了,不用擔(dān)心字符串會(huì)寫(xiě)錯(cuò)的問(wèn)題了。
同時(shí)也不用考慮module方式下命名空間的問(wèn)題,都是獨(dú)立的管理模塊,誰(shuí)也不影響誰(shuí)。
只讀 vs 隨意
因?yàn)閟tate的屬性會(huì)自動(dòng)變成reactive的形式,所以直接獲取里面的對(duì)象,也會(huì)自動(dòng)變成reactive形式,這樣雖然自帶響應(yīng)性,但是同時(shí)也可以造成直接修改狀態(tài)的問(wèn)題。
所以為了避免誤操作,我們可以利用readonly來(lái)實(shí)現(xiàn)只讀的狀態(tài),這樣就不怕誤操作導(dǎo)致直接修改狀態(tài)了。
- formState 可讀可寫(xiě)的狀態(tài)
- getFormState 只讀的狀態(tài)
具體用那個(gè),就看您喜歡了,不強(qiáng)求。
同步改變狀態(tài)
打開(kāi)、關(guān)閉彈窗,設(shè)置添加、修改狀態(tài)等,都是同步改變,封裝好對(duì)應(yīng)的函數(shù),組件里就可以直接調(diào)用了,這樣組件實(shí)現(xiàn)最小獲知原則,調(diào)用函數(shù)就好,不用管其他的。
代碼也很簡(jiǎn)單,不多說(shuō)了。
異步修改狀態(tài)
loadData 里面定義一個(gè)promise的實(shí)例,實(shí)現(xiàn)異步返回狀態(tài)的功能,這樣組件里可以知道確切的加載完畢時(shí)機(jī)。
表單組件可以這樣操作:
const { getFormState, loadData } = mapForm()
const formState = getFormState()
// 用于綁定表單的model
const model = reactive({
title: ''
})
watch(() => formState.id ,(v1, v2) => {
if (v1 === 0) {
// 添加狀態(tài),重置model
model.title = ''
} else {
// 修改或者只讀狀態(tài),異步加載數(shù)據(jù)
// 另一種加載狀態(tài),這個(gè)成功了。
const loading = ElementPlus.ElLoading.service({
lock: true,
text: 'Loading',
spinner: 'el-icon-loading',
background: 'rgba(0, 0, 0, 0.1)'
})
loadData().then((msg) => {
// 加載成功的提示
ElementPlus.ElMessage.success({
message: msg,
type: 'success'
})
// 設(shè)置model數(shù)據(jù)
Object.assign(model, formState.model)
// 關(guān)閉加載狀態(tài)
loading.close()
})
}
- 監(jiān)聽(tīng)I(yíng)D的變化,異步加載數(shù)據(jù)
在id變化的時(shí)候才需要加載對(duì)應(yīng)的數(shù)據(jù),所以這里做了一個(gè)wacth。
如果是添加的話(huà),設(shè)置model為初始狀態(tài)。
如果是修改,就調(diào)用函數(shù)異步加載數(shù)據(jù),同時(shí)設(shè)置加載狀態(tài)。
等加載結(jié)束,停止加載狀態(tài),并且設(shè)置數(shù)據(jù)給model。
這里僅僅是為了演示異步操作,才設(shè)置這種寫(xiě)法,不代表真實(shí)項(xiàng)目也是這么寫(xiě)的。
這樣一個(gè)基本的狀態(tài)管理方式就展現(xiàn)出來(lái)了,個(gè)人感覺(jué)還是很清爽的。