使用 Pinia 的五个技巧

本文分享了在使用Pinia时的五个实用技巧,包括避免无用getter、利用OptionStores中的组合式函数、在SetupStores中处理复杂逻辑、注入全局变量以及处理私有状态和SSR中的客户端状态。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

在这篇文章中,想与大家分享使用 Pinia 的五大技巧。

以下是简要总结:

  1. 不要创建无用的 getter
  2. 在 Option Stores 中使用组合式函数(composables)
  3. 对于复杂的组合式函数,使用 Setup Stores
  4. 使用 Setup Stores 注入全局变量,如路由器(Router)
  5. 如何创建私有状态

1、不要创建无用的 getter

你并不需要为所有事情使用 getter。在 Vuex 中有一个普遍的误解,认为你应该总是通过 getter 访问状态。

这是不正确的。

当你需要从状态中计算出某些东西时,getter 是有用的,例如,如果你有一个待办事项列表,想知道有多少已完成,你可以为此创建一个 getter。

在生产代码中经常看到过这样的代码:

export default Vuex.Store({
  state: () => ({ counter: 0 }),
  getters: {
    // 完全无用的 getter
    getCount: state => state.counter,
  },
})

这在 Vuex 中只是不必要的样板代码,在 Pinia 中也是如此。你可以直接访问状态:

const counterStore = useCounterStore()
counterStore.counter // 0 ✅

PS:大多数时候你不需要 storeToRefs()(或 toRef())。你可以直接使用 store,Vue 的响应式真的很方便 😄。

2、在 Option Stores 中使用组合式函数

你可以在 option stores 中使用一些组合式函数,特别是那些持有状态且可写的组合式函数。例如,你可以使用 @vueuse/core 的 useLocalStorage() 将一些状态存储在浏览器的本地存储中。

import { useLocalStorage } from '@vueuse/core'
const useAuthStore = defineStore('auth', {
  state: () => ({
    user: useLocalStorage('pinia/user/login', 'alice'),
  }),
})

或者使用 refDebounced() 对 ref 的更改进行防抖处理:

import { refDebounced } from '@vueuse/core'
const useSearchStore = defineStore('search', {
  state: () => ({
    user: {
      text: refDebounced(/* ... */),
    },
  }),
})

3、在 Setup Stores 中使用复杂的组合式函数

在 Setup stores 中,你可以使用任何你想要的组合式函数。你可以连接到 websocket、蓝牙处理或甚至游戏手柄!

import { useWebSocket } from '@vueuse/core'
export const useServerInfoStore = defineStore('server-info', () => {
  const { status, data, send, open, close } = useWebSocket('ws://websocketurl')
  return {
    status,
    data,
    send,
    open,
    close,
  }
})

Pinia 会自动识别哪些是状态、getter 或动作。记住,必须从 setup 函数返回所有状态属性。

在 setup stores 中使用 inject()

你可以在 setup stores 中使用 inject() 来访问应用级别提供的变量,如路由器实例:

import { useRouter } from 'vue-router'
export const useAuthStore('auth', () => {
  const router = useRouter()
  function logout() {
    // 登出用户
    return router.push('/login')
  }
  return {
    logout
  }
})
使用嵌套 Stores 创建私有状态

setup stores 的一个黄金规则是返回每一个状态片段:

export const useAuthStore('auth', () => {
  const user = ref<User | null>(null)
  const token = ref<string | null>(null)
  // 我们必须返回 user 和 token
  return {
    user,
    token,
  }
})

但如果我们想要隐藏一些 store 中的状态怎么办?我们可以创建一个包含私有状态的嵌套 store:

export const usePrivateAuthState('auth-private', () => {
  const token = ref<string | null>(null)
  return {
    token,
  }
})
export const useAuthStore('auth', () => {
  const user = ref<User | null>(null)
  const privateState = usePrivateAuthState()
  privateState.token // 仅在此 store 中可访问
  return {
    user,
  }
})
在 SSR 中使用仅客户端状态

服务器端渲染(SSR)是提高应用性能的绝佳方式。然而,与仅客户端应用相比,它带来了一些额外的困难。例如,你无法访问 window、document 或任何其他特定于浏览器的 API,如本地存储。

在 Option Stores 中,这要求你使用 hydrate 选项告诉 Pinia 某些状态不应在客户端上进行 hydrate:

import { useLocalStorage } from '@vueuse/core'
const useAuthStore = defineStore('auth', {
  state: () => ({
    user: useLocalStorage('pinia/user/login', 'alice'),
  }),
  hydrate(state, initialState) {
    state.user = useLocalStorage('pinia/user/login', 'alice')
  },
})

在 Setup Stores 中,你可以使用 skipHydrate 辅助函数标记某些状态为仅客户端状态:

