
在現代的 Web 應用開發中,有效管理全局狀態是一個關鍵挑戰。Nuxt3 作為一個強大的 Vue.js 框架,結合 Pinia 這個靈活的狀態管理庫,為開發者提供了一套完整的解決方案。本文將深入探討如何在 Nuxt3 中使用 Pinia 管理全局狀態,並整合其他先進的前端技術,如 Zod、Vee-Validate 和 VueUse,以構建一個健壯、高效的應用。
首先,我們需要安裝 Pinia 和其他相關的包:
bunx nuxi module add pinia
bunx nuxi module vee-validate
bunx nuxi module vueuse
bun add zod @vee-validate/zod @vueuse/core
在 stores 目錄下創建一個新的 store 文件,例如 userStore.ts:
import * as zod from 'zod';
import { defineStore, acceptHMRUpdate } from 'pinia';
export const userSchema = zod.object({
  id: zod.number(),
  name: zod.string(),
  email: zod.string().email(),
});
export type UserSchema = zod.infer<typeof userSchema>;
export const useBaseStore = defineStore('useBaseStore', () => {
  // state::
  const user = ref<UserSchema | null>(null);
  // methods::
  const fetchUser = async (): Promise<void> => {
    const response = await $fetch('/api/user', {
      method: 'GET',
    });
    const validator = userSchema.safeParse(response);
    if (!validator.success) {
      throw new TypeError('validator error');
    }
    user.value = validator.data;
  };
  const logout = (): void => {
    user.value = null;
  };
  return {
    // state::
    user,
    // methods::
    fetchUser,
    logout,
  }
})
if (import.meta.hot) {
  import.meta.hot.accept(acceptHMRUpdate(useBaseStore, import.meta.hot));
}
創建一個新的組件 UserProfile.vue:
<template>
  <div v-if="user">
    <h2>Welcome, {{ user.name }}!</h2>
    <p>Email: {{ user.email }}</p>
    <button @click="logout">Logout</button>
  </div>
  <div v-else>
    <p>Please log in</p>
  </div>
</template>
<script setup lang="ts">
import { useUserStore } from '~/stores/userStore'
import { storeToRefs } from 'pinia'
const userStore = useUserStore()
const { user } = storeToRefs(userStore)
const { logout } = userStore
onMounted(async () => {
  await userStore.fetchUser()
})
</script>
為了處理客戶端存儲,我們可以使用 VueUse 的 useLocalStorage 和 Nuxt3 的 useNuxtApp:
import { defineStore } from 'pinia'
import { useLocalStorage } from '@vueuse/core'
export const useUserStore = defineStore('user', () => {
  const nuxtApp = useNuxtApp()
  
  const user = ref(null)
  
  if (process.client) {
    const storedUser = useLocalStorage('user', null)
    watch(storedUser, (newValue) => {
      user.value = newValue
    })
  }
  async function fetchUser() {
    const response = await nuxtApp.$fetch('/api/user')
    user.value = response
    if (process.client) {
      localStorage.setItem('user', JSON.stringify(response))
    }
  }
  return { user, fetchUser }
})
為了解決可能出現的 hydration 問題,我們可以使用 Nuxt3 的 <ClientOnly> 組件:
<template>
  <ClientOnly>
    <div v-if="user">
      <h2>Welcome, {{ user.name }}!</h2>
      <p>Email: {{ user.email }}</p>
    </div>
    <template #fallback>
      <p>Loading user data...</p>
    </template>
  </ClientOnly>
</template>
在 Nuxt3 中使用 Pinia 管理全局狀態不僅提高了開發效率,還增強了應用的可維護性和可擴展性。通過整合 Zod 進行數據驗證、Vee-Validate 處理表單驗證,以及利用 VueUse 的 useLocalStorage 處理客戶端存儲,我們構建了一個強大而靈活的狀態管理系統。
需要特別注意的是在服務器端渲染(SSR)環境中處理客戶端特定的操作,如本地存儲。使用 Nuxt3 的 <ClientOnly> 組件和條件性的代碼執行可以有效解決 hydration 問題。
最後,使用 Nuxt 提供的 $fetch 方法進行 API 請求,進一步統一了我們的數據獲取策略。這種整合方法不僅簡化了開發流程,還提高了代碼的一致性和可讀性。
通過實施這些最佳實踐,開發者可以充分利用 Nuxt3 和 Pinia 的優勢,構建出高效、可靠且易於維護的現代 Web 應用。