vue3 快速入門系列 —— 狀態管理 pinia

来源:https://www.cnblogs.com/pengjiali/p/18160817
-Advertisement-
Play Games

其他章節請看: vue3 快速入門 系列 Pinia vue3 狀態管理這裡選擇 pinia。 雖然 vuex4 已支持 Vue 3 的 Composition API,但是 vue3 官網推薦新的應用使用 pinia —— vue3 pinia 集中式狀態管理 redux、mobx、vuex、pi ...


其他章節請看:

vue3 快速入門 系列

Pinia

vue3 狀態管理這裡選擇 pinia

雖然 vuex4 已支持 Vue 3 的 Composition API,但是 vue3 官網推薦新的應用使用 pinia —— vue3 pinia

集中式狀態管理

redux、mobx、vuex、pinia都是集中式狀態管理工具。與之對應的就是分散式。

Pinia 符合直覺的 Vue.js 狀態管理庫 甚至讓你忘記正在使用的是一個狀態庫 —— 官網

安裝 pinia 環境

首先下載安裝包:

PS hello_vue3> npm i pinia

added 2 packages, and audited 71 packages in 11s

10 packages are looking for funding
  run `npm fund` for details

1 moderate severity vulnerability

To address all issues, run:      
  npm audit fix

Run `npm audit` for details.  
 "dependencies": {
    "pinia": "^2.1.7",
    "vue": "^3.4.15",
    "vue-router": "^4.3.0"
  },

在 main.ts 中依次:引入、創建和安裝 pinia,在瀏覽器 vue 開發者工具中就能看到 pinia(一個菠蘿圖標)。

import {createApp} from 'vue'
import App from './App.vue'
import router from './router'

// 引入
import { createPinia } from 'pinia'

const app = createApp(App)

// 創建
const pinia = createPinia()
app.use(router)

// 安裝:就像安裝 vue-router 一樣使用
app.use(pinia)
app.mount('#app')

有時這個菠蘿沒出現,可以關閉瀏覽器或重啟服務。

Tip: 詳細請看 pinia 安裝官網

第一個示例

vuex 的核心概念有 State、Getters、Mutations、Actions和Modules。其中 State 是數據,我們不能直接修改數據。

pinia 比 vuex 更輕量,更易使用。比如拿到數據後就能直接改,符合直覺

請看示例:

pinia 的數據從項目目錄上說,會放在 store 文件夾中。

通常我們會對狀態進行分類,比如用戶相關的數據放在 store/user.ts 中:

// src/store/user.ts
import { defineStore } from 'pinia'

// 你可以任意命名 `defineStore()` 的返回值,但最好使用 store 的名字,同時以 `use` 開頭且以 `Store` 結尾。
// (比如 `useUserStore`,`useCartStore`,`useProductStore`)
// 第一個參數是你的應用中 Store 的唯一 ID。 Pinia 將用它來連接 store 和 devtools
export const useUserStore = defineStore('user', {
  // actions 裡面放一個一個的方法,用於響應組件中的動作
  actions: {
    changeNameAndAge() {
      // this Proxy(Object)
      // 裡面有 $state。在 vue2 中有 $watch、$on等以$開頭的都是給程式員用的實例方法
      console.log('this', this);
      
      // 沒必要通過 $state,直接訪問即可
      this.name += '~'
      this.$state.age += 1
    }
  },
  state: () => {
    return {
      name: 'peng',
      age: 18,
    }
  },
})

通過 defineStore 定義一個 store,第一個參數是 store 的id,命名建議規範,例如使用文件名,導出方式這裡選擇分別導出,導出的名字使用 use+user+store。

state 是一個函數,返回的就是數據

actions 中是一個一個的方法,但不需要像 vuex 中需用 dispatch 觸發。

接著在需要使用狀態的地方使用。讀取狀態的方式有2種,修改狀態的方式有3種:

// Home.vue
<template>
  <div>
    <!-- 讀取方式1 -->
    <p>{{ userStore.name }}</p>
    <!-- 讀取方式2。方式1更方便 -->
    <p>{{ userStore.$state.age }}</p>

    <p><button @click="changeNameAndAge">修改方式1:change age and name</button></p>
    <p><button @click="changeNameAndAge2">修改方式2:change age and name</button></p>
    <p><button @click="changeNameAndAge3">修改方式3:change age and name</button></p>
  </div>
