Skip to content

Pinia: 新一代 Vue 状态管理

Pinia 是 Vue 的官方状态管理库,它被设计为类型安全、模块化且对组合式 API 非常友好。相比于 Vuex,Pinia 的 API 更简洁,模板中访问 store 更直接,并且提供了出色的 TypeScript 支持。

为什么选择 Pinia?

  • 更简单的 API:没有 mutations,只有 state、getters 和 actions。Actions 可以是同步或异步的。
  • 完整的 TypeScript 支持:无需创建复杂的类型声明即可获得完美的类型推断。
  • 模块化设计:每个 store 都是一个独立的模块,可以按需导入和使用。
  • 极轻量:体积非常小,只有约 1KB。
  • 插件支持:可以通过插件扩展 Pinia 的功能。

安装与配置

首先,安装 Pinia:

bash
npm install pinia

然后,在你的 main.js 文件中创建并使用 Pinia 实例:

src/main.js

js
import { createApp } from 'vue'
import { createPinia } from 'pinia'
import App from './App.vue'

const app = createApp(App)

app.use(createPinia()) // 创建并使用 Pinia 实例
app.mount('#app')

定义 Store

在 Pinia 中,store 是通过 defineStore() 定义的。按照惯例,我们通常在 src/stores/ 目录下创建 store 文件。

src/stores/counter.js

js
import { defineStore } from 'pinia'

// 第一个参数是 store 的唯一 ID
export const useCounterStore = defineStore('counter', {
  // state: 返回一个函数,防止在服务端渲染时交叉请求导致数据状态污染
  state: () => ({
    count: 0,
    name: 'Eduardo'
  }),
  // getters: 类似于组件的 computed 属性
  getters: {
    doubleCount: (state) => state.count * 2,
    doubleCountPlusOne() {
      // 可以通过 `this` 访问其他 getter
      return this.doubleCount + 1
    }
  },
  // actions: 类似于组件的 methods,可以是异步的
  actions: {
    increment() {
      this.count++
    },
    randomizeCounter() {
      this.count = Math.round(100 * Math.random())
    }
  }
})

在组件中使用 Store

在组件的 <script setup> 中,你可以直接导入并调用 store 的 hook 函数来获取 store 实例。

CounterComponent.vue

vue
<script setup>
import { useCounterStore } from '@/stores/counter'

// 获取 store 实例
const counter = useCounterStore()

//可以直接在模板中使用 store 的 state、getters 和 actions
// counter.count
// counter.doubleCount
// counter.increment()
</script>

<template>
  <div>
    <p>Count: {{ counter.count }}</p>
    <p>Double Count: {{ counter.doubleCount }}</p>
    <button @click="counter.increment">Increment</button>
  </div>
</template>

解构 Store

如果你直接从 Pinia store 中解构,会破坏其响应性。为了解决这个问题,可以使用 storeToRefs

vue
<script setup>
import { storeToRefs } from 'pinia'
import { useCounterStore } from '@/stores/counter'

const counter = useCounterStore()

// `storeToRefs` 会将 state 和 getters 转换为响应式的 ref
const { count, doubleCount } = storeToRefs(counter)
const { increment } = counter // actions 可以直接解构
</script>

<template>
  <div>
    <p>Count: {{ count }}</p>
    <p>Double Count: {{ doubleCount }}</p>
    <button @click="increment">Increment</button>
  </div>
</template>

Pinia 以其简洁和强大的特性,正在迅速成为 Vue 3 项目中状态管理的首选方案。