学习 Pinia 状态管理【Plan - May - Week 2】

一、定义 Store

Store 由 defineStore() 定义,它的第一个参数要求独一无二的id

import { defineStore } from 'pinia'export const useAlertsStore = defineStore('alert', {// 配置
})
  • 最好使用以 use 开头且以 Store 结尾 (比如 useUserStoreuseCartStoreuseProductStore) 来命名 defineStore() 的返回值
  • Pinia 将用传入的 id 来连接 store 和 devtools
  • defineStore() 的第二个参数可接受两类值:Setup 函数或 Option 对象。

1、Option Store

与 Vue 的选项式 API 类似,可以传入一个带有 stateactionsgetters 属性的 Option 对象

export const useCounterStore = defineStore('counter', {
state: () => ({ count: 0, name: 'Tomato' }),
getters: {doubleCount: (state) => state.count * 2,
},
actions: {increment() {this.count++},
},
})
  • state 相当于 store 的数据,getters 相当于 store 的计算属性,action 相当于 store 的方法

2、Setup Store

Setup Store 与 Vue 组合式 API 的 setup 函数 相似

export const useCounterStore = defineStore('counter', () => {
const count = ref(0)
const doubleCount = computed(() => count.value * 2)
function increment() {count.value++
}return { count, doubleCount, increment }
})
  • ref() 相当于 state 属性
  • computed() 相当于 getters
  • function() 相当于 actions
  • 要让 pinia 正确识别 state必须在 setup store 中返回 state 的所有属性。因此不能在 store 中使用私有属性。
  • Setup store 也可以依赖于全局提供的属性,比如路由。任何应用层面提供的属性都可以在 store 中使用 inject() 访问,就像在组件中一样
import { inject } from 'vue'
import { useRoute } from 'vue-router'
import { defineStore } from 'pinia'export const useSearchFilters = defineStore('search-filters', () => {const route = useRoute()// 这里假定 `app.provide('appProvided', 'value')` 已经调用过const appProvided = inject('appProvided')// ...return {// ...}
})

3、使用 Store

store 实例需要像使用 <script setup> 调用 useStore() 才会被创建

<script setup>
import { useCounterStore } from '@/stores/counter'const store = useCounterStore()
</script>
  • store 是一个用 reactive 包装的对象,这意味着不需要在 getters 后面写 .value,就像 setup 中的 props 一样,所以不能对它进行解构
<script setup>
import { useCounterStore } from '@/stores/counter'
import { computed } from 'vue'const store = useCounterStore()// 这就和直接解构 `props` 一样
const { name, doubleCount } = store
name // 将始终是 "Tomato"
doubleCount // 将始终是 0
setTimeout(() => {store.increment()
}, 1000)
// 这样写是响应式的,也可以直接使用 `store.doubleCount`
const doubleValue = computed(() => store.doubleCount)
</script>

4、从 Store 解构

使用 storeToRefs() 保持属性从 store 中提取时仍然保持其响应性

  • 它可以为每一个响应式属性创建引用
<script setup>
import { storeToRefs } from 'pinia'
const store = useCounterStore()
// `name` 和 `doubleCount` 是响应式的 ref
// 同时通过插件添加的属性也会被提取为 ref
// 并且会跳过所有的 action 或非响应式 (不是 ref 或 reactive) 的属性
const { name, doubleCount } = storeToRefs(store)
// 作为 action 的 increment 可以直接解构
const { increment } = store
</script>

二、Pinia

1、使用 Pinia

使用 Pinia可以获得以下功能:

  • 测试工具集
  • 插件:可通过插件扩展 Pinia 功能
  • 为 JS 开发者提供适当的 TypeScript 支持以及自动补全功能。
  • 支持服务端渲染
  • Devtools 支持
    • 追踪 actions、mutations 的时间线
    • 在组件中展示它们所用到的 Store
    • 让调试更容易的 Time travel
  • 热更新
    • 不必重载页面即可修改 Store
    • 开发时可保持当前的 State

2、基础示例

创建 Store

// stores/counter.js
import { defineStore } from 'pinia'export const useCounterStore = defineStore('counter', {
state: () => {return { count: 0 }
},
// 也可以这样定义
// state: () => ({ count: 0 })
actions: {increment() {this.count++},
},
})

