Vue状态管理工具:Pinia

news2024/11/13 11:46:40

基本概念

Pinia 是一个专为 Vue.js 设计的状态管理库,特别是针对 Vue 3 进行了优化,完美支持Vue3的Composition api 以及TypesCcript语法。它提供了一种更加简单、直观且可扩展的方式来组织和访问应用程序的状态。目前Pinia已经取代Vuex成为vue官方文档生态的一部分。

与vuex相比,Pinia主要有以下优点:

  1. Pinia 的 API 采用Vue 3的Composition API风格,设计更加简洁明了,易于理解和使用,状态管理更加直观。
  2. Pinia构建得更加轻量级,其体积比Vuex小,这有助于减少应用程序的加载时间和提高性能。
  3. Pinia 支持 TypeScript,提供了类型安全的 API ,有助于在开发过程中捕获错误和进行静态类型检查。
  4. Pinia支持创建多个store全局实例,每个store都可以视为一个独立的状态管理模块。
  5. Pinia 不需要嵌套模块,符合Vue3的Composition api ,让代码更加扁平化。

官方文档:Pinia | The intuitive store for Vue.js (vuejs.org)


使用步骤

1.安装Pinia

npm install pinia

2.main.js中引入pinia

import { createApp } from "vue";
import { createPinia } from "pinia"; //引入pinia
import App from "./App.vue";

const pinia = createPinia(); //创建pinia实例
const app = createApp(App);
app.use(pinia);  //挂载实例
app.mount("#app");

3.创建仓库Option Store:store.js文件(后面会介绍Setup Store)

import { defineStore } from "pinia";

export const useMainStore = defineStore("main", {
  state: () => ({}),
  getters: {},
  actions: {},
});
  1. defineStore的第一个参数:是容器的唯一标识,不能重复
  2. defineStore的第二个参数:是对容器仓库的配置说明(对象)
  3. state 属性: 存储全局状态数据data
  4. getters属性:计算属性,有缓存机制
  5. actions属性: 对state里数据变化的业务逻辑,简单说就是修改state全局状态数据

4.在组件中使用:需要引入和实例化

虽然我们前面定义了一个 store,但在我们使用 <script setup> 调用 useStore()(或者使用 setup() 函数,像所有的组件那样) 之前,store 实例是不会被创建的:

<script setup>
import { useMainStore } from "../stores/index";//引入
const store= useMainStore();//获取实例
// 可以在组件中的任意位置访问 `store` 变量 ✨
console.log(store);
</script>

核心概念

1. state

1).定义state

在大多数情况下,state 都是 store 的核心。在 Pinia 中,state 被定义为一个返回初始状态的函数。这使得 Pinia 可以同时支持服务端和客户端。

//index.js
export const useMainStore = defineStore("main", {
  state: () => ({
    name: "Jack",
    age: 18,
  })
});

2).访问state数据

//组件script中
import { useMainStore } from "../stores/index";//引入

const store = useMainStore();
//方式一:
console.log(store.name,store.age);//输出 Jack 18

//方式二:
const { name, age} = store //解构构赋值
console.log(name,age);//输出Jack 18

//template模板中
{{store.name + store.age}}或解构的{{name + age}}

注意:虽然在数据较多的时候,方式二解构赋值相较于store.[‘变量名’]的方式更加简洁明了。但使用这种解构赋值法会丢失state数据的响应性。因为store 是一个用reactive 包裹的对象

解决方式是:使用官方提供的storeToRefs()方法。它将为每一个响应式属性创建引用。

import { storeToRefs } from "pinia"//引入
const { name, age} = storeToRefs(store)//使用

补充:其实在Vuex中,直接解构数据也是不可以的。


3).修改state数据

方式一:直接修改,通过对store.[属性],对访问的属性重新赋值

function changeState(){
    store.name = '小明'
    store.age = '666'
}

方式二:$patch方法修改,它允许你用一个 state 的补丁对象在同一时间更改多个属性(修改多条数据时,性能更高)

function changeState(){
  store.$patch({
    name: "王小明",
    age: 666,
  });
}

