defineStore 是 Pinia (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 接收两个参数:
- id (必填): 字符串,Store 的唯一标识符。
- 配置对象 或 设置函数: 定义 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 的三种方式
- 直接修改: store.count++ (仅限 Setup Store 或非解构情况)。
- 批量修改 ($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()
常见最佳实践
- 命名规范: 函数名通常以 use 开头 (如 useUserStore),id 使用复数或名词 (如 'user', 'cart')。
- 文件结构: 通常在 src/stores/ 目录下按模块存放,例如 src/stores/user.js, src/stores/products.js。
- 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 }
})
- 避免直接解构 State: 永远记住 const { count } = store 会导致 count 失去响应性。务必使用
storeToRefs。
总结对比
| 特性 | Option Store | Setup Store |
|---|---|---|
| 语法风格 | 类似 Vuex (state, getters, actions 对象) | 类似 Vue Composition API (setup 函数) |
| This 上下文 | 在 getters/actions 中使用 this |
不使用 this,直接访问变量 |
| 逻辑复用 | 较难,需提取外部函数 | 容易,可直接调用 Composables |
| 推荐场景 | 老项目迁移,喜欢结构化配置 | 新项目,需要复杂逻辑组合 |