使用 store

<script setup>
import { useCounterStore } from '@/stores/counter'const counter = useCounterStore()counter.count++
// 自动补全!
counter.$patch({ count: counter.count + 1 })
// 或使用 action 代替
counter.increment()
</script><template>
<!-- 直接从 store 中访问 state -->
<div>Current Count: {{ counter.count }}</div>
</template>
  • 使用函数定义 Store
export const useCounterStore = defineStore('counter', () => {const count = ref(0)function increment() {count.value++}return { count, increment }
})

3、对比 Vuex

  • Pinia 已经实现了我们在 Vuex 5 中想要的大部分功能
  • 与 Vuex 相比,Pinia 不仅提供了一个更简单的 API,也提供了符合组合式 API 风格的 API,最重要的是,搭配 TypeScript 一起使用时有非常可靠的类型推断支持
  • Pinia 同样要经过 RFC 流程,并且其 API 也已经进入稳定状态
  • 无需要创建自定义的复杂包装器来支持 TypeScript,一切都可标注类型,API 的设计方式是尽可能地利用 TS 类型推理
  • 无过多的魔法字符串注入,只需要导入函数并调用它们
  • 无需要动态添加 Store,它们默认都是动态的
  • ……

三、Action

Action 相当于组件中的 method。它们可以通过 defineStore() 中的 actions 属性来定义

export const useCounterStore = defineStore('main', {
state: () => ({count: 0,
}),
actions: {increment() {this.count++},randomizeCounter() {this.count = Math.round(100 * Math.random())},
},
})
  • 类似 getter,action 也可通过 this 访问整个 store 实例
  • action 可以是异步的
