iT邦幫忙

2025 iThome 鐵人賽

DAY 20
0
Vue.js

打造銷售系統30天修練 - 全集中・Vue之呼吸系列 第 20

Day 20:[Stateの呼吸・壹之型] Pinia入門 - 全域狀態管理

  • 分享至 

  • xImage
  •  

在上一回的修煉中,我們為了讓路由守衛能得知「使用者是否登入」,引入了一個名為 useAuthStore 的東西。我們只是簡單地使用了它,但你心中可能充滿了疑問:這是什麼東西?它從何而來?又為何要用它?

今天,我們將深入了解 Vue 官方推薦的狀態管理函式庫——Pinia,理解它如何幫助我們管理散落在應用各處的「全域狀態」。

為什麼需要全域狀態管理?

想像一下,我們的銷售系統越來越複雜,許多元件都需要共用同一份資料:

  • Header 元件需要顯示使用者名稱。
  • SideBar 元件需要根據使用者權限來顯示不同的選單。
  • Profile 頁面需要讀取並修改使用者的個人資料。
  • router 需要知道使用者是否登入來決定是否放行。

如果沒有一個統一的管理機制,我們可能會陷入「Props 地獄 (Prop Drilling)」——將資料作為 props 從父元件一層一層地往下傳遞,即使中間的好幾層元件根本用不到這些資料。這會讓程式碼變得極難維護。

為了解決這個問題,「全域狀態管理」應運而生。它的核心思想是:

將共用的狀態抽離出來,放置在一個獨立於所有元件的「中央倉庫 (Store)」中。任何元件都可以直接從這個倉庫讀取或修改狀態,而無需透過 props 傳遞。

而 Pinia,就是 Vue 3 生態中最閃亮、最受推薦的那個「中央倉庫」。

Pinia 的核心概念

Pinia 的設計非常簡潔直觀,你可以把每一個 Store 都想像成一個獨立的模組,它由三個核心部分組成:stategettersactions

讓我們再次請出 Day 19 的 auth.js,以它為範例來解構 Pinia Store。

// src/stores/auth.js

import { defineStore } from 'pinia'

// 使用 defineStore() 來定義一個 Store
// 第一個參數是這個 Store 的唯一 ID
export const useAuthStore = defineStore('auth', {
  // ... 核心三本柱
})

1. State (狀態)

state 是 Store 的心臟,是存放資料的地方。它必須是一個函式,並且回傳一個物件,這個物件就包含了這個 Store 的初始狀態。

// ...
  state: () => ({
    // 我們假設 token 存在 localStorage 中
    token: localStorage.getItem('pos-auth-token') || null,
    // 我們也可以在這裡存放使用者資訊
    userInfo: null,
  }),
// ...
  • state 之所以是函式,是為了避免在伺服器端渲染 (SSR) 時造成交叉請求的狀態污染。
  • 這裡就是我們應用程式「單一資料來源 (Single Source of Truth)」的所在地。所有關於認證的狀態,都應該從這裡讀取。

2. Getters (衍伸狀態)

getters 就像是 Store 的「計算屬性 (Computed Properties)」。它們可以用來基於 state 的值,衍伸出新的資料。getters 的值會被快取,只有當它依賴的 state 發生變化時,才會重新計算。

// ...
  getters: {
    // getter 接收 state 作為第一個參數
    isLoggedIn: (state) => !!state.token,

    // 也可以使用 this 來存取 state
    userName: (state) => state.userInfo?.name || 'Guest',
  },
// ...
  • isLoggedIn 就是一個完美的例子。我們不需要在每個元件中都寫一次 !!authStore.token,而是可以直接取用這個 getter,讓程式碼更簡潔。

3. Actions (動作)

actions 就像是 Store 的「方法 (Methods)」。它們是唯一推薦用來修改 state 的地方。Action 可以是同步的,也可以是異步的。

// ...
  actions: {
    // 登入成功後呼叫此 action
    login(token, user) {
      // 在 action 中,我們可以用 `this` 來存取 state
      this.token = token;
      this.userInfo = user;
      localStorage.setItem('pos-auth-token', token);
    },

    // 登出時呼叫此 action
    logout() {
      this.token = null;
      this.userInfo = null;
      localStorage.removeItem('pos-auth-token');
    },

    // 也可以是 async action
    async fetchUserProfile() {
      // const user = await api.getUser();
      // this.userInfo = user;
    }
  },
// ...
  • 將所有修改 state 的邏輯都封裝在 actions 中,可以讓我們的狀態變更流程更可預測、更易於追蹤和除錯。

在元件中使用 Store

定義好 Store 之後,在元件中使用它就非常簡單了。

<script setup>
import { useAuthStore } from '@/stores/auth';
import { storeToRefs } from 'pinia';

// 取得 store 實例
const authStore = useAuthStore();

//使用 storeToRefs 來保持響應性
const { isLoggedIn, userName } = storeToRefs(authStore);

function handleLogout() {
  authStore.logout();
}
</script>

<template>
  <div v-if="isLoggedIn">
    <p>歡迎, {{ userName }}</p>
    <button @click="handleLogout">登出</button>
  </div>
</template>
  • storeToRefs:這是一個非常重要的輔助函式。如果你想從 Store 中解構 stategetters 並在模板中使用,同時又希望它們保持響應性,就必須使用 storeToRefs。它會將每一個屬性都轉換成一個 .valueref
  • 呼叫 Actions:直接像呼叫普通物件的方法一樣 authStore.logout() 即可。

總結

今天,我們正式認識了 Pinia 這位強大的狀態管理夥伴。我們學到了:

  1. 為何需要 Pinia:為了解決大型應用中跨元件的狀態共享問題,避免「Props 地獄」。
  2. Pinia 的三大核心state (資料來源)、getters (衍伸資料) 和 actions (修改資料的方法)。
  3. 如何在元件中使用:透過 useAuthStore() 取得實例,並使用 storeToRefs 來安全地解構響應式資料。

Pinia 以其簡潔的 API、完整的 TypeScript 支援和強大的 DevTools 整合,成為了 Vue 3 開發的首選。掌握了它,就等於掌握了管理複雜應用狀態的key。

明日,Day 21:[Stateの呼吸・貳之型] User State - 管理登入狀態與資料。心を燃やせ 🔥!


上一篇
Day 19:[Routerの呼吸・貳之型] 路由守衛 - 權限控制與保護
下一篇
Day 21:[Stateの呼吸・貳之型] User State - 管理登入狀態與資料
系列文
打造銷售系統30天修練 - 全集中・Vue之呼吸22
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言