Vue 新的狀態(tài)管理 Pinia

Pinia 和 Vuex

Vuex:State、Gettes、Mutations(同步)、Actions(異步)

Pinia:State、Gettes、Actions(同步異步都支持)

Vuex 當(dāng)前最新版是 4.x

Vuex4 用于 Vue3

Vuex3 用于 Vue2

Pinia 當(dāng)前最新版是 2.x

即支持 Vue2 也支持 Vue3

就目前而言 Pinia 比 Vuex 好太多了,解決了 Vuex 的很多問題,所以筆者也非常建議直接使用 Pinia,尤其是 TypeScript 的項(xiàng)目

Pinia 核心特性

  • Pinia 沒有 Mutations

  • Actions 支持同步和異步

  • 沒有模塊的嵌套結(jié)構(gòu)

  • Pinia 通過設(shè)計(jì)提供扁平結(jié)構(gòu),就是說每個(gè) store 都是互相獨(dú)立的,誰也不屬于誰,也就是扁平化了,更好的代碼分割且沒有命名空間。當(dāng)然你也可以通過在一個(gè)模塊中導(dǎo)入另一個(gè)模塊來隱式嵌套 store,甚至可以擁有 store 的循環(huán)依賴關(guān)系

  • 更好的 TypeScript 支持

  • 不需要再創(chuàng)建自定義的復(fù)雜包裝器來支持 TypeScript 所有內(nèi)容都類型化,并且 API 的設(shè)計(jì)方式也盡可能的使用 TS 類型推斷

  • 不需要注入、導(dǎo)入函數(shù)、調(diào)用它們,享受自動(dòng)補(bǔ)全,讓我們開發(fā)更加方便

  • 無需手動(dòng)添加 store,它的模塊默認(rèn)情況下創(chuàng)建就自動(dòng)注冊(cè)的

  • Vue2 和 Vue3 都支持

  • 除了初始化安裝和SSR配置之外,兩者使用上的API都是相同的

  • 支持 Vue DevTools

  • 跟蹤 actions, mutations 的時(shí)間線

  • 在使用了模塊的組件中就可以觀察到模塊本身

  • 支持 time-travel 更容易調(diào)試

  • 在 Vue2 中 Pinia 會(huì)使用 Vuex 的所有接口,所以它倆不能一起使用

  • 但是針對(duì) Vue3 的調(diào)試工具支持還不夠完美,比如還沒有 time-travel 功能

  • 模塊熱更新

  • 無需重新加載頁(yè)面就可以修改模塊

  • 熱更新的時(shí)候會(huì)保持任何現(xiàn)有狀態(tài)

  • 支持使用插件擴(kuò)展 Pinia 功能

  • 支持服務(wù)端渲染

Pinia 使用

以 Vue3 + TypeScript 為例
安裝

npm install pinia

main.ts 初始化配置

import { createPinia } from 'pinia'
createApp(App).use(createPinia()).mount('#app')

在 store 目錄下創(chuàng)建一個(gè) user.ts 為例,我們先定義并導(dǎo)出一個(gè)名為 user 的模塊

import { defineStore } from 'pinia'
export const userStore = defineStore('user', {
   state: () => {
       return {
           count: 1,
           arr: []
      }
  },
   getters: { ... },
   actions: { ... }
})

defineStore 接收兩個(gè)參數(shù)
第一個(gè)參數(shù)就是模塊的名稱,必須是唯一的,多個(gè)模塊不能重名,Pinia 會(huì)把所有的模塊都掛載到根容器上 第二個(gè)參數(shù)是一個(gè)對(duì)象,里面的選項(xiàng)和 Vuex 差不多

  • 其中 state 用來存儲(chǔ)全局狀態(tài),它必須是箭頭函數(shù),為了在服務(wù)端渲染的時(shí)候避免交叉請(qǐng)求導(dǎo)致的數(shù)據(jù)狀態(tài)污染所以只能是函數(shù),而必須用箭頭函數(shù)則為了更好的 TS 類型推導(dǎo)
  • getters 就是用來封裝計(jì)算屬性,它有緩存的功能
  • actions 就是用來封裝業(yè)務(wù)邏輯,修改 state

