<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          除了 Vuex ,還有更好的選擇 Pinia

          共 10736字,需瀏覽 22分鐘

           ·

          2022-05-20 11:31

          來自:掘金,作者:凱哥愛吃皮皮蝦

          鏈接:https://juejin.cn/post/7068113574043844622

          Pinia

          pinia 目前已經(jīng)是 vue 官方正式的狀態(tài)庫。適用于 vue2 和 vue3,本文只描述vue3的寫法。

          pinia 的優(yōu)勢

          相對于以前的 vuex,pinia具有以下優(yōu)勢

          • 更簡單的寫法,代碼更清晰簡潔,支持?composition api?和?options api?語法
          • 更完善的 typescript 支持,無需創(chuàng)建自定義復雜的包裝類型來支持 TypeScript,所有內(nèi)容都是類型化的,并且 API 的設計方式盡可能利用 TS 類型推斷
          • 非常輕量,只有1kb的大小
          • 不需要再注入魔法字符串等進行調用

          安裝

          yarn?add?pinia
          //?or
          npm?install?pinia

          定義、使用store

          創(chuàng)建一個 pinia 并傳遞給 vue 應用

          import?{?createPinia?}?from? pinia 
          import?{?createApp?}?from? vue
          import?App?from? ./app.vue

          createApp(App).use(createPinia()).mount( #app )

          定義store

          store的定義是通過 defineStore 這個函數(shù),

          它需要一個唯一的名稱,該名稱可以作為第一個參數(shù)傳遞,也可以用 id 熟悉傳遞。

          import?{?defineStore?}?from? pinia 

          export?const?useMainStore?=?defineStore( main ,?{
          ??//?other?options...
          })
          import?{?defineStore?}?from? pinia 

          export?const?useMainStore?=?defineStore({
          ??id:? main
          ??//?other?options...
          })

          該 id 是必要的,主要是用于 vue devtools

          使用store

          import?{?useMainStore?}?from? @/stores/main 

          export?default?defineComponent({
          ??setup()?{
          ????const?store?=?useMainStore()
          ????return?{
          ??????store,
          ????}
          ??},
          })

          上述代碼中,useMainStore實例化后的,我們就可以在 store 上訪問 state、getters、actions 等(pinia中沒有mutations)。

          該 store 是一個 reactive 對象,所以不需要 “.value”,也不能對其進行解構使用,否則失去響應性(類似 props)。

          storeToRefs

          如果一定要對其進行解構使用,可以使用 storeToRefs ,類似 vue3 中的?toRefs

          import?{?storeToRefs?}?from? pinia 

          export?default?defineComponent({
          ??setup()?{
          ????const?store?=?useMainStore()
          ????const?{?user,?company?}?=?storeToRefs(store)
          ????return?{
          ??????user,?
          ??????company
          ????}
          ??},
          })

          state

          定義state

          在 pinia 中,定義 state 是在函數(shù)中返回 state 初始狀態(tài)

          import?{?defineStore?}?from? pinia 

          const?useMainStore?=?defineStore( main ,?{
          ????state:?()?=>?({
          ????????teacherName:? 艾倫 ,
          ????????userList:?[
          ????????????{?name:? 小明 ,?age:?18?},
          ????????????{?name:? 小李 ,?age:?15?},
          ????????????{?name:? 小白 ,?age:?16?},
          ????????],
          ????}),
          })

          export?default?useMainStore

          訪問state

          可以通過store 實例直接訪問

          import?useMainStore?from? @/store/main 

          export?default?defineComponent({
          ????setup()?{
          ????????const?mainStore?=?useMainStore()
          ????????const?teacherName?=?computed(()?=>?mainStore.teacherName)
          ????????const?userList?=?computed(()?=>?mainStore.userList)

          ????????return?{
          ????????????teacherName,
          ????????????userList,
          ????????}
          ????},
          })

          也可以直接修改狀態(tài)

          import?useMainStore?from? @/store/main 
          export?default?defineComponent({
          ????setup()?{
          ????????const?mainStore?=?useMainStore()
          ????????function?change()?{
          ????????????mainStore.teacherName?=? 米利
          ????????????mainStore.userList.push({
          ????????????????name:? 小琪 ,
          ????????????????age:?19
          ????????????})
          ????????}
          ????????return?{
          ????????????change
          ????????}
          ????},
          })

          雖然可以直接修改,但是出于代碼結構來說,全局的狀態(tài)管理還是不要直接在各個組件處隨意修改狀態(tài),應放于 action 中統(tǒng)一方法修改(沒有mutation了)

          重置狀態(tài)

          可以通過調用store 上的方法將狀態(tài)重置為初始狀態(tài)

          const?mainStore?=?useMainStore()

          mainStore.$reset()

          $patch

          修改state還可以通過使用 $patch 方法

          $patch 可以同時修改多個值,舉個例子

          import?useMainStore?from? @/store/main 

          export?default?defineComponent({
          ????setup()?{
          ????????const?mainStore?=?useMainStore()
          ????????
          ??mainStore.$patch({
          ??????teacherName:? 德普 ,
          ????????????userList:?[
          ????????????????{?name:? 小明 ,?age:?18?},
          ????????????????{?name:? 小李 ,?age:?15?},
          ????????????]
          ??})
          ????????return?{}
          ????},
          })

          但是,這種寫法的在修改數(shù)組時,例如我只想要把 userList 的中第一項"小明"的age 改為 20,也需要傳入整個包括所有成員的數(shù)組,這無疑增加了書寫成本和風險,于是一般都推薦使用以下的傳入一個函數(shù)的寫法

          mainStore.$patch((state)=>{
          ??state.teacherName?=? 德普
          ??state.userList[0].age?=?20
          })

          監(jiān)聽訂閱state

          通過 store.$subscribe() 的方法,

          該方法的第一個參數(shù)接受一個回調函數(shù),該函數(shù)可以在 state 變化時觸發(fā)

          const?subscribe?=?mainStore.$subscribe((mutation,?state)?=>?{
          ????console.log(mutation)
          ????console.log(state)
          })

          如上所示,該回調函數(shù)的兩個參數(shù)

          其中 state 是 mainStore 實例,而 mutation 打印如下

          可以發(fā)現(xiàn),打印結果的mutation對象主要包含三個屬性

          • events : 是這次state改變的具體數(shù)據(jù),包括改變前的值和改變后的值等等數(shù)據(jù)
          • storeId :是當前store的id
          • type:type表示這次變化是通過什么產(chǎn)生的,主要有三個分別是
            • “direct” :通過 action 變化的
            • ”patch object“ :通過 $patch 傳遞對象的方式改變的
            • “patch function” :通過 $patch 傳遞函數(shù)的方式改變的

          停止監(jiān)聽

          上面代碼中,調用mainStore.$subscribe返回的值(即上方示例的 subscribe 變量)可以停止訂閱

          subscribe()

          store.$subscribe() 的方法的第二個參數(shù)options對象,是各種配置參數(shù),包括

          detached屬性,其值是一個布爾值,默認是 false, 正常情況下,當 訂閱所在的組件被卸載時,訂閱將被停止刪除,如果設置detached值為 true 時,即使所在組件被卸載,訂閱依然可以生效。

          其他屬性主要還有 immediate、deep、flush 等等,和 vue3 watch的對應參數(shù)效果一樣。

          getter

          定義getter

          getter 是 store 中的 state 計算值,以defineStore中的getters屬性定義

          getters屬性的值是一個函數(shù),該函數(shù)的第一個參數(shù)是 state

          const?useMainStore?=?defineStore( main ,?{
          ????state:?()?=>?({
          ????????user:?{
          ????????????name:? 小明 ,
          ????????????age:?7,
          ????????},
          ????}),

          ????getters:?{
          ????????userInfo:?(state)?=>?`${state.user.name}今年${state.user.age}歲了`,
          ????????//?這里想要正確推斷參數(shù)?state?的類型,則定義?state?時需要使用箭頭函數(shù)定義
          ????},
          })

          上面代碼中,getters的值是箭頭函數(shù),當getters的值是普通函數(shù)時,可以通過 this 訪問整個store實例(如下)

          但是如果是普通函數(shù),想要通過 this 獲取state的值并希望this的類型能正確推斷,同時希望函數(shù)的返回值類型正確推斷,我們需要聲明函數(shù)的返回類型。

          getters:?{
          ????????userDesc:?(state)?=>?`${state.user.name}今年${state.user.age}歲了`,
          ????????????
          ????????userBesidesDesc():?string{?//?需注明類型
          ????????????return?`${this.user.age}歲的${this.user.name}`?//?可以使用?this?獲取值
          ????????},
          ????????????
          ????????returnUserInfo()?{
          ????????????return?this.userDesc?//?也可以使用?this?獲取其他getters
          ????????},????
          },

          訪問getter

          import?useMainStore?from? @/store/main 
          export?default?defineComponent({
          ????setup()?{
          ????????const?mainStore?=?useMainStore()

          ????????const?userDesc?=?computed(()?=>?mainStore.userDesc)
          ????????const?userBesidesDesc?=?computed(()?=>?mainStore.userBesidesDesc)
          ????????const?returnUserInfo?=?computed(()?=>?mainStore.returnUserInfo)

          ????????return?{
          ????????????userDesc,
          ????????????userBesidesDesc,
          ????????????returnUserInfo,
          ????????}
          ????},
          })

          action

          定義action

          action 是 store 中的 方法,支持同步或異步。

          action 定義的函數(shù)可以是普通函數(shù)從而可以通過 this 訪問整個store實例,同時該函數(shù)可以傳入任意參數(shù)并返回任何數(shù)據(jù)

          const?useMainStore?=?defineStore( main ,?{
          ????state:?()?=>?({
          ????????count:?0,
          ????}),

          ????actions:?{
          ????????add()?{
          ????????????this.count++
          ????????},
          ????????
          ????????addCountNum(num:?number)?{
          ????????????this.count?+=?num
          ????????},
          ????},
          })

          調用action

          setup()?{
          ????????const?mainStore?=?useMainStore()

          ????????function?mainAction()?{
          ????????????mainStore.addCount()
          ????????}
          ????
          ?????function?addCountTwo()?{
          ????????????mainStore.addCountNum(2)
          ????????}

          ????????return?{
          ????????????mainAction,
          ????????????addCountTwo
          ????????}
          },

          監(jiān)聽訂閱action

          通過?store.$onAction(),可以監(jiān)聽action的動作及結果等

          該函數(shù)可以接收一個回調函數(shù)作為參數(shù),回調函數(shù)的參數(shù)中有五個屬性,具體如下

          const?unsubscribe?=?mainStore.$onAction(({
          ????name,?//?action?函數(shù)的名稱
          ????store,?//?store?實例,這里是?mainStore
          ????args,?//?action?函數(shù)參數(shù)數(shù)組
          ????after,?//?鉤子函數(shù),在action函數(shù)執(zhí)行完成返回或者resolves后執(zhí)行
          ????onError,?//?鉤子函數(shù),在action函數(shù)報錯或者rejects后執(zhí)行
          })?=>?{})

          舉個例子,

          首先,定義一個store

          import?{?defineStore?}?from? pinia 
          const?useMainStore?=?defineStore( main ,?{
          ????state:?()?=>?({
          ????????user:?{
          ????????????name:? 小明 ,
          ????????????age:?7,
          ????????},
          ????}),
          ????actions:?{
          ????????subscribeAction(name:?string,?age:?number,?manualError?:?boolean)?{
          ????????????return?new?Promise((resolve,?reject)?=>?{
          ????????????????console.log( subscribeAction函數(shù)執(zhí)行 )
          ????????????????if?(manualError)?{
          ????????????????????reject( 手動報錯 )
          ????????????????}?else?{
          ????????????????????this.user.name?=?name
          ????????????????????this.user.age?=?age
          ????????????????????resolve(`${this.user.name}今年${this.user.age}歲了`)
          ????????????????}
          ????????????})
          ????????},
          ????},
          })
          export?default?useMainStore

          然后在 setup 中使用

          import?useMainStore?from? @/store/main 
          import?{?ref,?defineComponent,?computed?}?from? vue
          export?default?defineComponent({
          ????setup()?{
          ????????const?mainStore?=?useMainStore()

          ????????function?subscribeNormal()?{
          ????????????mainStore.subscribeAction( 小李 ,?18,?false)
          ????????}
          ????????
          ????????function?subscribeError()?{
          ????????????mainStore.subscribeAction( 小白 ,?17,?true)
          ????????}

          ????????const?unsubscribe?=?mainStore.$onAction(({
          ????????????name,?//?action?函數(shù)的名稱
          ????????????store,?//?store?實例,這里是?mainStore
          ????????????args,?//?action?函數(shù)參數(shù)數(shù)組
          ????????????after,?//?鉤子函數(shù),在action函數(shù)執(zhí)行完成返回或者resolves后執(zhí)行
          ????????????onError,?//?鉤子函數(shù),在action函數(shù)報錯或者rejects后執(zhí)行
          ????????})?=>?{
          ????????????console.log( action的函數(shù)名 ,?name)
          ????????????console.log( 參數(shù)數(shù)組 ,?args)
          ????????????console.log( store實例 ,?store)

          ????????????after((result)?=>?{
          ????????????????console.log( $onAction?after函數(shù) ,?result)
          ????????????})

          ????????????onError(error?=>?{
          ????????????????console.log( 錯誤捕獲 ,?error)
          ????????????})
          ????????})

          ????????return?{
          ????????????subscribeNormal,
          ????????????subscribeError,
          ????????}
          ????},
          })

          如上,在 setup 中,調用了 subscribeNormal 函數(shù)后,頁面打印如下

          調用了 subscribeError 函數(shù)后,頁面打印如下

          同樣,可以通過調用 mainStore.$onAction 返回的值來手動停止訂閱,在上面代碼的例子中,即是

          unsubscribe()?//?手動停止訂閱

          store.$onAction 默認在所在組件卸載時會被自動刪除,可以通過傳遞第二個參數(shù) true,來將action訂閱和所在組件分開(即組件卸載時,訂閱依然有效)

          mainStore.$onAction(callback,?true)

          store使用位置

          在組件中使用時,useStore() 在大多數(shù)情況下都可以在調用后開箱即用。

          在其他地方使用時,需確保在 pinia 激活使用后( app.use(createPinia()) )才能使用 useStore()

          例如在路由守衛(wèi)中

          import?{?createRouter?}?from? vue-router 
          import?useMainStore?from? @/store/main
          const?router?=?createRouter({
          ??//?...
          })

          //?報錯
          const?mainStore?=?useMainStore()

          router.beforeEach((to)?=>?{
          ??//?正常使用
          ??const?mainStore?=?useMainStore()
          })

          在store中也可以訪問其他store

          import?{?defineStore?}?from? pinia 
          import?{?useUserStore?}?from? ./user

          export?const?useMainStore?=?defineStore( main ,?{
          ??getters:?{
          ????otherGetter(state)?{
          ??????const?userStore?=?useUserStore()
          ??????return?userStore.data?+?state.data
          ????},
          ??},
          ??actions:?{
          ????async?fetchUserInfo()?{
          ??????const?userStore?=?useUserStore()
          ??????if?(userStore.userInfo)?{
          ????????...
          ??????}
          ????},
          ??},
          })

          pinia插件

          pinia store 支持擴展,通過 pinia 插件我們可以實現(xiàn)以下

          • 給 store 添加新屬性

          • 給 store 添加新選項

          • 給 store 添加新方法

          • 包裝已存在的方法

          • 修改甚至刪除actions

            ...

          例如可以寫一個簡單的插件來給所有store添加一個靜態(tài)屬性

          import?{?createPinia?}?from? pinia 

          const?pinia?=?createPinia()
          //?傳遞一個返回函數(shù)
          pinia.use(()?=>?({?env:? dev ?}))

          app.use(pinia)

          然后,在所有其他的store都可以訪問到上面添加的 env 屬性

          setup()?{
          ????????const?mainStore?=?useMainStore()
          ????????console.log(mainStore.env)?//?dev
          }????????

          插件函數(shù)

          從上方代碼可以發(fā)現(xiàn),pinia 插件是一個函數(shù),這個函數(shù)有一個可選參數(shù)

          import?{?PiniaPluginContext?}?from? pinia 
          function?myPiniaPlugin(context:?PiniaPluginContext)?{
          ????console.log(context)
          }

          context 打印出來主要有

          • app : 當前應用 Vue.createApp() 創(chuàng)建的 app
          • options : defineStore 配置的數(shù)據(jù)
          • pinia : 當前通過 createPinia() 創(chuàng)建的 pinia 實例
          • store :當前 store 實例

          通過 context 我們可以在 store 上設置屬性

          pinia.use(({?store?})?=>?{
          ????store.env?=? dev
          })

          這樣,在所有其他的store都可以訪問到上面添加的 env 屬性

          pinia 的 store 是通過 reactive 包裝的,可以自動解包它包含的任何 ref 對象

          pinia.use(({?store?})?=>?{
          ????store.env?=?ref( dev )
          })

          通過上面插件,訪問store 的 env 時不需要 .value,就可以直接訪問

          setup()?{
          ????????const?mainStore?=?useMainStore()
          ????????console.log(mainStore.env)?//?不需要加?.value
          }

          添加外部屬性

          當需要添加來自其他庫或不需要響應式的數(shù)據(jù)時,應該用 markRaw() 包裝傳遞的對象,例如

          markRaw 來自 vue3,可以標記一個對象,使其永遠不會轉換為 proxy。返回對象本身。

          import?{?markRaw?}?from? vue 
          import?{?router?}?from? ./router
          import?{?axios?}?from? axios

          pinia.use(({?store?})?=>?{
          ??store.router?=?markRaw(router)
          ??store.axios?=?markRaw(axios)
          })

          在插件內(nèi)部使用onAction

          pinia.use(({?store?})?=>?{
          ??store.$subscribe(()?=>?{
          ????//?react?to?store?changes
          ??})
          ??store.$onAction(()?=>?{
          ????//?react?to?store?actions
          ??})
          })

          新屬性的typescript支持

          當通過插件添加新屬性時,可以擴展?PiniaCustomProperties接口

          可以用設置get,set或者簡單聲明值的類型,以此來安全地寫入和讀取新加的屬性

          import? pinia 

          declare?module? pinia ?{
          ????export?interface?PiniaCustomProperties?{
          ????????set?env(value:?string?|?Ref<string>)
          ????????get?env():?string
          ????????//?或者
          ????????env:?string
          ????}
          }


          --- EOF ---


          瀏覽 29
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  免费视频黄片 | 美国一级大黄 | jiZZ亚洲女人高潮大叫 | 青娱乐手机视频 | 亚洲精品国产精品国自产观看 |