export const useUsers = defineStore('users', {state: () => ({userData: null,// ...}),actions: {async registerUser(login, password) {try {this.userData = await api.post({ login, password })showTooltip(`Welcome back ${this.userData.name}!`)} catch (error) {showTooltip(error)// 让表单组件显示错误return error}},},
})
  • 调用
<script setup>
const store = useCounterStore()
// 将 action 作为 store 的方法进行调用
store.randomizeCounter()
</script>
<template><!-- 即使在模板中也可以 --><button @click="store.randomizeCounter()">Randomize</button>
</template>

1、访问其他 store 的 action

直接调用

import { useAuthStore } from './auth-store'export const useSettingsStore = defineStore('settings', {
state: () => ({preferences: null,// ...
}),
actions: {async fetchUserPreferences() {const auth = useAuthStore()if (auth.isAuthenticated) {this.preferences = await fetchPreferences()} else {throw new Error('User must be authenticated')}},
},
})

2、使用选项式 API 的用法

使用 setup()

<script>
import { useCounterStore } from '../stores/counter'
export default defineComponent({
setup() {const counterStore = useCounterStore()return { counterStore }
},
methods: {incrementAndPrint() {this.counterStore.increment()console.log('New Count:', this.counterStore.count)},
},
})
</script>

3、订阅 action

通过 store.$onAction() 来监听 action 和它们的结果

  • 传递给它的回调函数会在 action 本身之前执行
  • after 表示在 promise 解决之后,允许你在 action 解决后执行一个回调函数
  • onError 允许你在 action 抛出错误或 reject 时执行一个回调函数
const unsubscribe = someStore.$onAction(({name, // action 名称store, // store 实例,类似 `someStore`args, // 传递给 action 的参数数组after, // 在 action 返回或解决后的钩子onError, // action 抛出或拒绝的钩子}) => {// 为这个特定的 action 调用提供一个共享变量const startTime = Date.now()// 这将在执行 "store "的 action 之前触发。console.log(`Start "${name}" with params [${args.join(', ')}].`)// 这将在 action 成功并完全运行后触发。// 它等待着任何返回的 promiseafter((result) => {console.log(`Finished "${name}" after ${Date.now() - startTime}ms.\nResult: ${result}.`)})// 如果 action 抛出或返回一个拒绝的 promise,这将触发onError((error) => {console.warn(`Failed "${name}" after ${Date.now() - startTime}ms.\nError: ${error}.`)})}
)// 手动删除监听器
unsubscribe()
  • action 会绑定在 store 组件的 setup() 内,当组件被卸载时,它们将被自动删除
  • 可以将 true 作为第二个参数传递给 action 订阅器,可以实现即便在组件卸载之后仍会被保留
<script setup>
const someStore = useSomeStore()
// 此订阅器即便在组件卸载之后仍会被保留
someStore.$onAction(callback, true)
</script>

四、插件

1、简介

Pinia 插件是一个函数,可以选择性地返回要添加到 store 的属性。它接收一个可选参数,即 context

export function myPiniaPlugin(context) {
context.pinia // 用 `createPinia()` 创建的 pinia。
context.app // 用 `createApp()` 创建的当前应用(仅 Vue 3)。
context.store // 该插件想扩展的 store
context.options // 定义传给 `defineStore()` 的 store 的可选对象。
// ...
}pinia.use(myPiniaPlugin)

2、扩展 Store

  • 直接通过在一个插件中返回包含特定属性的对象来为每个 store 都添加上特定属性
pinia.use(() => ({ hello: 'world' }))

或者

  • 直接在 store 上设置该属性(建议使用返回对象的方法,这样它们就能被 devtools 自动追踪到)
pinia.use(({ store }) => {store.hello = 'world'// 确保你的构建工具能处理这个问题,webpack 和 vite 在默认情况下应该能处理。if (process.env.NODE_ENV === 'development') {// 添加你在 store 中设置的键值store._customProperties.add('hello')}
})

每个 store 都被 reactive包装过,所以可以自动解包任何它所包含的 Ref(ref()computed()…)

const sharedRef = ref('shared')
pinia.use(({ store }) => {
// 每个 store 都有单独的 `hello` 属性
store.hello = ref('secret')
// 它会被自动解包
store.hello // 'secret'// 所有的 store 都在共享 `shared` 属性的值
store.shared = sharedRef
store.shared // 'shared'
})

添加新的 state

如果给 store 添加新的 state 属性或者在服务端渲染的激活过程中使用的属性,必须同时在两个地方添加。

  • store
  • store.$state
import { toRef, ref } from 'vue'pinia.use(({ store }) => {// 为了正确地处理 SSR,我们需要确保我们没有重写任何一个// 现有的值if (!store.$state.hasOwnProperty('hasError')) {// 在插件中定义 hasError,因此每个 store 都有各自的// hasError 状态const hasError = ref(false)// 在 `$state` 上设置变量,允许它在 SSR 期间被序列化。store.$state.hasError = hasError}// 我们需要将 ref 从 state 转移到 store// 这样的话,两种方式:store.hasError 和 store.$state.hasError 都可以访问// 并且共享的是同一个变量// 查看 https://cn.vuejs.org/api/reactivity-utilities.html#torefstore.hasError = toRef(store.$state, 'hasError')// 在这种情况下,最好不要返回 `hasError`// 因为它将被显示在 devtools 的 `state` 部分// 如果我们返回它,devtools 将显示两次。
})
  • 在一个插件中, state 变更或添加(包括调用 store.$patch())都是发生在 store 被激活之前

重置插件中添加的 state

import { toRef, ref } from 'vue'pinia.use(({ store }) => {
if (!store.$state.hasOwnProperty('hasError')) {const hasError = ref(false)store.$state.hasError = hasError
}
store.hasError = toRef(store.$state, 'hasError')// 确认将上下文 (`this`) 设置为 store
const originalReset = store.$reset.bind(store)// 覆写其 $reset 函数
return {$reset() {originalReset()store.hasError = false},
}
})

3、添加新的外部属性

当添加外部属性、第三方库的类实例或非响应式的简单值时,需要先用 markRaw() 进行包装后再传给 pinia

import { markRaw } from 'vue'
// 根据你的路由器的位置来调整
import { router } from './router'pinia.use(({ store }) => {
store.router = markRaw(router)
})

4、添加新的选项

在定义 store 时,可以创建新的选项,以便在插件中使用它们。例如,你可以创建一个 debounce 选项,允许你让任何 action 实现防抖。

defineStore('search', {
actions: {searchContacts() {// ...},
},// 这将在后面被一个插件读取
debounce: {// 让 action searchContacts 防抖 300mssearchContacts: 300,
},
})
// 使用任意防抖库
import debounce from 'lodash/debounce'pinia.use(({ options, store }) => {
if (options.debounce) {// 我们正在用新的 action 来覆盖这些 actionreturn Object.keys(options.debounce).reduce((debouncedActions, action) => {debouncedActions[action] = debounce(store[action],options.debounce[action])return debouncedActions}, {})
}
})

学习资料来源:

定义 Store | Pinia
简介 | Pinia
Action | Pinia
插件 | Pinia

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

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

相关文章

智能制造:基于AI制造企业解决方案架构设计【附全文阅读】

该方案面向制造企业管理者、技术工程师及智能制造转型需求企业&#xff0c;聚焦AI技术在制造业的落地应用。核心内容以AI开放平台为基础&#xff0c;构建“感知层-算法层-平台层-认知层”技术架构&#xff0c;提供从数据采集、模型训练到智能检测的全流程解决方案。 方案通过机…

JVM 高质量面试题

📌 文章目录 一、JVM 内存结构与运行时模型1. JVM 内存结构分区及作用2. 栈帧结构及方法调用链维护3. 逃逸分析及其对对象分配策略的影响4. TLAB 的作用及提升对象创建效率的机制二、垃圾回收器与 GC 调优1. CMS 与 G1 垃圾收集器的设计区别及适用场景2. Full GC 频繁问题的排…

使用Spring Boot和Spring Security构建安全的RESTful API

使用Spring Boot和Spring Security构建安全的RESTful API 引言 在现代Web开发中&#xff0c;安全性是一个不可忽视的重要方面。Spring Boot和Spring Security是Java生态中广泛使用的框架&#xff0c;它们提供了强大的工具来保护RESTful API。本文将介绍如何结合这两个框架&am…

Web项目流程总结

前端 1.下载pnpm管理器 pnpm是一种JavaScript包管理器。 前提&#xff1a;确保安装了Node.js 和 npm 打开cmd&#xff0c;以管理员身份运行&#xff08;输入以下指令&#xff09; npm install -g pnpm 安装成功后显示&#xff1a; npm: 这是 Node.js 的包管理工具&#xf…

Java中static关键字深度解析:从入门到高阶实战

Java中static关键字深度解析&#xff1a;从入门到高阶实战 目录 static的本质与核心特性静态变量 vs 实例变量&#xff1a;底层对比静态方法的设计哲学与应用场景高级用法&#xff1a;突破常规的static技巧 4.1 静态代码块&#xff1a;类加载的“初始化引擎”4.2 静态内部类&…

学习人工智能:从0到1的破局指南与职业成长路径

当AI绘画工具在几秒内生成一幅媲美专业画师的作品&#xff0c;当AI程序员自主优化代码逻辑&#xff0c;当AI客服精准解答复杂问题——一个现实愈发清晰&#xff1a;人工智能&#xff08;AI&#xff09;不再是科技公司的专属游戏&#xff0c;而是每个普通人必须直面的时代命题。…

2025年医美行业报告60+份汇总解读 | 附 PDF 下载

原文链接&#xff1a;https://tecdat.cn/?p42122 医美行业在消费升级与技术迭代的双重驱动下&#xff0c;已从边缘市场逐步走向主流。数据显示&#xff0c;2024 年中国医美市场规模突破 3000 亿元&#xff0c;年复合增长率达 15%&#xff0c;但行业仍面临正品率不足、区域发展…

深入了解Springboot框架的启动流程

目录 1、介绍 2、执行流程 1、运行run方法 2、初始化SpringApplication对象 1、确定容器类型 3、加载所有的初始化器 4、加载Spring上下文监听器 5、设置程序运行的主类 3、进入run方法 1、开启计时器 2、Headless模式配置 3、获取并启用监听器 4、准备环境 1、设…

【Java多态】:灵活编程的核心

&#x1f381;个人主页&#xff1a;User_芊芊君子 &#x1f389;欢迎大家点赞&#x1f44d;评论&#x1f4dd;收藏⭐文章 &#x1f50d;系列专栏&#xff1a;【Java】内容概括 【前言】 在Java面向对象编程的世界中&#xff0c;多&#xff08;Polymorphism&#xff09; 是一个核…

Python打卡训练营day33——2025.05.22

知识点回顾&#xff1a; PyTorch和cuda的安装 查看显卡信息的命令行命令&#xff08;cmd中使用&#xff09; cuda的检查 简单神经网络的流程 数据预处理&#xff08;归一化、转换成张量&#xff09; 模型的定义 继承nn.Module类 定义每一个层 定义前向传播流程 定义损失函数和优…

uni-app学习笔记九-vue3 v-for指令

v-for 指令基于一个数组来渲染一个列表。v-for 指令的值需要使用 item in items 形式的特殊语法&#xff0c;其中 items 是源数据的数组&#xff0c;而 item 是迭代项的别名&#xff1a; <template><view v-for"(item,index) in 10" :key"index"…

【C++算法】70.队列+宽搜_N 叉树的层序遍历

文章目录 题目链接&#xff1a;题目描述&#xff1a;解法C 算法代码&#xff1a; 题目链接&#xff1a; 429. N 叉树的层序遍历 题目描述&#xff1a; 解法 使用队列层序遍历就可以了。 先入根节点1。queue&#xff1a;1 然后出根节点1&#xff0c;入孩子节点2&#xff0c;3&a…

pycharm无法正常调试问题

pycharm无法正常调试问题 1.错误代码 已连接到 pydev 调试器(内部版本号 231.8109.197)Traceback (most recent call last):File "E:\Python\pycharm\PyCharm 2023.1\plugins\python\helpers\pydev\_pydevd_bundle\pydevd_comm.py", line 304, in _on_runr r.deco…

【机器学习基础】机器学习入门核心算法:线性回归(Linear Regression)

机器学习入门核心算法&#xff1a;线性回归&#xff08;Linear Regression&#xff09; 1. 算法逻辑2. 算法原理与数学推导3. 评估指标4. 应用案例5. 面试题6. 扩展分析总结 1. 算法逻辑 核心思想 通过线性方程拟合数据&#xff0c;最小化预测值与真实值的误差平方和&#xff0…

手机打电话时由对方DTMF响应切换多级IVR语音菜单(话术脚本与实战)

手机打电话时由对方DTMF响应切换多级IVR语音菜单 &#xff08;话术脚本与实战&#xff09; --本地AI电话机器人 上一篇&#xff1a;手机打电话时由对方DTMF响应切换多级IVR语音应答&#xff08;二&#xff09; 下一篇&#xff1a;手机打电话时由对方DTMF响应切换多级IVR语音…

flutter dart class语法说明、示例

&#x1f539; Dart 中的 class 基本语法 class ClassName {// 属性&#xff08;字段&#xff09;数据类型 属性名;// 构造函数ClassName(this.属性名);// 方法返回类型 方法名() {// 方法体} }✅ 示例&#xff1a;创建一个简单的 Person 类 class Person {// 属性String name;…

Apollo10.0学习——planning模块(10)之依赖注入器injector_

好不好奇injector_是干什么用的&#xff1f;为什么planning每个模块都要初始化这个变量&#xff1f; 类功能概述 DependencyInjector&#xff08;依赖注入器&#xff09;是一个 集中管理规划模块关键数据和服务 的容器类。它通过提供统一的访问接口&#xff0c;解耦各个组件之…

关于vue彻底删除node_modules文件夹

Vue彻底删除node_modules的命令 vue的node_modules文件夹非常大,常规手段根本无法删除. 解决方法: 在node_modules文件夹所在的路径运行命令窗口,并执行下面的命令. npm install rimraf -g rimraf node_modules说明&#xff1a; npm install rimraf -g 该命令是安装 node…

MCTS-RAG:通过树搜索重塑小模型中的检索增强生成(RAG)

https://arxiv.org/pdf/2503.20757v1这篇论文提出了MCTS-RAG框架&#xff0c;用于解决小型语言模型在知识密集型任务上的推理能力不足问题。具体来说&#xff0c; ​​MCTS-RAG框架​​&#xff1a;MCTS-RAG通过迭代地精炼检索和推理过程来工作。给定一个查询&#xff0c;它探…

数据结构:绪论之时间复杂度与空间复杂度

作者主页 失踪人口回归&#xff0c;陆续回三中。 开辟文章新专栏——数据结构&#xff0c;恳请各位大佬批评指正&#xff01; 文章目录 作者主页 数据结构的基本知识数据&#xff1a;数据元素&#xff1a;数据对象&#xff1a;数据类型&#xff1a;数据结构&#xff1a;逻辑结…