Pinia不酸,保甜

为什么是Pinia

怎么说呢,其实在过往的大部分项目里面,我并没有引入过状态管理相关的库来维护状态。因为大部分的业务项目相对来说比较独立,哪怕自身功能复杂的时候,可能也仅仅是通过技术栈自身的提供的状态管理能力来处理业务场景问题,比如React中的context,基本都能解决我遇到的问题。

针对Redux或者Vuex这类状态管理的库,我认为在较为复杂的大型业务场景下才能发挥他们的真实作用,在场景较为简单单一,数据状态相对不复杂的时候,引入他们可能并不能带来那么多的便捷。

说回Pinia,接触使用到它主要是因为有一次手里的发布功能需要进行重构。虽然仅仅是一个发布页面,但是梳理起来发现,里面涉及几类数据需要维护,包括主表单信息、错误校验信息、公共弹窗信息以及关联用户的各种状态信息等。想起之前有看到过关于小的介绍,抱着试试看的心态接入试试。(不行我就继续Provide/Inject了[捂脸])

认识Pinia

Pinia 是 Vue 的存储库,它允许您跨组件/页面共享状态。

上面这个是官网对Pinia的一个定义,从定义上我们其实可以看出来,它可能比Vuex要精炼一些(Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式 + 库。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。)具体如何我们还是得具体从使用情况来看一下。

Pinia的常规用法

安装

通过常用的包管理器进行安装:

yarn add pinia
// 或者
npm install pinia

安装完成后,我们需要将pinia挂载到Vue应用中,也就是我们需要创建一个根存储传递给应用程式。我们需要修改main.js,引入pinia提供的cteatePinia方法:

import { createApp } from 'vue';
import { createPinia } from 'pinia';
​
const pinia = createPinia();
const app = createApp(App);
app.use(pinia).mount('#app');

上述安装引入基于Vue3,如果使用Vue2的话,轻参照官网相关说明即可。

Store

store简单来说就是数据仓库的意思,我们 的数据都放在store里面。当然你也可以把它理解为一个公共组件,只不过该公共组件只存放数据,这些数据我们其它所有的组件都能够访问且可以修改。它有三个概念,stategettersactions,我们可以将它们等价于组件中的“数据”、“计算属性”和“方法”。

store中应该包含可以在整个应用中访问的数据、全局性数据,我们应该避免将可以管理在具体组件内部的数据放到store中。

我们需要使用pinia提供的defineStore()方法来创建一个store,该store用来存放我们需要全局使用的数据。我们可以在项目中创建store目录存储我们定义的各种store:

// src/store/formInfo.js
import { defineStore } from 'pinia';
​
// 第一个参数是应用程序中 store 的唯一 id
const useFormInfoStore = defineStore('formInfo', {
  // 其他配置项,后面逐一说明
})
​
export default useFormInfoStore;

defineStore接收两个参数:

  • name:一个字符串,必传项,该store的唯一id。
  • options:一个对象,store的配置项,比如配置store内的数据,修改数据的方法等等。

将返回的函数命名为 use… 是组合式开发的约定,使其符合使用习惯。我们可以根据项目情况定义任意数量的store存储不同功能模块的数据,一个store就是一个函数,它和Vue3的实现思想也是一致的。

使用store

我们可以在任意组件中引入定义的store来进行使用


store 被实例化后,你就可以直接在 store 上访问 stategettersactions 中定义的任何属性。

解构store

store 是一个用reactive 包裹的对象,这意味着不需要在getter 之后写.value,但是,就像setup 中的props 一样,我们不能对其进行解构,如果我们想要提取store中的属性同时保持其响应式的话,我们需要使用storeToRefs(),它将为响应式属性创建refs。


State

store是用来存储全局状态数据的仓库,那自然而然需要有地方能够保存这些数据,它们就保存在state里面。defineStore传入的第二个参数options配置项里面,就包括state属性。

// src/store/formInfo.js
import { defineStore } from 'pinia';
​
const useFormInfoStore = defineStore('formInfo', {
   // 推荐使用 完整类型推断的箭头函数
   state: () => {
      return {
        // 所有这些属性都将自动推断其类型
        name: 'Hello World',
        age: 18,
        isStudent: false
      }
   }
  
   // 还有一种定义state的方式,不太常见,了解即可
   // state: () => ({
   //    name: 'Hello World',
   //    age: 18,
   //    isStudent: false
   // })
})
​
export default useFormInfoStore;
访问state

默认情况下,您可以通过 store 实例来直接读取和写入状态:


pinia还提供了几个常见场景的方法供我们使用来操作state:$reset$patch$state$subscribe


针对上面示例,有几点需要关注一下:

  • 1.同直接修改state中的属性不同,通过$patch方法更新多个属性时,在devtools的timeline中,是合并到一个条目中的。

Pinia不酸,保甜_第1张图片

  • 2.通过实验得知,$state在进行替换时,会更新已有的属性,新增原来state不存在的属性,针对不在替换范围内的,则保持不变。

Pinia不酸,保甜_第2张图片

如上图,针对gender属性,执行的是"add"操作,然后这个替换过程我们没有设置isStudent属性,它仍然保持原状态在state中不变。

  • 3. s u b s c r i b e 只会订阅到 p a t c h e s 引起的变化,即上面的通过 subscribe只会订阅到patches引起的变化,即上面的通过 subscribe只会订阅到patches引起的变化,即上面的通过patch方法和$state覆盖时会触发到状态订阅,直接修改state的属性则不会触发。

Getters

pinia中的getters可以完全等同于Store状态的计算属性,通常在defineStore中的getters属性中定义。同时支持组合多个getter,此时通过this访问其他getter。

import { defineStore } from 'pinia';
​
const useFormInfoStore = defineStore('formInfo', {
    state: () => {
        return {
            name: 'Hello World',
            age: 18,
            isStudent: false,
            gender: '男'
        };
    },
    getters: {
        // 仅依赖state,通过箭头函数方式
        isMan: (state) => {
            return state.gender === '男';
        },
        isWoman() {
            // 通过this访问其他getter,此时不可以用箭头函数
            return !this.isMan;
        }
    }
});
​
export default useFormInfoStore;
​

在使用时,我们可以直接在store实例上面访问getter: