vue3中pinia的使用:state和actions

本文介绍了如何在Vue项目中使用Pinia库创建store,管理state和actions,以及如何通过storeToRefs保持状态的响应性。示例展示了如何导入store,解构actions,监听state变化并执行相应操作。

前言

以下代码和内容的使用都是在setup中,未使用TS。

<script setup></script>

内容只涉及最基本的 state 和 actions。getters用法和不同store间的调用,请自行了解。


安装和引入

创建 store

在项目的src文件夹下,创建stores文件夹,新建 homeStore.js文件(名字不重要)。

import { defineStore } from 'pinia';
export const homeStore = defineStore({
  id: 'homeStore',// 项目中 所有store 的唯一 ID。 这里与文件名一致好处是比较方面后期使用。
  state: () => ({
    version: '1.1.0',
    buildDate: '20231011',
  }),
  getters: {},
  actions: {    
    fetchData() {
      // do something
    }
  }
});

注意:actions是允许异步的。可以使用promise或者发起请求。

使用

下面为具体.vue 文件中的使用

<template>
  <section>{{ version }}</section>
</template>

<script setup>
import { watch } from 'vue';
import { storeToRefs } from 'pinia';
import { homeStore } from '@/stores/homeStore';
const HomeStore = homeStore();// 引入 这里使用 大写H的 HomeStore作为当前页面中的使用实例。后续的使用都是该实例
const { fetchData } = HomeStore;
const { version, buildDate } = storeToRefs(HomeStore);
console.log('version: ', version);
console.log('buildDate: ', buildDate);
watch(
  () => version.value,
  (newVersion) => {
    // do something
  }
);
</script>
<style></style>

说明

  1. 代码中的const { fetchData } = HomeStore; 为解构引入了homeStore中的actions。
    后续页面中如果要调用 fetchData 这个action 可以直接调用,如同调用函数一般:fetchData ()
    如果actions 是promise,调用:
fetchData()
  .then((res) => {
    // do something
  })
  .catch((err) => {
    // do something
  });
  1. storeToRefs 方法是 pinia 提供的api:从 store 中提取state 时保持其响应性。
    这样页面在用如代码中version 或者buildDate 时,不仅可以获取到值,也可以对state做赋值操作:
version.value = '1.1.0 (bate)'

赋值之后,所有引用了homeStore 中 version 这个state的地方也会同步变化。
代码后面的watch,也是为了页面监听对应state变化后进行其它操作。

备注

在用使用 const HomeStore = homeStore(); 为页面引入store后。
直接解构引入的是store的actions
storeToRefs 方法引入的是store的 state

参考

pinia 官网