import { defineStore, skipHydrate } from 'pinia'
const useAuthStore = defineStore('auth', () => {
  const user = skipHydrate(useLocalStorage('pinia/user/login', 'alice'))
  return { user }
})

总结

当然还有许多其他的技巧可以分享,但这些是我认为比较有用的。此外,大多数人对它们并不了解。你有没有发现任何有用的 Pinia 技巧或窍门?

### 关于 Pinia 在 Vue 和 TypeScript 中的模板配置及常见问题排查 #### 1. **Pinia 基本概念** Pinia 是 Vue.js 的状态管理库,设计上更加简洁灵活,适合现代前端开发需求。它取消了 Vuex 中 `mutations` 和 `actions` 的区分,使得开发者可以直接定义响应式的 state 和 getter 方法[^2]。 --- #### 2. **Pinia 在 Vue 3 和 TypeScript 下的安装与初始化** 为了在 Vue 3 和 TypeScript 项目中使用 Pinia,需完成以下步骤: - **安装依赖** 使用 npm 或 yarn 安装 Pinia 及其类型声明文件: ```bash npm install pinia @pinia/nuxt ``` - **创建 Store 文件夹** 在项目的 `src` 目录下新建一个名为 `store` 的文件夹,并在其内部创建 store 文件。例如: ```typescript // src/store/app.ts import { defineStore } from 'pinia'; interface AppState { count: number; } export const useAppStore = defineStore('app', { state: (): AppState => ({ count: 0, }), getters: { doubleCount(): number { return this.count * 2; }, }, actions: { increment() { this.count++; }, }, }); ``` - **注册 Pinia 到应用实例** 在入口文件(如 `main.ts`)中导入并注册 Pinia 实例: ```typescript // main.ts import { createApp } from 'vue'; import App from './App.vue'; import { createPinia } from 'pinia'; const app = createApp(App); const pinia = createPinia(); app.use(pinia).mount('#app'); ``` --- #### 3. **常见问题及其解决方案** ##### a) **No Active Pinia Error** 如果遇到错误提示 `"getActivePinia was called with no active Pinia"`,通常是因为未正确注册 Pinia 实例或者尝试访问尚未挂载的状态对象[^3]。 解决办法: - 确保在根组件之前调用了 `createPinia()` 并通过 `use` 注册到 Vue 应用实例。 - 如果是在测试环境中发生此问题,则需要手动设置上下文环境变量 `setActivePinia(createPinia())`。 ##### b) **Import 错误** 当出现类似 `ERROR: No matching export in "node_modules/vue-demi/lib/index.mjs" for import "hasInjectionContext"` 这样的报错时,可能是由于 Vue 版本和 Pinia 版本不兼容引起的[^4]。 解决办法: - 检查当前使用的 Vue 和 Pinia 版本是否匹配。推荐组合为 Vue 3.x 和 Pinia >= 2.x。 - 清理缓存重新安装依赖项: ```bash rm -rf node_modules package-lock.json && npm install ``` ##### c) **优化构建性能** 为了避免 Pinia 被多次打包进入不同模块而导致体积膨胀,可以按照以下方式调整 Vite 构建配置[^5]: ```typescript // vite.config.ts import path from 'path'; import { defineConfig } from 'vite'; export default defineConfig(({ command }) => { return { build: { rollupOptions: { output: { manualChunks(id) { if (id.includes(path.resolve(__dirname, '/src/store/index.ts'))) { return 'vendor'; } }, }, }, }, }; }); ``` --- #### 4. **TypeScript 支持下的最佳实践** Pinia 对 TypeScript 提供了良好的支持,能够自动推断数据类型从而减少显式类型的冗余书写量。以下是几个建议来提升开发体验: - **利用接口约束 State 数据结构** 如上述示例中的 `AppState` 接口用于描述存储的数据字段及其类型。 - **Action 返回值类型标注** 当 action 函数有明确返回值时应为其指定具体类型: ```typescript async fetchUserData(): Promise<User> { const response = await api.get('/user'); return response.data as User; } ``` - **Getter 类型安全增强** Getter 方法同样可以通过 TypeScript 自动获取计算属性的结果类型: ```typescript getters: { filteredItems(state): Item[] { return state.items.filter(item => item.active); }, }, ``` --- #### 5. **调试技巧** - **日志记录** 启用插件功能以便观察每次 mutation 发生的变化过程: ```typescript import { createPinia } from 'pinia'; const pinia = createPinia(); pinia.use((context) => ({ subscribe(listener) { context.store.$subscribe((mutation, state) => listener(mutation, state)); }, })); ``` - **时间旅行调试工具** 结合官方提供的 DevTools 插件实现可视化追踪历史变更轨迹。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值