不过,用这种语法的话,有些变更真的很难实现或者很耗时:任何集合的修改(例如,向数组中添加、移除一个元素或是做 splice 操作)都需要创建一个新的集合。因此,$patch 方法也接受一个函数来组合这种难以用补丁对象实现的变更。

//index.js
export const useMainStore = defineStore("main", {
  state: () => ({
    items: [],
    hasChanged: false,
  }),
});

//组件内 修改
function changeState(){
  store.$patch((state) => {
    state.items.push({ name: 'shoes', quantity: 1 })
    state.hasChanged = true
  })
}

4).重置state

整个state的属性值都会变成定义时的初始值

function resetState(){
  store.$reset()
}

2. getter

1).定义getter

Getter 完全等同于 store 的 state 的计算值。推荐使用箭头函数,并且它将接收 state 作为第一个参数:

export const useMainStore = defineStore("main", {
  state: () => ({
    name: "Jack",
    age: 18,
  }),
  getters: {
    doubleAge: (state) => state.age * 2,
    //在getter中 使用另一个getter  this指向当前存储库
    addOneAge() {
      return this.doubleAge + 1;
    },
    //或使用箭头函数但要将getters作为参数传入。(this指向问题)
    addTowAge: (getters) => getters.doubleAge + 2,
  }
});

 2).使用getter

//组件script中
import { useMainStore } from "../stores/index";
const store = useMainStore();

//方式一:
console.log(store.doubleAge);

//方式二:解构赋值
import { storeToRefs } from "pinia"
const { doubleAge, addOneAge , addTowAge} = storeToRefs(store)
//.value访问计算属性的值
console.log(doubleAge.value,addOneAge.value, addTowAge.value);

//temlate模板中
<p>{{store.doubleAge}}</p>
<p>{{store.addOneAge}}</p>
<p>{{store.addTowAge}}</p>

getter相当于state的计算属性,具有缓存机制。它的值会基于state依赖被缓存。仅会在state依赖被更新时才重新计算。只要依赖的state数据不变,无论访问多少次计算结果,它都会立即返回先前的计算结果,而不用重复执行 getter 函数。


 3. action

1).定义action

Action 相当于组件中的 method。它们是定义业务逻辑的完美选择。类似 getter,action 也可通过 this 访问整个 store 实例。不同的是,action 可以是异步的,你可以在它里面 await 调用任何 API,以及其他 action!

export const useCounterStore = defineStore("main", {
  state: () => ({
    count: 0,
  }),
  actions: {
    increment() {
      this.count++;
    },
    //模拟异步请求函数
    randomizeCounter() {//1 s后但会一个值未随机数字的promise对象
      return new Promise((resolve, reject) => {
        setTimeout(() => {
          let number = Math.round(100 * Math.random());
          resolve(number);
        }, 1000);
      });
    },
    //异步操作
    async changeCount() {
      const res = await this.randomizeCounter();//解析promise
      this.count = res;
      //调用其他action
      this.increment();
    },
  },
});

 2).使用action

//script中
const store = useCounterStore();

//方式一:
// 将 action 作为 store 的方法进行调用
store.changeCount()

//方式二:
//解构赋值,直接使用
const { changeCount , increment }= store
changeCount()
increment()

//template模板中
<button @click="store.changeCount()">changeCount</button>

 注意:作为 action 的 方法可以直接解构


其他补充

1. Setup Store

这是另一种定义 store 的语法。与 Vue 组合式 API 的 setup 函数相似,我们可以传入一个函数,该函数定义了一些响应式属性和方法,并且返回一个带有我们想暴露出去的属性和方法的对象。

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

export const useCounterStore = defineStore("counter", () => {
  const count = ref(0);
  const doubleCount = computed(() => count.value * 2);

  function increment() {
    count.value++;
  }
  return { count, doubleCount, increment };
});

在 Setup Store 中:ref() 就是 state 属性,computed() 就是 getters,function() 就是 actions

注意,要让 pinia 正确识别 state,你必须在 setup store 中返回 state 的所有属性。这意味着,你不能在 store 中使用私有属性。不完整返回会影响 SSR ,开发工具和其他插件的正常运行。

