Vuex4.0(八)在Vue3環(huán)境下如何優(yōu)雅的管理狀態(tài)?

基于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é)還是很清爽的。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀(guān)點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容