写点什么

使用 Pinia:让 Vue 状态管理更简单

  • 2023-04-26
    广东
  • 本文字数:2873 字

    阅读完需:约 9 分钟

使用Pinia:让Vue状态管理更简单

前言

pinia,一个基于 Vue3 的状态管理库,它可以帮助开发人员管理 Vue 应用程序的状态,Pinia 使用 Vue3 的 Composition API 提供了更简单,灵活的状态管理方式,通过创建 store 来管理应用程序的状态,还提供了插件和工具来帮助开发人员更轻松的使用它。

Pinia 对比 VueX

之前我们在 vue2 的时候使用的状态管理库是 VueX,在 Vue3 中想使用 VueX,需要使用到 VueX4 版本,而在 Vue3 组合式 API 下设计了全新的状态管理库:Vuex5,也就是 Pinia(已经被纳入官方了)


那 Pinia 相比 VueX 有什么优势呢?



在当时 pinia 的作者就作出了这么一个提案:


  1. pinia 都支持在 vue2 和 vue3 中使用

  2. 在 vuex 想修改数据状态需要 mutations,而 pinia 去掉了 mutations,只有 state,getters 和 actions。修改数据状态直接在 actions 里操作,pinia 推崇使用类似于 JavaScript 对象的方式来更新状态,即直接对 state 进行修改,可以减少代码量,并且降低了出错的可能性

  3. pinia 中没有嵌套模块,在 vuex 中我们可能会用到大量的模块,一层套一层,使状态管理变得复杂,也有可能会造成命名冲突,在 Composition API 下 pinia 中可以定义多个 store,然后将多个 store 组合在一起,变得更加灵活

  4. 完整的 TypeScript 支持,vuex 对 ts 的类型检查,类型推断和泛型的支持都不太友好,模块命名空间的支持也不够完善


除此之外两者的 api 都是相同的,使用的方式也是相同的,关于 vuex 的使用可以看看之前写的vuex文章

Pinia 的使用

安装 pinia

使用 pinia 需要先安装,可以通过 yarn 或者 npm 安装


yarn add pinia # or with npm npm install pinia
复制代码


在 vue2 中使用,你还需要安装一个插件并pinia在应用程序的根目录注入创建的,如果 vue 版本<2.7,还需要安装 composition api: @vue/composition-api



安装完之后在入口文件引入 pinia 的 createPinia 方法并调用得到 pinia 的实例,然后挂载到 vue 实例上


import { createApp } from "vue";import App from "./App.vue";import { createPinia } from "pinia";
//创建pinia实例const pinia = createPinia();
const app = createApp(App);//挂载到vue实例上app.use(pinia);app.mount("#app");
复制代码

定义 store 容器

在 src 目录下创建 store 目录来存放 pinia 容器相关代码,在里面新建了一个 index.ts 文件,在 index.ts 文件里首先要定义一个容器,定义容器使用defineStore来定义,主要接收两个参数,一个是容器的名字(唯一),另一个是容器的对象,最后将容器导出,就可以在组件中使用了


import { defineStore } from "pinia";
export const useTestStore = defineStore("test", { /** * 类似组件中的data,存储全局状态 * 必须是箭头函数,避免数据状态污染 */ state: () => { return { message: "hello world", count:0, }; },
/** * 类似组件中的computed */ getters: {},
/** * 类似组件中的methods,封装业务逻辑,修改state */ actions: {},});
复制代码

在组件中导入并使用容器

state 的使用

在组件中导入容器并且调用,得到容器的实例,就可以直接使用 state 里的数据或者在模板中展示


<script setup lang="ts">import { useTestStore } from "./store";const store = useTestStore();</script>
<template> <div>{{ store.message }}</div></template>
<style scoped></style>
复制代码



也可以使用解构的方式拿到和使用数据,但是这样拿到的数据不是响应式的,修改的时候页面上是不会发生变化的


<script setup lang="ts">import { useTestStore } from "./store";const store = useTestStore();
//解构stateconst { message, count } = store;
const countAdd = () => { store.count++;};</script>
<template> <div> <div>{{ store.message }}</div> <div>{{ store.count }}</div> <hr /> <div>{{ "解构后:" + message }}</div> <div>{{ "解构后:" + count }}</div> <button @click="countAdd">count++</button> </div></template>
<style scoped></style>
复制代码



针对这种情况解决的办法就是解构的时候使用 pinia 的storeToRefs方法将实例包裹住,state 里的数据就变成响应式的了


import { storeToRefs } from "pinia";//解构state,对state里的数据做了reactive处理const { message, count } = storeToRefs(store);
复制代码



状态的更新和 actions 的使用

在组件里修改 state 的状态的时候有三种方式:


  • 直接修改

  • $patch 直接修改

  • $patch 通过函数修改


const countAdd = () => {  // 直接修改  store.count++;  store.message = 'hello pinia'
// 修改多个数据 $patch批量修改 store.$patch({ count: store.count + 1, message: "hello pinia", }); // $patch 函数(推荐) store.$patch(state=>{ state.count++ state.message = "hello pinia" })};
复制代码


也可以通过actions处理,在 actions 定义一个方法,通过this访问当前实例拿到 state 里的数据,就像 vue2 的 options API 一样在 methods 里拿 data 里的数据


// storeactions: {    handleChangeState() {      this.message = "hello pinia";      this.count++;    },},
//componentconst countAdd = () => { store.handleChangeState();};
复制代码


也可以在调用 actions 里的方法的时候传递参数


// storeactions: {    handleChangeState(num?: number) {      this.count += num ?? 0;    },},
//componentconst countAdd = () => { store.handleChangeState(10);};
复制代码

getters 的使用

getters 类似组件中的 computed,接收一个 state 参数,这个 state 就是容器里的 state 状态对象,当依赖的 state 发生变化的时候 pinia 就会自动更新 getters 的值,而且 getters 具有默认的缓存机制


如果一个 getter 所依赖的 state 没有发生变化,那么就返回上一次计算的结果,而不会重新计算,显著提高了性能


// gettersgetters: {    // typescript自动推导返回值类型    countMUL10(state) {      console.log("getter被调用了");      return state.count * 10;    },        // or手动指定返回值类型    countMUL10():number {      console.log("getter被调用了");      return this.count * 10;    },},
//component<template> <div> <div>{{ store.count }}</div> // 调用多次 <div>{{ store.countMUL10 }}</div> <div>{{ store.countMUL10 }}</div> <div>{{ store.countMUL10 }}</div> <button @click="countAdd">count++</button> </div></template>
复制代码



store 容器调用另一个 store 容器

如果你想在一个 Store 中使用另一个 Store,就和在组件中使用 store 一样操作就可以了


import { defineStore } from "pinia";//导入一个store容器import { useTestStore } from ".";export const useLoginStore = defineStore("login", {  state: () => {    return {};  },  getters: {},  actions: {    handlerOtherStore() {      //得到store实例,调用实例里的方法      const testStore = useTestStore();      testStore.handleChangeState(10);    },  },});
复制代码

总结

总结就八个字,简单好用,推荐使用😤😤

发布于: 刚刚阅读数: 4
用户头像

你若毁我天堂,我必戳你脊梁 2022-11-01 加入

还未添加个人简介

评论

发布
暂无评论
使用Pinia:让Vue状态管理更简单_Pinia_格斗家不爱在外太空沉思_InfoQ写作社区