《Pinia 从入门到精通》Vue 3 官方状态管理 -- 进阶使用篇

《Pinia 从入门到精通》Vue 3 官方状态管理 – 基础入门篇
《Pinia 从入门到精通》Vue 3 官方状态管理 – 进阶使用篇
《Pinia 从入门到精通》Vue 3 官方状态管理 – 插件扩展篇

目录

    • Store 的模块化设计
      • 4.1 多模块结构设计
        • ✅ 推荐目录结构(中大型项目)
      • 4.2 定义独立模块 Store
      • 4.3 在组件中组合多个 Store
      • 4.4 跨模块调用(解耦调用)
      • 4.5 模块化 Store 的命名约定
      • 4.6 所有 Store 统一导出(可选)
      • ✅ 小结
    • 类型系统集成(TypeScript 支持)
      • 5.1 Store 的类型推导基础
      • 5.2 自定义类型(推荐)
        • ✅ 定义接口
        • ✅ 使用泛型定义 Store
      • 5.3 Setup Store(组合式写法)中的类型支持
      • 5.4 类型提示与 IDE 自动补全效果
      • 5.5 类型约束下的好处
      • ✅ 小结
    • 持久化存储与插件机制
      • 6.1 什么是 Pinia 插件?
      • 6.2 状态持久化插件:`pinia-plugin-persistedstate`
        • ✅ 安装
        • ✅ 注册插件
      • 6.3 使用持久化配置
      • 6.4 自定义持久化策略
      • 6.5 多模块下持久化组合
      • 6.6 自定义插件机制(进阶)
      • 6.7 生命周期钩子:`$subscribe` & `$onAction`
        • 1. `$subscribe` —— 监听状态变化
        • 2. `$onAction` —— 监听 Action 执行
      • ✅ 小结
    • 最佳实践总结与项目结构规范化设计
      • 7.1 推荐项目结构
      • 7.2 Store 命名规范
      • 7.3 状态设计原则
      • 7.4 Store 类型系统统一
      • 7.5 持久化策略标准化
      • 7.6 自动化导出 Store
      • 7.7 组合逻辑封装:`useXXXLogic`
      • ✅ 实战应用:多页面应用的 Store 模型设计范式
      • ✅ 统一 Store 模板(可直接复制)
    • ✅ 结语:Pinia 最佳实践总览图

Store 的模块化设计

随着项目规模扩大,单一 Store 会迅速膨胀,导致维护困难。
我们将从实际项目结构出发,讲解如何构建多模块 Store,支持清晰组织、职责分离和可维护性。
Pinia 提供了天然模块化的设计,每一个 Store 就是一个模块,天生支持按需加载、组合调用。


4.1 多模块结构设计

✅ 推荐目录结构(中大型项目)
src/
├── stores/
│   ├── index.ts            # 导出所有 store(可选)
│   ├── user.ts             # 用户模块
│   ├── auth.ts             # 登录认证模块
│   ├── cart.ts             # 购物车模块
│   └── product.ts          # 商品模块

每个模块都用 defineStore 定义自己的状态、逻辑、计算属性,保持内聚。


4.2 定义独立模块 Store

// stores/user.ts
import { defineStore } from 'pinia'export const useUserStore = defineStore('user', {state: () => ({name: '',email: ''}),actions: {setUser(name: string, email: string) {this.name = namethis.email = email}}
})
// stores/cart.ts
import { defineStore } from 'pinia'export const useCartStore = defineStore('cart', {state: () => ({items: [] as { id: number; name: string; qty: number }[]}),getters: {totalItems: (state) => state.items.reduce((sum, item) => sum + item.qty, 0)},actions: {addItem(item: { id: number; name: string; qty: number }) {this.items.push(item)}}
})

4.3 在组件中组合多个 Store

<script setup lang="ts">
import { useUserStore } from '@/stores/user'
import { useCartStore } from '@/stores/cart'const userStore = useUserStore()
const cartStore = useCartStore()function checkout() {console.log(`${userStore.name} 正在购买 ${cartStore.totalItems} 件商品`)
}
</script>

