Chapter 04

Getters 计算

Getters 是 store 的计算属性——学会定义派生状态、跨 getter 引用、带参 getter,并理解它的缓存机制。

4.1 Getters 核心概念

Getter
基于 state(或其它 getter)计算出的派生值,等价于 store 层的 computed
缓存(Caching)
getter 结果会被缓存,只有依赖的响应式数据变化时才重新计算,多次访问不重复计算。
state 参数
选项式 getter 的第一个参数是 state,用箭头函数可自动推断类型。
this 上下文
用普通函数写 getter 时,this 指向整个 store,可访问其它 getter/action。
带参 getter
返回一个函数的 getter,可接收参数(如按 id 查找),但这种 getter 不缓存
组合式 getter
组合式 store 中用 computed(() => ...) 定义 getter,并 return 出来。

4.2 基础 Getter

export const useCartStore = defineStore("cart", {
  state: () => ({
    items: [] as { name: string; price: number; qty: number }[],
  }),
  getters: {
    // 箭头函数 + state 参数,自动推断返回类型
    itemCount: (state) => state.items.reduce((n, i) => n + i.qty, 0),
    totalPrice: (state) =>
      state.items.reduce((sum, i) => sum + i.price * i.qty, 0),
  },
});

4.3 访问其它 Getter

getter 之间可以互相引用。若要引用其它 getter,需用普通函数并通过 this 访问(此时应显式标注返回类型,帮助 TS 推断):

getters: {
  totalPrice: (state) =>
    state.items.reduce((s, i) => s + i.price * i.qty, 0),

  // 引用 totalPrice,需用普通函数拿 this,标注返回类型
  totalWithTax(): number {
    return Math.round(this.totalPrice * 1.13);
  },
},
箭头函数拿不到 this

箭头 getter 只能通过参数 state 访问状态,拿不到 this。需要引用其它 getter 时改用普通函数并标注返回类型。

4.4 带参数的 Getter

让 getter 返回一个函数,就能接收参数(比如按 id 查商品):

getters: {
  // 返回函数 → 可传参
  getItemById: (state) => {
    return (id: number) =>
      state.items.find((i) => i.id === id);
  },
},
// 组件中调用
const cart = useCartStore();
const item = cart.getItemById(42);
带参 getter 不缓存

返回函数的 getter 每次调用都会执行,失去缓存优势。如果结果昂贵且参数固定,考虑在组件里用 computed 缓存,或改造数据结构(如把数组转成 Map)。

4.5 缓存机制详解

Getter 的缓存和 Vue computed 完全一致:

场景是否重新计算
依赖的 state 未变,多次读取否(返回缓存)
依赖的 state 变化后再读取
带参 getter(返回函数)每次调用都执行
getter 内引用了非响应式的外部变量不会因它变化而更新(易踩坑)

4.6 组合式写法的 Getter

import { defineStore } from "pinia";
import { ref, computed } from "vue";

export const useCartStore = defineStore("cart", () => {
  const items = ref<Item[]>([]);

  // computed 即 getter,自动缓存
  const totalPrice = computed(() =>
    items.value.reduce((s, i) => s + i.price * i.qty, 0)
  );

  // 带参 getter:computed 返回函数
  const getItemById = computed(() => {
    return (id: number) => items.value.find((i) => i.id === id);
  });

  return { items, totalPrice, getItemById };
});

4.7 在模板中使用

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

const cart = useCartStore();
const { totalPrice, itemCount } = storeToRefs(cart);
</script>

<template>
  <p>共 {{ itemCount }} 件,合计 ¥{{ totalPrice }}</p>
</template>

4.8 小结与下一步