</template>

<script lang="ts" setup name="App">
// 寫 '@/store/user.ts' vscode 報錯:An import path can only end with a '.ts' extension when 'allowImportingTsExtensions' is enabled
// 去掉 .ts 即可
import {useUserStore} from '@/store/user'

const userStore = useUserStore()
// userStore: Proxy(Object)
console.log('userStore: ', userStore);

function changeName() {
  // 修改數據方式1:直接操作數據
  // vue2中的vuex必須通過mutation更新數據,不能直接修改數據
  userStore.name += '~'
}

function changeNameAndAge() {
  userStore.$state.name += '~'
  userStore.$state.age += 1
}

function changeNameAndAge2() {
  // $patch 用於批量修改
  // patch 中文“碎片”,比如age 就是 pinia 中一個數據碎片
  userStore.$patch({
    name: userStore.name + '~',
    age: userStore.age + 1
  })
}

function changeNameAndAge3() {
  // 調用 actions
  userStore.changeNameAndAge()
}
</script>

Tip: changeNameAndAge 會觸發2次修改,而 changeNameAndAge2 使用 $patch 會進行批量修改,從開發者時間線中看到,只執行1次。如果很多數據同時修改,推薦使用 patch。

優雅的讀取數據

前面我們是這麼讀取 store 中數據:

<p>{{ userStore.name }}</p>

const userStore = useUserStore()

如果需要讀取的數據太多,在模板中就會出現很多 userStore,於是我們想到用 toRefs 解構解決。就像這樣:

<p>優雅的讀:{{ name }}</p>

import {toRefs} from 'vue'
const userStore = useUserStore()
const {name} = toRefs(userStore)

但是 toRefs(userStore) 太重,通過console.log(toRefs(userStore)) 你會發現toRefs將 store 所有屬性(包括方法)都轉成 ref,其實我們只需要將數據轉成 ref 即可。

pinia 也想到了這個問題,於是可以用 storeToRefs 替代。就像這樣:

<p>優雅的讀:{{ name }}</p>

import {storeToRefs} from 'pinia'
const userStore = useUserStore()

const {name} = storeToRefs(userStore)

// storeToRefs(userStore): {name: ObjectRefImpl, age: ObjectRefImpl}
console.log('storeToRefs(userStore): ', storeToRefs(userStore));

// toRefs(userStore): {$id: ObjectRefImpl, $onAction: ObjectRefImpl, $patch: ObjectRefImpl, $reset: ObjectRefImpl, $subscribe: ObjectRefImpl, …}
console.log('toRefs(userStore): ', toRefs(userStore));

Getters

和 vuex 中 Getters 作用相同,用法類似。

這裡用了兩種方式定義 getters:

  state: () => {
    return {
      name: 'Peng',
      age: 18,
    }
  },
  getters: {
    // 推薦使用箭頭函數,參數會傳入 state
    bigName: (state) => state.name.toLocaleUpperCase(),

    // 如果需要訪問其他 getters 屬性,可以通過非箭頭函數,通過 this 訪問
    lowerName2(): string{
      return this.bigName.toLocaleLowerCase()
    }
  },

數據取得的方式和 state 相同:

<p>優雅的讀:{{ name }}</p>
<p>bigName:{{ bigName }}</p>
<p>lowerName2:{{ lowerName2 }}</p>

const userStore = useUserStore()

const {name, bigName, lowerName2} = storeToRefs(userStore)

Tip:詳細請看 pinia getters

訂閱

類似於 Vuex 的 subscribe 方法,你可以通過 store 的 $subscribe() 方法偵聽 state 及其變化

只要 userStore 中的數據變化了,函數就會被調用,我們通常關心第二個參數:

// 只要 userStore 數據變化,這個
userStore.$subscribe((mutation, state) => {
  // {storeId: 'user', type: 'direct', events: {…}}
  console.log('mutation: ', mutation);

  // Proxy(Object) {name: 'Peng~', age: 19}
  console.log('state: ', state);

  // 每當狀態發生變化時,將整個 state 持久化到本地存儲。
  localStorage.setItem('userStore', JSON.stringify(state))
})

我們可以將 state 存儲到本地,這樣就可以實現頁面刷新,狀態不丟失。

