Pinia中defineStore的使用方法

defineStorePinia (Vue.js 的官方状态管理库) 中用于定义 Store 的核心函数。它取代了 Vuex 中的 store 模块定义方式,提供了更简洁的 API 和更好的 TypeScript 支持。

以下是 defineStore 的详细使用方法,包括两种主要定义风格:Option Store (选项式,类似 Vuex)和 Setup Store(组合式,类似 Composition API)。

基础安装与引入

首先确保已安装 Pinia 并在 Vue 应用中注册:

bash 复制代码
npm install pinia
# 或
yarn add pinia

在 main.js / main.ts 中注册:

js 复制代码
import { createApp } from 'vue'
import { createPinia } from 'pinia'
import App from './App.vue'

const app = createApp(App)
const pinia = createPinia()

app.use(pinia)
app.mount('#app')

定义 Store 的两种方式

defineStore 接收两个参数:

  1. id (必填): 字符串,Store 的唯一标识符。
  2. 配置对象 或 设置函数: 定义 state, getters, actions。

方式一:Option Store (选项式)

适合从 Vuex 迁移过来的项目,结构清晰。

js 复制代码
// stores/counter.js
import { defineStore } from 'pinia'

export const useCounterStore = defineStore('counter', {
  // 1. State: 返回初始状态的函数
  state: () => ({
    count: 0,
    name: 'Eduardo'
  }),

  // 2. Getters: 类似计算属性
  getters: {
    doubleCount: (state) => state.count * 2,
    // 使用 this 访问其他 getter
    doubleCountPlusOne() {
      return this.doubleCount + 1
    }
  },

  // 3. Actions: 方法,支持同步和异步
  actions: {
    increment() {
      this.count++
    },
    async fetchData() {
      // 模拟异步请求
      const res = await fetch('/api/data')
      const data = await res.json()
      this.name = data.name
    },
    // 修改多个 state
    setCount(newCount) {
      this.count = newCount
    }
  }
})

方式二:Setup Store (组合式)

推荐在新项目中使用,逻辑更灵活,可以直接使用 ref , computed , async/await

js 复制代码
// stores/counter.js
import { defineStore } from 'pinia'
import { ref, computed } from 'vue'

export const useCounterStore = defineStore('counter', () => {
  // 1. State: 使用 ref
  const count = ref(0)
  const name = ref('Eduardo')

  // 2. Getters: 使用 computed
  const doubleCount = computed(() => count.value * 2)

  // 3. Actions: 普通函数 (this 不可用,直接访问变量)
  function increment() {
    count.value++
  }

  async function fetchData() {
    const res = await fetch('/api/data')
    const data = await res.json()
    name.value = data.name
  }

  // 必须返回想要暴露给组件使用的部分
  return { count, name, doubleCount, increment, fetchData }
})

在组件中使用 Store

无论使用哪种定义方式,使用方法都是一样的。 在 script setup 中使用 (推荐)

vue 复制代码
<template>
  <h1>{{ store.name }}</h1>
  <p>计数: {{ store.count }}</p>
  <p>双倍: {{ store.doubleCount }}</p>
  <button @click="store.increment">增加</button>
  <button @click="changeName">修改名字</button>
</template>

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

// 1. 获取 Store 实例
const store = useCounterStore()

// 【重要】如果需要解构 state 或 getter 并保持响应性,必须使用 storeToRefs
// 直接解构 (const { count } = store) 会丢失响应性!
const { count, doubleCount, name } = storeToRefs(store)

// Actions 可以直接解构,不需要 storeToRefs
const { increment, fetchData } = store

// 示例:调用 action
const changeName = () => {
  store.name = 'New Name' // 直接修改
  // 或者 store.$patch({ name: 'New Name' })
}
</script>

在 Options API (data, methods) 中使用

js 复制代码
export default {
  setup() {
    const counterStore = useCounterStore()
    return { counterStore }
  },
  methods: {
    add() {
      this.counterStore.increment()
    }
  }
}

核心概念详解

A. 修改 State 的三种方式

  1. 直接修改: store.count++ (仅限 Setup Store 或非解构情况)。
  2. 批量修改 ($patch): 性能更好,适合修改多个字段。
js 复制代码
store.$patch({
  count: store.count + 1,
  name: 'Updated'
})

// 或者使用函数形式处理复杂逻辑
store.$patch((state) => {
  state.items.push({ name: 'new item' })
  state.hasChanged = true
})

替换整个 State: store.$state = { count: 0, name: '...' }

B. 订阅状态变化

可以使用 $subscribe 监听 state 的变化(常用于持久化到 localStorage 或发送日志)。

js 复制代码
store.$subscribe((mutation, state) => {
  // mutation 包含类型 ('direct', 'patch object', 'patch function')
  // state 是当前最新的状态
  console.log('State changed:', state)
  localStorage.setItem('my-store', JSON.stringify(state))
})

C. 重置 State

调用 $reset() 可以将 state 重置为初始值。

注意:这只在 Option Store 或 Setup Store 中返回了初始值时有效。

js 复制代码
store.$reset()

常见最佳实践

  1. 命名规范: 函数名通常以 use 开头 (如 useUserStore),id 使用复数或名词 (如 'user', 'cart')。
  2. 文件结构: 通常在 src/stores/ 目录下按模块存放,例如 src/stores/user.js, src/stores/products.js。
  3. TypeScript 支持: Pinia 对 TS 支持极佳。在 Setup Store 中,TS 可以自动推断类型;在 Option Store 中,可以通过泛型定义类型。
js 复制代码
// TS 示例 (Setup Store)
export const useUserStore = defineStore('user', () => {
  const name = ref<string>('')
  const age = ref<number>(0)
  return { name, age }
})
  1. 避免直接解构 State: 永远记住 const { count } = store 会导致 count 失去响应性。务必使用 storeToRefs

总结对比

特性 Option Store Setup Store
语法风格 类似 Vuex (state, getters, actions 对象) 类似 Vue Composition API (setup 函数)
This 上下文 在 getters/actions 中使用 this 不使用 this,直接访问变量
逻辑复用 较难,需提取外部函数 容易,可直接调用 Composables
推荐场景 老项目迁移,喜欢结构化配置 新项目,需要复杂逻辑组合
相关推荐
_Eleven7 小时前
Pinia vs Vuex 深度解析与完整实战指南
前端·javascript·vue.js
技术狂小子7 小时前
# 一个 Binder 通信中的多线程同步问题
javascript·vue.js
阿懂在掘金8 小时前
Vue 表单避坑(二):多个 v-model 同时更新,为什么数据丢了?
前端·vue.js
an3174210 小时前
解决 VSCode 中 ESLint 格式化不生效问题:新手也能看懂的配置指南
前端·javascript·vue.js
青青家的小灰灰12 小时前
Vue 3 新标准:<script setup> 核心特性、宏命令与避坑指南
前端·vue.js·面试
SuperEugene12 小时前
路由与布局骨架篇:布局系统 | 头部、侧边栏、内容区、面包屑的拆分与复用
前端·javascript·vue.js
最强僚机斯内克12 小时前
Vue 3 + Vite 自动引入插件完整指南(unplugin-vue-components,unplugin-auto-import)
vue.js
滕青山13 小时前
个人所得税计算器 在线工具核心JS实现
前端·javascript·vue.js
阿懂在掘金13 小时前
Vue 表单避坑(一):为什么 v-model 绑定对象属性会偷偷修改父组件数据?
前端·vue.js