4.4 跨模块调用(解耦调用)

Pinia 中不同 Store 可相互独立调用,而无需手动注入依赖:

// stores/order.ts
import { defineStore } from 'pinia'
import { useUserStore } from './user'export const useOrderStore = defineStore('order', {actions: {submitOrder() {const userStore = useUserStore()console.log(`下单用户:${userStore.name}`)// ...业务逻辑}}
})

注意调用其他 Store 必须在 action 中动态获取,而不能在 Store 顶层直接引入(避免依赖环问题)。


4.5 模块化 Store 的命名约定

内容命名规则示例
Store 函数useXxxStoreuseUserStore
文件名模块名小写user.ts
Store ID与函数名一致的小写形式'user'
命名空间使用 id 区分,无需嵌套定义所有 Store 自动隔离

4.6 所有 Store 统一导出(可选)

你可以在 stores/index.ts 中统一导出,方便使用:

// stores/index.ts
export * from './user'
export * from './cart'
export * from './order'
// 使用
import { useUserStore, useCartStore } from '@/stores'

✅ 小结

模块化是构建大型 Vue 应用的核心策略,Pinia 以其函数式 Store 和天然隔离的 Store ID 设计,使模块化变得:

  • 更加清晰(每个模块职责单一)
  • 更易维护(按需加载,互不干扰)
  • 更易测试(每个 Store 独立可测试)

类型系统集成(TypeScript 支持)

Pinia 天生支持 TypeScript,基于其函数式 API 和明确的声明结构,使得类型推导更加自然、精准。


5.1 Store 的类型推导基础

最基础的 defineStore 形式在 TS 中已经具备类型提示能力:

export const useCounterStore = defineStore('counter', {state: () => ({count: 0,name: 'Counter Module'}),getters: {doubleCount: (state) => state.count * 2},actions: {increment() {this.count++}}
})

此时使用该 Store 时会自动获得类型提示:

const store = useCounterStore()
store.count       // number ✅
store.increment() // 自动补全 ✅

5.2 自定义类型(推荐)

虽然大多数场景下可自动推导,但我们仍推荐使用接口显式定义 stategettersactions 类型以获得更强可维护性。

✅ 定义接口
// types/store.d.ts
export interface CounterState {count: numbername: string
}export interface CounterGetters {doubleCount(state: CounterState): number
}export interface CounterActions {increment(): void
}
✅ 使用泛型定义 Store
import { defineStore, StoreDefinition } from 'pinia'
import type { CounterState, CounterActions, CounterGetters } from '@/types/store'export const useCounterStore: StoreDefinition<'counter', CounterState, CounterGetters, CounterActions> = defineStore('counter', {state: (): CounterState => ({count: 0,name: 'Counter Store'}),getters: {doubleCount: (state) => state.count * 2},actions: {increment() {this.count++}}
})

5.3 Setup Store(组合式写法)中的类型支持

对于复杂逻辑,我们可以使用组合式写法,即 setup 形式的 Store,TS 类型控制更灵活。

import { defineStore } from 'pinia'
import { ref, computed } from 'vue'export const useUserStore = defineStore('user', () => {const name = ref('Alice')const age = ref(25)const summary = computed(() => `${name.value}${age.value}岁)`)function setName(newName: string) {name.value = newName}return {name,age,summary,setName}
})

此种写法中,Pinia 能自动推导返回值类型,无需额外泛型,只要你在返回时显式写出 ref / computed


5.4 类型提示与 IDE 自动补全效果

  • State 属性:可识别为 ref<number> / ref<string>
  • Getter 属性:识别为 ComputedRef
  • Action 方法:自动推导参数和返回值类型

举个例子:

const userStore = useUserStore()
userStore.age.value     // number ✅
userStore.summary.value // string ✅
userStore.setName('Bob') // ✅

5.5 类型约束下的好处

特性说明
自动补全所有属性、方法可自动提示,无需手动查找
静态校验错误属性/参数在编译期即可发现,避免运行时异常
支持重构改名、移动属性时 IDE 可自动跟踪更新引用
接口复用同一份接口可复用在组件、接口、后端通讯中

✅ 小结

Pinia 在 TypeScript 项目中具备一流的类型体验:

  • 推荐用接口明确 State / Action / Getter 结构
  • 可选使用组合式写法提升灵活性与组合能力
  • 强类型推导贯穿编写、使用、调试各个环节

持久化存储与插件机制

本节将深入讲解如何使用 Pinia 插件 实现 Store 状态的持久化存储,以及自定义扩展 Store 功能。
在实际项目中,我们常常需要:

  • 保持用户登录信息,即使刷新页面也不丢失
  • 记住用户的主题偏好、语言设置
  • 在多个 Tab 页面之间共享状态

Pinia 原生支持插件机制,非常适合用于这些扩展场景。


6.1 什么是 Pinia 插件?

插件是一个函数,在每个 Store 实例创建时执行。你可以通过插件:

  • 添加全局状态
  • 劫持/监听 Store 生命周期
  • 自动同步状态到 localStorage / sessionStorage
  • 注入外部依赖(如 Axios)

6.2 状态持久化插件:pinia-plugin-persistedstate

最常用的第三方插件是 pinia-plugin-persistedstate,可自动将状态持久化到本地存储。

✅ 安装
npm install pinia-plugin-persistedstate
✅ 注册插件
// main.ts
import { createPinia } from 'pinia'
import piniaPluginPersistedstate from 'pinia-plugin-persistedstate'const pinia = createPinia()
pinia.use(piniaPluginPersistedstate)app.use(pinia)

6.3 使用持久化配置

只需在 defineStore 中增加 persist 配置:

// stores/user.ts
export const useUserStore = defineStore('user', {state: () => ({token: '',theme: 'light'}),persist: true // 默认使用 localStorage
})

刷新后依然保留 token 和主题偏好。


6.4 自定义持久化策略

persist: {enabled: true,strategies: [{key: 'user-token',storage: sessionStorage,paths: ['token'] // 只存 token}]
}
  • key:本地存储的键名
  • storage:可选 localStoragesessionStorage
  • paths:只持久化指定字段

6.5 多模块下持久化组合

每个模块 Store 都可独立配置 persist,互不影响:

// user.ts => 存 token 到 sessionStorage
persist: {storage: sessionStorage,paths: ['token']
}// settings.ts => 存 theme 到 localStorage
persist: {storage: localStorage,paths: ['theme']
}

6.6 自定义插件机制(进阶)

你可以自定义自己的插件来扩展 Store 功能:

// plugins/logger.ts
import type { PiniaPluginContext } from 'pinia'export function loggerPlugin({ store }: PiniaPluginContext) {store.$subscribe((mutation, state) => {console.log(`[${mutation.storeId}]`, mutation.type, mutation.events)})
}

注册插件:

pinia.use(loggerPlugin)

这样每次状态改变都会打印日志。


6.7 生命周期钩子:$subscribe & $onAction

Pinia 提供两种原生生命周期钩子:

1. $subscribe —— 监听状态变化
const userStore = useUserStore()userStore.$subscribe((mutation, state) => {console.log('状态发生变化:', mutation, state)
})
2. $onAction —— 监听 Action 执行
userStore.$onAction(({ name, args, after, onError }) => {console.log(`Action 被调用: ${name}`, args)after(() => console.log(`${name} 执行成功`))onError((err) => console.error(`${name} 报错`, err))
})

非常适合做埋点、错误日志等逻辑。


✅ 小结

Pinia 插件机制极为强大和灵活:

功能类型工具用途描述
本地持久化pinia-plugin-persistedstate自动同步状态到 Storage
状态监听$subscribe跟踪 State 的变化
行为监听$onAction跟踪 Action 的执行情况
自定义扩展插件函数注入工具、处理副作用、封装逻辑等

最佳实践总结与项目结构规范化设计

将基于前面内容,系统梳理一套企业级 Pinia 状态管理的最佳实践,从模块设计、命名规范、状态解耦、持久化、类型安全等多个维度,构建一个清晰、稳定、可维护、易扩展的 Store 架构体系。


7.1 推荐项目结构

src/
├── stores/                # 所有 Pinia Store 模块
│   ├── user.ts            # 用户相关状态
│   ├── auth.ts            # 权限与登录认证
│   ├── ui.ts              # UI 状态(如 sidebar)
│   ├── settings.ts        # 全局设置项
│   └── index.ts           # 自动导出所有 Store
├── types/                # Store 类型定义
│   └── store.d.ts
├── plugins/              # 自定义插件(如 router 注入、日志等)
├── composables/          # 组合逻辑封装,可配合 Store 使用

7.2 Store 命名规范

类型命名建议示例
Store 名称useXxxStoreuseUserStore
ID(storeId)模块名小写user, auth, ui
文件名模块名小写user.ts, auth.ts

命名统一,利于团队协作和自动化生成。


7.3 状态设计原则

  1. 一个模块职责单一,避免巨型 Store
  2. 状态最小化:只存 UI 需要的状态,不要存派生数据(放 getters)
  3. 与组件无关的数据放 Store,临时数据放组件
  4. 使用 ref, computed, watch 配合使用 Store 提高响应性控制

7.4 Store 类型系统统一

统一定义 Store 的 state, getters, actions 类型接口:

// types/store.d.ts
export interface UserState { name: string; age: number }
export interface UserActions { login(): void; logout(): void }
export interface UserGetters { isAdult: boolean }

结合 StoreDefinition

export const useUserStore: StoreDefinition<'user', UserState, UserGetters, UserActions> =defineStore('user', {state: (): UserState => ({ ... }),...})

优点:

  • 强类型保障
  • 便于重构
  • 编辑器智能提示清晰

7.5 持久化策略标准化

  • token、用户信息 → 存到 sessionStorage(浏览器关闭清空)
  • 用户偏好、主题设置 → 存到 localStorage(长期保存)
  • 配置统一封装成策略常量:
const localPersist = {storage: localStorage,paths: ['theme', 'language']
}

7.6 自动化导出 Store

// stores/index.ts
export * from './user'
export * from './auth'
export * from './settings'

支持模块自动导入:

import { useUserStore, useAuthStore } from '@/stores'

7.7 组合逻辑封装:useXXXLogic

业务逻辑不要堆在组件里,应该封装成组合逻辑:

// composables/useLogin.ts
export function useLogin() {const userStore = useUserStore()const doLogin = async (formData) => {await userStore.login(formData)router.push('/dashboard')}return { doLogin }
}
  • 提高复用性
  • 分离 UI 与业务逻辑
  • 组件更轻盈可测试

✅ 实战应用:多页面应用的 Store 模型设计范式

模块名称典型状态字段持久化常驻内存是否解耦 UI
usertoken, info
uisidebar, theme
authroles, routes
searchkeyword, filters❌(页面级)

✅ 统一 Store 模板(可直接复制)

// stores/xxx.ts
import { defineStore } from 'pinia'
import type { XxxState } from '@/types/store'export const useXxxStore = defineStore('xxx', {state: (): XxxState => ({ ... }),getters: {...},actions: {...},persist: {enabled: true,strategies: [...]}
})

✅ 结语:Pinia 最佳实践总览图

+-------------------------------+
|  模块划分清晰                |
|  ↓                           |
|  单一职责                    |
|  ↓                           |
|  类型定义接口统一            |
|  ↓                           |
|  Plugin 封装 & 逻辑抽离       |
|  ↓                           |
|  状态最小化 & 可持久化        |
|  ↓                           |
|  与 Router / API / UI 解耦    |
+-------------------------------+

Pinia 的设计理念是简单、透明、类型友好。配合组合式 API,它不仅可以替代 Vuex,还能帮助我们构建更现代、更可控、更高效的前端架构。


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/pingmian/78049.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

西甲001:奥萨苏纳VS塞维利亚

西甲001&#xff1a;奥萨苏纳VS塞维利亚 奥萨苏纳主场强势力擒塞维利亚 奥萨苏纳中场核心蒙卡约纳上轮联赛早段伤退&#xff0c;本轮将由巴勃罗-伊瓦涅斯顶替首发。当家射手布迪米尔状态爆棚&#xff0c;近两轮斩获3球&#xff0c;本赛季联赛已轰入18球创生涯新高&#xff0c;将…

C语言编程--15.四数之和

题目&#xff1a; 给你一个由 n 个整数组成的数组 nums &#xff0c;和一个目标值 target 。请你找出并返回满足下述全部条件且不重复的四元组 [nums[a], nums[b], nums[c], nums[d]] &#xff08;若两个四元组元素一一对应&#xff0c;则认为两个四元组重复&#xff09;&…

2025.04.23【探索工具】| STEMNET:高效数据排序与可视化的新利器

文章目录 1. STEMNET工具简介2. STEMNET的安装方法3. STEMNET常用命令 1. STEMNET工具简介 在生物信息学领域&#xff0c;分析和处理大规模数据集是研究者们面临的日常挑战。STEMNET工具应运而生&#xff0c;旨在提供一个强大的平台&#xff0c;用于探索和分析单细胞RNA测序&a…

Day-3 应急响应实战

应急响应实战一&#xff1a;Web入侵与数据泄露分析 1. Web入侵核心原理 ​​漏洞利用路径​​ 未授权访问&#xff1a;弱口令&#xff08;如空密码/默认口令&#xff09;、目录遍历漏洞代码注入攻击&#xff1a;JSP/ASP木马、PHP一句话木马&#xff08;利用eval($_POST[cmd])&…

两段文本比对,高亮出差异部分

用法一:computed <div class"card" v-if"showFlag"><div class"info">*红色背景为已删除内容&#xff0c;绿色背景为新增内容</div><el-form-item label"与上季度比对&#xff1a;"><div class"comp…

Python中的 for 与 迭代器

文章目录 一、for 循环的底层机制示例&#xff1a;手动模拟 for 循环 二、可迭代对象 vs 迭代器关键区别&#xff1a; 三、for 循环的典型应用场景1. 遍历序列类型2. 遍历字典3. 结合 range() 生成数字序列4. 遍历文件内容 四、迭代器的自定义实现示例&#xff1a;生成斐波那契…

Pytest教程:为什么Pytest要用插件模式?

目录 一、历史背景:测试框架的局限性与Pytest的设计哲学 1.1 早期测试框架的困境 1.2 Pytest的模块化设计 二、横向对比:插件机制如何让Pytest脱颖而出 2.1 与Unittest/Nose的对比 2.2 插件模式的架构优势 三、插件模式的核心优势解析 3.1 可扩展性:从单元测试到全链…

【深度】如何通过MCP实现多智能体之间的协同

来源&#xff1a;腾讯技术工程、infoQ、原力注入 自 OpenAI 于 2023 年发布函数调用功能以来&#xff0c;我一直在思考如何构建一个开放的智能体与工具使用生态系统。随着基础模型愈发智能化&#xff0c;智能体与外部工具、数据和 API 的交互能力却日益碎片化&#xff1a;开发…

NVIDIA自动驾驶安全与技术读后感

ll在阅读了 NVIDIA 自动驾驶安全报告后&#xff0c;我对该公司致力于推进自动驾驶汽车&#xff08;AV&#xff09;技术、同时优先考虑安全和标准化的承诺印象深刻。它揭示了 NVIDIA 在功能安全、法规合规性以及与全球标准组织合作方面的严谨态度。    报告中最引人注目的部分…

关于nginx,负载均衡是什么?它能给我们的业务带来什么?怎么去配置它?

User 关于nginx&#xff0c;我还想知道&#xff0c;负载均衡是什么&#xff1f;它能为我的业务带来什么&#xff1f;怎么去配置它&#xff1f; Assistant 负载均衡是 Nginx 另一个非常强大的功能&#xff0c;也是构建高可用、高性能应用的关键技术之一。我们来详细了解一下。 …

前端如何优雅地对接后端

作为一名前端开发者&#xff0c;与后端对接是我们日常工作中不可避免的一部分。从API设计的理解到错误处理的优雅实现&#xff0c;前端需要的不只是调用接口的代码&#xff0c;更是一种协作的艺术。本文将从Vue 3项目出发&#xff0c;分享如何与后端高效协作&#xff0c;减少联…

PYTHON用几何布朗运动模型和蒙特卡罗MONTE CARLO随机过程模拟股票价格可视化分析耐克NKE股价时间序列数据

原文链接&#xff1a;http://tecdat.cn/?p27099 金融资产/证券已使用多种技术进行建模。该项目的主要目标是使用几何布朗运动模型和蒙特卡罗模拟来模拟股票价格。该模型基于受乘性噪声影响的随机&#xff08;与确定性相反&#xff09;变量&#xff08;点击文末“阅读原文”获取…

头歌之动手学人工智能-机器学习 --- PCA

目录 第1关&#xff1a;维数灾难与降维 第2关&#xff1a;PCA算法流程 任务描述 编程要求 测试说明 第3关&#xff1a;sklearn中的PCA 任务描述 编程要求 测试说明 第1关&#xff1a;维数灾难与降维 第2关&#xff1a;PCA算法流程 任务描述 本关任务&#xff1a;补充…

IOMUXC_SetPinMux的0,1参数解释

IOMUXC_SetPinMux(IOMUXC_ENET1_RX_DATA0_FLEXCAN1_TX, 0); 这里的第二个参数 0 实际上传递给了 inputOnfield&#xff0c;它控制的是 SION&#xff08;Software Input On&#xff09;位。 当 inputOnfield 为 0 时&#xff0c;SION 关闭&#xff0c;此时引脚的输入/输出方向由…

express响应设置 以及redirect,download,json.sendFdile

Express 中常用响应方法 的整理&#xff0c;包括设置响应头、重定向、下载、发送 JSON、发送文件等&#x1f447; &#x1f4e4; 一、设置响应头与状态码 设置状态码 res.status(404).send(Not Found);设置响应头 res.set(Content-Type, text/plain); // 设置内容类型 res.s…

深度学习-数值稳定性和模型初始化

到目前为止&#xff0c;我们实现的每个模型都是根据某个预先制定的分布来初始化模型的参数&#xff0c;有人会认为初始化方案时理所当然的&#xff0c;忽略了如何做出这些选择的细节&#xff0c;甚至有人可能会觉得&#xff0c;初始化方案的选择并不是特别重要&#xff0c;实际…

SFINAE(Substitution Failure Is Not An Error)

C 中的 SFINAE&#xff08;替换失败并非错误&#xff09; SFINAE&#xff08;Substitution Failure Is Not An Error&#xff09;是 C 模板元编程的核心机制之一&#xff0c;允许在编译时根据类型特性选择不同的模板实现。以下通过代码示例和底层原理&#xff0c;逐步解析 SFI…

【Python笔记 04】输入函数、转义字符

一、Input 输入函数 prompt是提示&#xff0c;会在控制台显示&#xff0c;用作提示函数。 name input("请输入您的姓名&#xff1a;") print (name)提示你输入任意信息&#xff1a; 输入input test后回车&#xff0c;他输出input test 二、常用的转义字符 只讲…

什么是量子计算?它能做什么?

抛一枚硬币。要么正面朝上&#xff0c;要么反面朝上&#xff0c;对吧&#xff1f;当然&#xff0c;那是在我们看到硬币落地的结果之后。但当硬币还在空中旋转时&#xff0c;它既不是正面也不是反面&#xff0c;而是正面和反面都有一定的可能性。 这个灰色地带就是量子计算的简…

入门 Go 语言

本专栏的 Go 语言学习参考了B站UP 软件工艺师的视频 本节需要&#xff1a; Go 语言环境VSCode 安装环境 下载 Go 环境&#xff0c;并安装下载 VSCode&#xff0c;安装。在 VSCode 中安装 Go 扩展&#xff1a; 接下来就可以编写 Go 语言了 第一条 Go Go 语言是一种编译型…