相比于上面介绍的Option Store,Setup store拥有更高的灵活性,因为你可以在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 {
    // ...
  }
})

总之,两种语法都有各自的优势和劣势,Option Store更容易使用,而Setup Store更灵活和强大。


2. pinia持久化存储

pinia 和 vuex 一样,数据是短时的,只要一刷新页面,数据就会恢复成初始状态,为了避免这个问题,可以对其采用持久化保存方法。

持久化保存的原理是在 pinia 中数据更新时,同步保存到 localStorage 或 sessionStorage 中,刷新后从本地存储中读取数据。

1).安装插件

官方文档:开始使用 |Pinia 插件持续存在

npm i pinia-plugin-persistedstate

2).引入使用插件:main.js文件

import { createApp } from "vue";
import App from "./App.vue";
import { createPinia } from "pinia"; //引入pinia
import persist from 'pinia-plugin-persistedstate'//引入插件

const pinia = createPinia(); //创建pinia实例
pinia.use(persist)//使用插件

const app = createApp(App);
app.use(pinia); //挂载pinia到vue实例
app.mount("#app");

3).使用插件

方式一:默认保存,将当前模块中的所有数据都进行持久化存储,保存在localStorage 或sessionStorage 中,刷新页面不需要手动读取数据,插件会自动读取。

//setup store
defineStore(
  "counter",
  () => {
    return { };
  },
  { persist: true }// true 表示开启持久化保存
);

//option store
defineStore("main", {
  state: () => ({}),
  actions: {},
  persist: true,
});

方式二:传递一个对象给 Store 的 persist 属性来配置持久化。

persist: {
    key: "counterStore", //存储名称
    storage: sessionStorage, // 存储方式
    paths: ["count"], 
    //paths指定 state 中哪些数据需要被持久化。
    //[] 表示不持久化任何状态,undefined 或 null 表示持久化整个 state
  },

效果如图:

64ffbf5388444e4b81e2e0553075f4d1.png


3. 封装pinia

 封装的目的是pinia独立维护,减少main.js文件的冗余度。同时因为Pinia支持创建多个store全局实例,封装pinia可更加方便统一管理这些仓库。举例说明:

1).创建仓库userStore:user.js文件

import { defineStore } from "pinia";

export const useUserStore = defineStore("user", {
  state: () => ({
    name: "Jack",
    age: 18,
  }),
  getters: {
    doubleAge: (state) => state.age * 2,
  },
  actions: {
    increment() {
      this.age++;
    },
  },
  persist: true,
});

2).创建仓库counterStore:counter.js文件

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

export const useCounterStore = defineStore(
  "counter",
  () => {
    //数据state
    const count = ref(1);
    //getter
    const doubleCount = computed(() => count.value * 2);
    //action
    function increment() {
      this.count++;
    }
    return {
      count,
      doubleCount,
      increment,
    };
  },
  {
    persist: true,
  }
);

3).封装pinia

import { createPinia } from "pinia"; //引入pinia
import persist from "pinia-plugin-persistedstate"; //引入持久化插件

const pinia = createPinia(); //创建pinia实例
pinia.use(persist); //使用插件

export default pinia; //导出pinia用于main.js注册

//统一导出,可以控制各个仓库是否可用
// import { useUserStore } from '@/stores/modules/user.js'
// export { useUserStore }
// import { useCounterStore } from '@/stores/modules/counter.js'
// export { useCounterStore }

// 简写
export * from "./modules/user";
export * from "./modules/counter";

4).在mian.js中注册pinia

import { createApp } from "vue";
import App from "./App.vue";
import pinia from "./stores/index";

const app = createApp(App);

app.use(pinia); //挂载pinia到vue实例
app.mount("#app");

文件结构: 

03cf1ac7a31f4a7785152d90a7a42bd3.png

至此pinia封装完成,由于pinia具有较强的可扩展性。如果需要添加其他插件扩展便可一并放入index.js文件独立维护。参考官方文档:插件 | Pinia (vuejs.org)

 5).在组件中使用:只需要从index.js文件中导入,减少代码复杂度