Tip: 細節請看 訂閱 state

組合式寫法

目前 actions state 寫法屬於聲明式的:

import { defineStore } from 'pinia'

export const useUserStore = defineStore('user', {
  actions: {
    changeNameAndAge() {
      this.name += '~'
      this.$state.age += 1
    }
  },
  state: () => {
    return {
      name: 'Peng',
      age: 18,
    }
  },
  getters: {
    bigName: (state) => state.name.toLocaleUpperCase(),
    lowerName2(): string{
      return this.bigName.toLocaleLowerCase()
    }
  },
})

將其改成組合式。代碼如下:

import { defineStore } from 'pinia';
import {ref, computed} from 'vue'
export const useUserStore = defineStore('user', () => {
  // 數據用 ref 或 reactive 定義
  const name = ref('Peng')
  const age = ref(18)
  
  // getters 用計算屬性
  const bigName = computed(() => name.value.toLocaleUpperCase())
  const lowerName2 = computed(() => bigName.value.toLocaleLowerCase())

  // actions 用方法定義
  function changeNameAndAge() {
    name.value += '~';
    age.value += 1;
  }

  // 最後必須暴露出去
  return {
    // vscode 中數據一個顏色、方法另一個顏色
    name,
    age,
    bigName,
    lowerName2,
    changeNameAndAge,
  };
});

Tip:組合式寫法更靈活(請看 組合式 Store),層級少,但必須返回。具體如何選擇自行決定。

擴展

ref 數據要不要 .value

const a = reactive({
    x: 1,
    y: 2,
    z: ref(3)
})

const b = ref(4)
console.log(a.x)
// ref 如果在裡面,則不需要拆包
console.log(a.z)
console.log(b.value)

讀取響應式對象中的 ref 不需要 .value

其他章節請看:

vue3 快速入門 系列

作者:彭加李
出處:https://www.cnblogs.com/pengjiali/p/18160817
本文版權歸作者和博客園共有,歡迎轉載,但未經作者同意必須保留此段聲明,且在文章頁面明顯位置給出原文連接。

