2.1 defineStore 签名
defineStore 是 Pinia 的核心 API,返回一个"use 函数"。它有两种调用形式:
- defineStore(id, options)
- 选项式:第二个参数是一个包含
state/getters/actions的对象。结构清晰,接近 Vuex。 - defineStore(id, setup)
- 组合式(Setup Store):第二个参数是一个 setup 函数,用
ref/computed/普通函数定义状态与逻辑,最后 return 暴露。 - id(Store ID)
- 字符串,全局唯一。DevTools 显示、SSR 状态序列化、持久化插件都靠它区分不同 store。
- use 函数
defineStore的返回值,习惯命名useXxxStore。在组件 setup 中调用它才拿到 store 实例。- Store 实例
- 调用 use 函数得到的响应式对象,直接读写 state、访问 getters、调用 actions。
- 选项对象里的 id
- 也可以把 id 写进选项对象:
defineStore({ id: "counter", state, ... }),效果等同第一个参数传 id。
2.2 选项式 Store(Options Store)
// stores/user.ts —— 选项式写法
import { defineStore } from "pinia";
export const useUserStore = defineStore("user", {
// state 必须是箭头函数返回对象(SSR 需要每次新建)
state: () => ({
name: "",
age: 0,
roles: [] as string[],
}),
// getters 接收 state,返回派生值
getters: {
isAdult: (state) => state.age >= 18,
displayName: (state) => state.name || "匿名",
},
// actions 用普通函数(不要用箭头),才能拿到 this
actions: {
setName(name: string) {
this.name = name;
},
async fetchUser(id: number) {
const res = await fetch(`/api/user/${id}`);
const data = await res.json();
this.name = data.name;
this.age = data.age;
},
},
});
state 必须是函数
选项式的 state 一定要写成返回对象的箭头函数 () => ({...}),不能直接给对象。SSR 场景下每个请求要一份全新的 state,函数才能保证不共享引用。
2.3 组合式 Store(Setup Store)
组合式写法把 store 当成一个 setup 函数:ref 就是 state,computed 就是 getter,普通函数就是 action,最后 return 出去。
// stores/user.ts —— 组合式写法(等价上面)
import { defineStore } from "pinia";
import { ref, computed } from "vue";
export const useUserStore = defineStore("user", () => {
// state
const name = ref("");
const age = ref(0);
const roles = ref<string[]>([]);
// getters
const isAdult = computed(() => age.value >= 18);
const displayName = computed(() => name.value || "匿名");
// actions
function setName(v: string) {
name.value = v;
}
async function fetchUser(id: number) {
const res = await fetch(`/api/user/${id}`);
const data = await res.json();
name.value = data.name;
age.value = data.age;
}
// 必须 return 才会暴露
return { name, age, roles, isAdult, displayName, setName, fetchUser };
});
组合式必须 return 全部 state
没有被 return 的 ref 不会成为 store 的 state,也不会被 DevTools、SSR、持久化识别。为了 SSR 正确工作,所有 state ref 都必须 return。
2.4 两种写法映射关系
| 选项式 | 组合式 | 作用 |
|---|---|---|
| state: () => ({ x: 0 }) | const x = ref(0) | 响应式状态 |
| getters: { g: s => ... } | const g = computed(() => ...) | 派生计算值 |
| actions: { a() {...} } | function a() {...} | 修改状态的方法 |
| this.x | x.value | 访问 state |
| (自动暴露) | return { ... } | 暴露成员 |
2.5 如何选择
选项式适合
• 团队从 Vuex 迁移,习惯 state/getters/actions 分区
• 结构简单、成员固定的 store
• 希望约束统一、可读性高
组合式适合
• 需要 watch、onMounted 等组合式 API
• 逻辑复杂、想复用组合函数
• 团队已全面拥抱 <script setup>
官方建议
两种写法功能完全等价,团队内保持一致最重要。新项目若已全面使用组合式 API,推荐 Setup Store;从 Vuex 迁移则用选项式过渡更平滑。
2.6 组合式 Store 的额外能力
组合式 store 能直接使用其它组合式 API,这是选项式做不到的:
import { defineStore } from "pinia";
import { ref, watch } from "vue";
import { useLocalStorage } from "@vueuse/core";
export const useSettingsStore = defineStore("settings", () => {
// 直接用 VueUse,自动同步 localStorage
const theme = useLocalStorage("theme", "light");
watch(theme, (v) => {
document.documentElement.dataset.theme = v;
});
return { theme };
});
2.7 小结与下一步
defineStore(id, options|setup)是唯一入口,返回useXxxStore函数。- 选项式 = state/getters/actions 三区;组合式 = ref/computed/function + return。
- 两者功能等价;组合式能用完整的组合式 API,选项式约束更强。
- 下一章深入 State——如何访问、修改、批量更新、重置和响应式解构。