Pinia

Store 的三大核心 state、getters 与 actions

掌握 Pinia Store 的 state、getters、actions 三个核心概念,学会定义和使用 Pinia 的状态管理单元。

🎯 引言

学会这篇文章,你能搞清楚 Pinia Store 里的三个核心部分:stategettersactions 分别负责什么。学完以后,你能够独立写出一个结构清晰、职责明确的 Store。


🧱 Store 的基本结构

一个 Store 通常长这样:

stores/counter.js
import { defineStore } from 'pinia';

export const useCounterStore = defineStore('counter', {
    state: () => ({
        count: 0,
    }),
    getters: {
        double() {
            return this.count * 2;
        },
    },
    actions: {
        add() {
            this.count++;
        },
    },
});

三部分分工:

  • state:存数据
  • getters:基于数据做计算
  • actions:改数据或执行业务逻辑
可以把 Store 想象成一个小仓库:state 是货架,getters 是价签/统计表,actions 是仓库操作员。

📦 state 存放数据

state 必须是一个函数,返回一个对象。Pinia 会自动把这个对象变成响应式数据。

stores/user.js
export const useUserStore = defineStore('user', {
    state: () => ({
        name: '张三',
        age: 20,
    }),
});

在组件中读取:

<template>
    <p>{{ user.name }},{{ user.age }} 岁</p>
</template>

<script setup>
import { useUserStore } from './stores/user.js';

const user = useUserStore();
</script>
state 一定要写成函数返回对象。如果直接写对象,多个应用实例可能会共享同一份数据,导致状态互相污染。

⚡ getters 做计算

getters 里的方法会根据 state 计算出一个新值,并且有缓存。只要依赖的 state 不变,多次访问不会重复计算。

stores/user.js
export const useUserStore = defineStore('user', {
    state: () => ({
        firstName: '',
        lastName: '',
    }),
    getters: {
        fullName() {
            return this.firstName + this.lastName;
        },
    },
});

使用:

<template>
    <p>全名:{{ user.fullName }}</p>
</template>
getter 里用 this.xxx 访问 state。getter 之间也可以互相访问,比如 this.otherGetter

🛠 actions 改数据

actions 里定义修改 state 的方法。简单修改可以直接做,复杂逻辑可以封装起来。

stores/counter.js
export const useCounterStore = defineStore('counter', {
    state: () => ({
        count: 0,
    }),
    actions: {
        add() {
            this.count++;
        },
        addN(n) {
            this.count += n;
        },
        reset() {
            this.count = 0;
        },
    },
});

组件中调用:

<template>
    <p>{{ counter.count }}</p>
    <button @click="counter.add">加一</button>
    <button @click="counter.addN(5)">加五</button>
    <button @click="counter.reset">重置</button>
</template>
在 action 里用 this.count 修改 state,不需要像 Vuex 那样提交 mutation。这是 Pinia 最省心的地方。

💡 什么时候该用哪个

场景用哪个例子
存储原始数据statecountuserInfo
需要基于数据计算gettersdoublefullName
修改数据或执行逻辑actionsaddlogin
不要把所有逻辑都堆在组件里,也不要把所有修改都直接写在模板中。简单的状态读取和展示交给模板,状态的修改建议集中到 Store 的 actions 中。

🧾 小节总结

  • Store 由 stategettersactions 三部分组成。
  • state 是函数返回的响应式对象,用来存数据。
  • getters 是基于 state 的计算属性,有缓存。
  • actions 是用来修改 state 或处理业务逻辑的方法。
  • 简单修改可以直接做,复杂修改建议放到 action 中。

❓ 知识问答

Q1:getter 和 action 都能访问 state,它们有什么区别?

getter 是“只读”的,用来计算和返回派生值;action 是用来“改写”状态的。一个负责算,一个负责改。

Q2:getter 里能调用 action 吗?

不推荐。getter 应该保持纯计算,不要产生副作用。需要副作用的请用 action。

Q3:action 里能访问 getter 吗?

可以,用 this.xxx 即可。

Q4:state 里能放函数吗?

不建议。state 只放数据,函数放到 actions 中。


🧪 小练习

补全下面的 useTodoStore,实现:

  1. todos 数组,每项有 idtextdone
  2. finishedCount getter 返回已完成数量。
  3. addTodo(text) action 添加新事项。
  4. toggle(id) action 切换对应事项的 done
stores/todo.js
import { defineStore } from 'pinia';

export const useTodoStore = defineStore('todo', {
    state: () => ({
        todos: [],
    }),
    getters: {
        // 请在这里编写 finishedCount
    },
    actions: {
        // 请在这里编写 addTodo 和 toggle
    },
});

🎉 恭喜你掌握了 Store 的三大核心!下一篇我们学习在组件中如何正确使用 Store,尤其是避免响应式丢失。