您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 字元編碼和排序規則 下麵的討論用到W、王和三個字元,以下是這三個字元的各種編碼 先看看不帶N和帶N的字元字面量各用什麼編碼,用Microsoft SQL Server Management Studio連接SQL SERVER 2022執行下麵SQL語句: select N'W' charact ...
  • 引言 在數據驅動的世界中,企業正在尋求可靠且高性能的解決方案來管理其不斷增長的數據需求。本系列博客從一個重視數據安全和合規性的 B2C 金融科技客戶的角度來討論雲上雲下混合部署的情況下如何利用亞馬遜雲科技雲原生服務、開源社區產品以及第三方工具構建無伺服器數據倉庫的解耦方法。 Apache EMR(E ...
  • 問題:Jetpack Compose 中使用 Material 包中的控制項,點擊預設會有水波紋效果。如何去除這個點擊水波紋效果呢? 看下 Modifier.clickable 的簽名: fun Modifier.clickable( interactionSource: MutableInterac ...
  • 看問題本質,設置全面屏,是系統視窗的行為,與 View 和 Compose 有什麼關係呢? 所以,原理和傳統 View 視圖是一樣的,甚至 Api 都是一模一樣的,不熟悉的可以看我之前的文章。傳送門: Android 全面屏體驗 那為什麼還要寫這篇文章呢?主要是在 Compose 中寫法上的一些區別 ...
  • 目錄一、低級別動畫 API1.1 animate*AsState1.2 Animatable1.3 Transition 動畫1.3.1 updateTransition1.3.2 createChildTransition1.3.3 封裝並復用 Transition 動畫1.4 remeberIn ...
  • 前言 鍵鼠事件是指在電腦操作中,用戶通過鍵盤和滑鼠來與電腦進行交互的行為。常見的鍵鼠事件包括按下鍵盤上的鍵、移動滑鼠、點擊滑鼠左鍵或右鍵等等。鍵鼠事件可以觸發許多不同的操作,比如在文本編輯器中輸入文字、在游戲中移動角色、在網頁上點擊鏈接等等。電腦操作系統和應用程式可以通過監聽鍵鼠事件來響應 ...
  • 前言 觸屏事件是指通過觸摸屏幕來進行操作和交互的事件。常見的觸屏事件包括點擊(tap)、雙擊(double tap)、長按(long press)、滑動(swipe)、拖動(drag)等。觸屏事件通常用於移動設備和平板電腦等具有觸摸屏幕的設備上,用戶可以通過觸摸屏幕上的不同區域或者以不同的方式進 ...
  • DTD 是文檔類型定義(Document Type Definition)的縮寫。DTD 定義了 XML 文檔的結構以及合法的元素和屬性。 為什麼使用 DTD 通過使用 DTD,獨立的團體可以就數據交換的標準 DTD 達成一致。 應用程式可以使用 DTD 來驗證 XML 數據的有效性。 內部 DTD ...
一周排行
    -Advertisement-
    Play Games
  • C#TMS系統代碼-基礎頁面BaseCity學習 本人純新手,剛進公司跟領導報道,我說我是java全棧,他問我會不會C#,我說大學學過,他說這個TMS系統就給你來管了。外包已經把代碼給我了,這幾天先把增刪改查的代碼背一下,說不定後面就要趕鴨子上架了 Service頁面 //using => impo ...
  • 委托與事件 委托 委托的定義 委托是C#中的一種類型,用於存儲對方法的引用。它允許將方法作為參數傳遞給其他方法,實現回調、事件處理和動態調用等功能。通俗來講,就是委托包含方法的記憶體地址,方法匹配與委托相同的簽名,因此通過使用正確的參數類型來調用方法。 委托的特性 引用方法:委托允許存儲對方法的引用, ...
  • 前言 這幾天閑來沒事看看ABP vNext的文檔和源碼,關於關於依賴註入(屬性註入)這塊兒產生了興趣。 我們都知道。Volo.ABP 依賴註入容器使用了第三方組件Autofac實現的。有三種註入方式,構造函數註入和方法註入和屬性註入。 ABP的屬性註入原則參考如下: 這時候我就開始疑惑了,因為我知道 ...
  • C#TMS系統代碼-業務頁面ShippingNotice學習 學一個業務頁面,ok,領導開完會就被裁掉了,很突然啊,他收拾東西的時候我還以為他要旅游提前請假了,還在尋思為什麼回家連自己買的幾箱飲料都要叫跑腿帶走,怕被偷嗎?還好我在他開會之前拿了兩瓶芬達 感覺感覺前面的BaseCity差不太多,這邊的 ...
  • 概述:在C#中,通過`Expression`類、`AndAlso`和`OrElse`方法可組合兩個`Expression<Func<T, bool>>`,實現多條件動態查詢。通過創建表達式樹,可輕鬆構建複雜的查詢條件。 在C#中,可以使用AndAlso和OrElse方法組合兩個Expression< ...
  • 閑來無聊在我的Biwen.QuickApi中實現一下極簡的事件匯流排,其實代碼還是蠻簡單的,對於初學者可能有些幫助 就貼出來,有什麼不足的地方也歡迎板磚交流~ 首先定義一個事件約定的空介面 public interface IEvent{} 然後定義事件訂閱者介面 public interface I ...
  • 1. 案例 成某三甲醫預約系統, 該項目在2024年初進行上線測試,在正常運行了兩天後,業務系統報錯:The connection pool has been exhausted, either raise MaxPoolSize (currently 800) or Timeout (curren ...
  • 背景 我們有些工具在 Web 版中已經有了很好的實踐,而在 WPF 中重新開發也是一種費時費力的操作,那麼直接集成則是最省事省力的方法了。 思路解釋 為什麼要使用 WPF?莫問為什麼,老 C# 開發的堅持,另外因為 Windows 上已經裝了 Webview2/edge 整體打包比 electron ...
  • EDP是一套集組織架構,許可權框架【功能許可權,操作許可權,數據訪問許可權,WebApi許可權】,自動化日誌,動態Interface,WebApi管理等基礎功能於一體的,基於.net的企業應用開發框架。通過友好的編碼方式實現數據行、列許可權的管控。 ...
  • .Net8.0 Blazor Hybird 桌面端 (WPF/Winform) 實測可以完整運行在 win7sp1/win10/win11. 如果用其他工具打包,還可以運行在mac/linux下, 傳送門BlazorHybrid 發佈為無依賴包方式 安裝 WebView2Runtime 1.57 M ...