import { useCounterStore } from '../stores/index'
const counterStore = useCounterStore()

 若有错误或描述不当的地方,烦请评论或私信指正,万分感谢 😃

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

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

相关文章

总结:数字图像处理基础知识

数字图像处理中的图像坐标系是理解图像数据结构和图像处理算法的基础。在计算机视觉和图像处理领域&#xff0c;通常有两种主要的坐标系统用于描述图像中的像素位置&#xff1a;笛卡尔坐标系和像素坐标系。下面我将详细介绍这两种坐标系统及其在数字图像处理中的应用。 1. 笛卡…

T/CECS 10035-2019 绿色建材评价 金属复合装饰材料

一、绿色建材 绿色建材是指在全生命周期内减少对天然资源消耗和减轻对生态环境影响&#xff0c;具有节能、减排、安全、便利和可循环的建材产品&#xff0c;获得绿色产品证书的产品在实际招投标以及品牌宣传中具有绝对优势。 二、认证模式 初始工厂检查产品抽样检验获证后监督…

稳压二极管、肖特基二极管、TVS管、静电ESD管的区别

1.稳压二极管 正向导通电压跟普通二级管一样约为0.7v&#xff0c;反向状态下在临界电压之前截止&#xff0c;在达到临界电压的条件下会处于导通的状态&#xff0c;电压也不再升高&#xff0c;所以用在重要元器件上&#xff0c;起到稳压作用。 稳压二极管主要利用其反向击穿时…

LLM概念梳理(二):检索增强RAG

非常感谢RAG&#xff08;检索增强生成&#xff09;技术详解&#xff1a;基于垂直领域专有数据的 Chatbots 是如何实现的&#xff0c;这篇文章对 RAG 技术进行了详细的描述。我根据自己的理解&#xff0c;并且按照代码思路重新进行整理。 RAG 技术看似神奇&#xff0c;其本质是…

2024.8.22(Docker)

一、回顾 高并发集群 eleme后端登录模块 1、数据库 1.主从复制(高可用) 2.传统的主从复制 3.gtids事务型的主从复制 4.注意 &#xff08;1&#xff09;server_id唯一 &#xff08;2&#xff09;8.x版本需要get_ssl_pub_key &#xff08;3&#xff09;5.x不需要 &#xff08;4…

ts枚举类型校验,提示枚举信息

ts 类型验证&#xff0c;提示枚举信息 const eventTypeList [MOUSE_MOVE, // 鼠标移动LEFT_CLICK // 左键单击// MOUSE_OUT, ] as consttype EventTypeList typeof eventTypeList[number]

FST(Finite State Transducer)

有限状态转换器&#xff08;Finite State Transducer&#xff0c;简称FST&#xff09;是一种计算模型&#xff0c;它是有限状态自动机&#xff08;Finite State Automaton&#xff0c;简称FSA&#xff09;的扩展。FSA是一种理论计算模型&#xff0c;用于表示和处理正则语言&…

苹果iOS / iPadOS 18 beta 7版本发布,或将是最后一个iOS / iPadOS 18beta版本

今日苹果公司向 iPhone 和 iPad 用户推送了 iOS/iPadOS 18 开发者预览版 Beta 7 更新&#xff08;内部版本号&#xff1a;22A5346a&#xff09;&#xff0c;本次更新距离上次发布 Beta/RC 间隔 8 天。 值得注意的是&#xff0c;本次更新版本号以a结尾&#xff0c;意味着如果不…

国密算法SSL证书:守护网络安全的利器

国密算法SSL证书是基于中国自主研发的密码算法标准生成的安全套接层证书&#xff0c;主要用于保护网站和应用程序的数据传输安全。该证书采用SM2椭圆曲线公钥密码算法、SM3密码哈希函数和SM4对称密钥加密算法&#xff0c;确保数据传输过程中的机密性、完整性和抗攻击性。 国密…

KKView远程Microsoft Remote Desktop