訪問 state

比如我們要在頁(yè)面中訪問 state 里的屬性 count
由于 defineStore 會(huì)返回一個(gè)函數(shù),所以要先調(diào)用拿到數(shù)據(jù)對(duì)象,然后就可以在模板中直接使用了

<template>
   <div>{{ user_store.count }}</div>
</template>
<script lang="ts" setup>
import { userStore } from '../store'
const user_store = userStore()
// 解構(gòu)
// const { count } = userStore()
</script>

比如像注釋中的解構(gòu)出來使用,是完全沒有問題的,只是注意了,這樣拿到的數(shù)據(jù)不是響應(yīng)式的,如果要解構(gòu)還保持響應(yīng)式就要用到一個(gè)方法 storeToRefs(),示例如下

<template>
   <div>{{ count }}</div>
</template>
<script lang="ts" setup>
import { storeToRefs } from 'pinia'
import { userStore } from '../store'
const { count } = storeToRefs(userStore)
</script>

原因就是 Pinia 其實(shí)是把 state 數(shù)據(jù)都做了 reactive 處理,和 Vue3 的 reactive 同理,解構(gòu)出來的也不是響應(yīng)式,所以需要再做 ref 響應(yīng)式代理

getters

這個(gè)和 Vuex 的 getters 一樣,也有緩存功能。如下在頁(yè)面中多次使用,第一次會(huì)調(diào)用 getters,數(shù)據(jù)沒有改變的情況下之后會(huì)讀取緩存

<template>
   <div>{{ myCount }}</div>
   <div>{{ myCount }}</div>
   <div>{{ myCount }}</div>
</template>

注意兩種方法的區(qū)別,寫在注釋里了

getters: {
   // 方法一,接收一個(gè)可選參數(shù) state
   myCount(state){
       console.log('調(diào)用了') // 頁(yè)面中使用了三次,這里只會(huì)執(zhí)行一次,然后緩存起來了
       return state.count + 1
  },
   // 方法二,不傳參數(shù),使用 this
   // 但是必須指定函數(shù)返回值的類型,否則類型推導(dǎo)不出來
   myCount(): number{
       return this.count + 1
  }
}

更新和 actions

更新 state 里的數(shù)據(jù)有四種方法,我們先看三種簡(jiǎn)單的更新,說明都寫在注釋里了

<template>
   <div>{{ user_store.count }}</div>
   <button @click="handleClick">按鈕</button>
</template>
<script lang="ts" setup>
import { userStore } from '../store'
const user_store = userStore()
const handleClick = () => {
   // 方法一
   user_store.count++
   
   // 方法二,需要修改多個(gè)數(shù)據(jù),建議用 $patch 批量更新,傳入一個(gè)對(duì)象
   user_store.$patch({
       count: user_store.count1++,
       // arr: user_store.arr.push(1) // 錯(cuò)誤
       arr: [ ...user_store.arr, 1 ] // 可以,但是還得把整個(gè)數(shù)組都拿出來解構(gòu),就沒必要
  })
   
   // 使用 $patch 性能更優(yōu),因?yàn)槎鄠€(gè)數(shù)據(jù)更新只會(huì)更新一次視圖
   
   // 方法三,還是$patch,傳入函數(shù),第一個(gè)參數(shù)就是 state
   user_store.$patch( state => {
       state.count++
       state.arr.push(1)
  })
}
</script>

第四種方法就是當(dāng)邏輯比較多或者請(qǐng)求的時(shí)候,我們就可以封裝到示例中 store/user.ts 里的 actions 里

可以傳參數(shù),也可以通過 this.xx 可以直接獲取到 state 里的數(shù)據(jù),需要注意的是不能用箭頭函數(shù)定義 actions,不然就會(huì)綁定外部的 this 了

actions: {
   changeState(num: number){ // 不能用箭頭函數(shù)
       this.count += num
  }
}

調(diào)用

const handleClick = () => {
   user_store.changeState(1)
}
最后編輯于
?著作權(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)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

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

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