Chapter 03

State 状态

State 是 store 的核心——学会定义、访问、直接修改、批量更新、重置,以及用 storeToRefs 保持解构后的响应式。

3.1 State 的核心概念

state 工厂函数
选项式中返回初始状态的 () => ({...}),每次实例化 store 调用一次。
直接修改
Pinia 允许 store.count++ 这样直接改,不需要像 Vuex 那样 commit mutation。
$patch
store 实例方法,一次性修改多个字段,DevTools 中记录为单次变更,比多次单独赋值更高效。
$reset
把 state 重置回 state 工厂函数返回的初始值(仅选项式 store 内置)。
$state
store 的整个 state 对象,可读取也可整体替换:store.$state = {...}
storeToRefs
把 store 解构成一组 ref,保留响应式;直接解构会丢失响应式。

3.2 访问 State

const user = useUserStore();

// 读取:直接点属性
console.log(user.name);
console.log(user.age);

在模板中同样直接用:{{ user.name }}。store 本身是响应式代理,读到的永远是最新值。

3.3 直接修改

const counter = useCounterStore();

counter.count++;              // ✅ 合法,Pinia 无 mutations
counter.count = 42;           // ✅ 直接赋值
counter.list.push(1);         // ✅ 数组方法也响应式
为什么可以直接改?

Vuex 要求所有变更走 mutation 是为了"可追踪"。Pinia 靠 Vue 3 的响应式系统 + DevTools 插件即可追踪任意变更,因此取消了 mutation 这层样板代码。

3.4 $patch 批量更新

当要同时改多个字段,用 $patch 合并成一次更新(DevTools 记为一条记录,也减少触发次数):

// 对象形式:浅合并
user.$patch({
  name: "张三",
  age: 28,
});

// 函数形式:适合数组/复杂逻辑
cart.$patch((state) => {
  state.items.push({ id: 1, qty: 2 });
  state.total += 20;
});
对象形式 vs 函数形式

对象形式做浅合并,适合替换整字段;涉及数组 push/splice 或依赖当前值的计算时,用函数形式更清晰高效。

3.5 $reset 重置

const form = useFormStore();

form.name = "临时输入";
form.$reset();      // 回到 state() 定义的初始值
组合式 store 没有内置 $reset

$reset 依赖"初始 state 工厂函数",只有选项式 store 才自带。组合式 store 需要自己写一个 reset action,把每个 ref 手动赋回初值。

// 组合式 store 手写 reset
export const useFormStore = defineStore("form", () => {
  const name = ref("");
  const email = ref("");
  function reset() {
    name.value = "";
    email.value = "";
  }
  return { name, email, reset };
});

3.6 $state 整体替换

// 读取整个 state
console.log(user.$state);

// 整体替换(常用于 SSR 注水 / 恢复本地缓存)
user.$state = { name: "李四", age: 30, roles: ["admin"] };

3.7 storeToRefs:解构不丢响应式

直接解构 store 会丢失响应式——因为解构出来的是普通值的快照:

// ❌ 错误:name、age 变成一次性快照,不再更新
const { name, age } = useUserStore();

正确做法是用 storeToRefs,它把 state 和 getters 转成 ref,保留响应式:

import { storeToRefs } from "pinia";

const user = useUserStore();
// ✅ name、age、isAdult 都是响应式 ref
const { name, age, isAdult } = storeToRefs(user);

// actions 直接从 store 解构即可(函数无需响应式)
const { setName, fetchUser } = user;
storeToRefs 不含 actions

storeToRefs 只提取 state 和 getters,不会包含 action(函数不需要响应式包装)。action 直接从 store 实例解构或用 store.action() 调用。

3.8 在模板中的对比

<script setup lang="ts">
import { storeToRefs } from "pinia";
import { useUserStore } from "@/stores/user";

const store = useUserStore();
const { name, isAdult } = storeToRefs(store);
</script>

<template>
  <!-- ref 在模板里自动解包,直接用 -->
  <p>{{ name }} · {{ isAdult ? "成年" : "未成年" }}</p>
  <button @click="store.setName('王五')">改名</button>
</template>

3.9 小结与下一步