Microsoft Remote Desktop&#xff1a;高效稳定的远程连接解决方案 在当今这个信息化、网络化的时代&#xff0c;远程桌面工具成为了许多用户进行远程办公、学习及技术支持的重要工具。其中&#xff0c;Microsoft Remote Desktop以其高效稳定的连接性能、强大的跨平台支持及出…

学校每年比用的招生简章在线制作平台!

每年招生季节&#xff0c;各大中小学及高校都会面临一个重要的任务&#xff1a;更新和发布招生简章。这不仅是一份简单的招生说明&#xff0c;它更是学校向外界展示自身特色、招生政策和培养目标的重要窗口。随着信息技术的发展&#xff0c;传统的纸质招生简章逐渐演变为在线制…

通过拖拽添加dom和一些属性

将图片通过拖拽添加到指定位置 <template><div class"draggle-box"><imgclass"draggle-source"src"/assets/bg/cat.jpg"dragstart"handleDragStart($event, 图片)"/><divclass"draggle-target"dragove…

嵌入式学习——(Linux高级编程——线程控制)

线程的互斥 一、互斥的重要性 在多线程编程中&#xff0c;互斥机制至关重要。当多个线程同时访问临界资源时&#xff0c;如果没有有效的互斥控制&#xff0c;可能会导致数据不一致、资源竞争等问题。通过互斥锁&#xff0c;可以确保在任何时刻只有一个线程能够访问临界资源&am…

Axure RP 9高手速成秘籍:解锁终极快捷键,设计效率飙升10倍!

Axure RP 9作为一款功能强大的原型设计工具&#xff0c;提供了丰富的快捷键来加速设计流程。以下是一份详尽的Axure RP 9快捷键大全&#xff0c;旨在帮助用户更高效地完成设计工作。 一、文件操作 新建&#xff1a;Ctrl N&#xff08;Windows&#xff09;/ Command N&#…

【LLM大模型】本地玩转多模态Llama3

一般开源的LLM&#xff0c;例如Llama3和Qwen2等&#xff0c;只支持文本的输入&#xff0c;只能理解文本的内容&#xff0c;实现基于文本的逻辑推理和意图识别。 但是一些Chatbot&#xff0c;例如GPT-4V&#xff0c;就是拥有视觉能力&#xff0c;能够理解图片内容&#xff0c;能…

【LeetCode Cookbook(C++ 描述)】一刷二叉搜索树

目录 LeetCode #700&#xff1a;Search in a Binary Search Tree 二叉搜索树中的搜索递归法迭代法 LeetCode #98&#xff1a;Validate Binary Search Tree 验证二叉搜索树递归法迭代法 LeetCode #530&#xff1a;Minimum Absolute Difference in BST 二叉搜索树的最小绝对差递归…

在IEDA里打包Maven项目记录

之前在网上查找到的方式发现比较繁琐&#xff0c;所以把自己的解决办法记录一下分享给兄弟们 <plugins><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-shade-plugin</artifactId><version>3.2.4</vers…

KT来袭,打造沉浸式体验的聚合性web3应用平台

随着步入 2024&#xff0c;漫长的区块链熊市即将接近尾声。纵观产业发展&#xff0c;逆流而上往往会是彰显品牌市场影响力和技术实力的最佳证明。在这次周期中&#xff0c;一个名为KT的web3.0聚合平台吸引了市场关注&#xff0c;无论在市场层面还是技术层面&#xff0c;都广泛赢…

【计算机三级-数据库技术】数据库后台编程技术

内容提要 1、掌握存储过程的定义与使用 2、掌握用户定义函数的创建与使用 3、掌握触发器的定义与使用 4、掌握游标的定义与使用 第一节 存储过程 使用T-SQL语言编写&#xff0c;有两种方式存储&#xff1a; 在客户端存储代码通过客户端程序或SQL命令向DBMS发出操作请求&…

JavaScript中设置器和获取器

在JavaScript中&#xff0c;setters 和 getters 是对象属性的特殊方法&#xff0c;用于定义如何访问和设置对象的属性。这些方法使得可以在对对象属性执行读取或写入操作时添加自定义逻辑。 举例 首先我们定义一个类似之前银行家的一个对象 const account {owner: ITshare,…