<think> 我们正在处理的问题是:在Vue3 + TypeScript + Piniaactions中,无法通过this访问state。 根据之前的分析,可能的原因包括:上下文丢失、Store未正确初始化、使用箭头函数定义action、异步嵌套导致this丢失、TypeScript类型未正确声明等。 现在我们需要针对Vue3 + TypeScript + Pinia环境,给出具体的解决方案。 注意:用户要求参考站内引用,但我们注意到引用[1]是关于MeterSphere前端架构的概述,并没有直接解决这个问题的内容。 因此,我们将主要基于之前总结的排查步骤解决方案,结合Vue3TypeScript的特点来回答。 解决方案: 1. **避免解构action**: 在Vue3组件中,使用store时,不要直接解构action,而是通过store实例调用action。 错误示例: ```typescript const { someAction } = useStore(); someAction(); // this丢失 ``` 正确做法: ```typescript const store = useStore(); store.someAction(); // this正确指向store ``` 2. **使用普通函数定义action**: 在定义Pinia store的action时,避免使用箭头函数,因为箭头函数会绑定父级作用域的this。 错误示例(store定义中): ```typescript import { defineStore } from &#39;pinia&#39;; export const useStore = defineStore(&#39;store&#39;, { state: () => ({ count: 0 }), actions: { increment: () => { // 箭头函数,this不正确 // ❌ 这里访问this.count会出错 this.count++; } } }); ``` 正确做法: ```typescript actions: { increment() { // 普通函数,this指向store this.count++; // ✅ } } ``` 3. **处理异步操作中的this**: 在异步操作(如setTimeout、Promise)中,如果需要访问storestate,确保在回调函数中正确捕获this。 示例: ```typescript actions: { async fetchData() { // 使用箭头函数捕获this setTimeout(() => { console.log(this.count); // ✅ }, 100); // 或者保存this引用 const store = this; setTimeout(function() { console.log(store.count); // ✅ }, 100); } } ``` 4. **在非组件环境中使用store时传入Pinia实例**: 如果在Vue组件外部(例如在普通的ts文件中)使用store,需要确保传入已经创建的Pinia实例。 在main.ts中创建并传递pinia实例: ```typescript // main.ts import { createApp } from &#39;vue&#39;; import { createPinia } from &#39;pinia&#39;; import App from &#39;./App.vue&#39;; const pinia = createPinia(); createApp(App).use(pinia).mount(&#39;#app&#39;); ``` 在非组件文件中使用store: ```typescript // 某个ts文件 import { useStore } from &#39;@/store&#39;; import pinia from &#39;@/store/piniaInstance&#39;; // 需要导出pinia实例 // 注意:需要在其他地方先创建pinia实例,然后导出 const store = useStore(pinia); store.someAction(); // 这样this才能正确指向 ``` 注意:如何导出pinia实例? 可以在store目录下创建一个文件,创建并导出pinia实例: ```typescript // store/piniaInstance.ts import { createPinia } from &#39;pinia&#39;; const pinia = createPinia(); export default pinia; ``` 然后在main.ts中使用这个实例,这样保证整个应用使用同一个pinia实例。 5. **TypeScript类型支持**: 确保store的类型正确,以便TypeScript能够识别this上的属性。 Pinia的defineStore函数在TypeScript下可以自动推断类型。但是如果需要扩展或者使用复杂类型,可以使用泛型。 示例: ```typescript interface MyState { count: number; // ...其他状态 } interface MyActions { increment(): void; // ...其他actions } // 使用泛型定义store export const useStore = defineStore<&#39;myStore&#39;, MyState, {}, MyActions>(&#39;myStore&#39;, { state: () => ({ count: 0 }), actions: { increment() { this.count++; // TypeScript会知道this.count是number } } }); ``` 或者更常见的,使用组合式写法(setup store): ```typescript export const useCounterStore = defineStore(&#39;counter&#39;, () => { const count = ref(0); function increment() { count.value++; } return { count, increment }; }); ``` 在组合式写法中,由于使用了setup函数,不存在this丢失的问题,因为可以直接访问变量函数。 6. **检查Pinia版本配置**: 确保使用的是较新的Pinia版本(与Vue3兼容)。另外,检查是否有其他配置影响了this的指向。 如果以上步骤都检查了,问题仍未解决,可以尝试创建一个最简单的store来测试,逐步排查问题。 最后,考虑到用户使用的是Vue3+TypeScript+Pinia,组合式store写法(setup store)可能是更推荐的方式,因为它更符合Vue3的组合式API风格,且避免了this指向问题。 示例组合式store: ```typescript import { defineStore } from &#39;pinia&#39;; import { ref } from &#39;vue&#39;; export const useCounterStore = defineStore(&#39;counter&#39;, () => { const count = ref(0); function increment() { count.value++; } async function asyncIncrement() { setTimeout(() => { count.value++; }, 1000); } return { count, increment, asyncIncrement }; }); ``` 在组合式store中,state通过ref或reactive定义,actions就是普通的函数,通过闭包访问状态,因此没有this的问题。 当然,用户可能已经使用了选项式store,所以以上解决方案仍然适用。 根据用户的问题,我们生成了以下相关问题: §§相关问题§§ 1.Pinia中,组合式store选项式store有何优缺点? 2. 如何在Vue3应用中全局初始化Pinia3. TypeScript下如何为Pinia的选项式store提供完整的类型推断? 4. Piniaactions中如何处理异步操作错误? 5. 如何在Vue组件外部使用Pinia store? </think> ### Vue3 + TypeScript + PiniaActions 无法通过 `this` 获取 State 的解决方案 在 Vue3 + TypeScript + Pinia 环境中,Actions 中 `this` 无法访问 State 的常见原因及解决方案如下: --- #### 1. **箭头函数导致上下文丢失(最常见)** Pinia 要求 Actions 必须使用**普通函数**而非箭头函数,因为箭头函数会继承父级作用域的 `this`。 **错误示例**: ```typescript actions: { increment: () => { // ❌ 箭头函数 console.log(this.count); // this 指向错误 } } ``` **解决方案**: ```typescript actions: { increment() { // ✅ 普通函数 console.log(this.count); // 正常访问[^1] } } ``` --- #### 2. **Store 实例未正确传递(TS 环境常见)** 在非组件环境(如工具函数)中使用 Store 时,必须显式传递 Pinia 实例。 **错误示例**: ```typescript // utils.ts import { useStore } from &#39;@/store&#39;; const store = useStore(); // ❌ 缺少 Pinia 实例 store.increment(); // this 可能为 undefined ``` **解决方案**: ```typescript // utils.ts import { useStore } from &#39;@/store&#39;; import pinia from &#39;@/stores/piniaInstance&#39;; // 导出的 Pinia 实例 const store = useStore(pinia); // ✅ 显式传入实例 store.increment(); // this 正确绑定[^3] ``` --- #### 3. **异步操作未捕获上下文** 在异步回调(如 `setTimeout` 或 Promise)中直接使用 `this` 会丢失上下文。 **错误示例**: ```typescript actions: { async fetch() { setTimeout(function() { console.log(this.count); // ❌ this 指向全局对象 }, 100); } } ``` **解决方案**: ```typescript actions: { async fetch() { // 方法1:箭头函数捕获 this setTimeout(() => { console.log(this.count); // ✅ }, 100); // 方法2:保存 this 引用 const store = this; setTimeout(function() { console.log(store.count); // ✅ }, 100); } } ``` --- #### 4. **TypeScript 类型声明缺失** 未声明 Store 类型时,TS 无法推断 `this` 的属性。 **解决方案**: ```typescript // 声明 Store 类型 interface CounterStore { count: number; increment: () => void; } // 使用类型断言 const store = useStore() as CounterStore; store.increment(); // ✅ TS 类型检查通过 // 或使用 PiniaStoreGeneric import { StoreGeneric } from &#39;pinia&#39;; const store = useStore() as StoreGeneric; ``` --- #### 5. **组合式 Store 的替代方案** 使用 Pinia 的组合式 API 写法可避免 `this` 问题: ```typescript export const useStore = defineStore(&#39;counter&#39;, () => { // State (直接使用 ref/reactive) const count = ref(0); // Actions (普通函数) function increment() { count.value++; // ✅ 直接访问变量 } return { count, increment }; }); ``` > ✅ 组合式 Store 无需 `this`,通过闭包直接访问状态[^1]。 --- ### 排查步骤总结 1. **检查函数类型**:确保 Action 是普通函数而非箭头函数 2. **验证 Store 初始化**:非组件环境需显式传入 Pinia 实例 3. **处理异步上下文**:异步回调中使用箭头函数或保存 `this` 4. **补充 TS 类型**:声明 Store 类型或使用 `StoreGeneric` 5. **考虑组合式写法**:彻底避免 `this` 问题 --- ### 相关问题 1. 为什么 Pinia 推荐使用组合式 API 替代选项式 API? 2. 如何为 Pinia Store 设计完整的 TypeScript 类型契约? 3.Vue3 中如何实现 Pinia Store 的跨模块通信? 4. Pinia 的插件机制如何扩展 Actions 功能? 5. Vue3 的响应式系统与 Pinia 的状态管理有何协同优化点? [^1]: [MeterSphere 前端架构分析:Vue3 组件化与状态管理实践] [^3]: [Pinia 官方文档:Store 外部使用与实例传递方案]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

余九月丶

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值