状态管理器Pinia

1,076 阅读7分钟

1. 前言

在介绍什么是pinia之前,有必要介绍一下状态管理器和vue官方的vuex。 首先申明并不是所有的Web应用和使用场景都需要添加状态管理,很多时候服务端渲染任然是更好的选择,而是否需要添加状态管理框架,是用哪一种状态管理框架, 根据自己的业务实际情况和技术团队的偏好而添加,而有些情况下,自己创建一个全局对象可能是更适合的选择。 如果非要解释状态的意思,大概意思是:现在的样子;之前的样子;以后的样子。一个服务现在的样子;之后的样子。也就是说状态是动态的。可以被我看到,也可以被你看到,比如说今天是晴天,我把被子拿出去晒晒,那么天气就是变量,可以存储的就是晴天,雨天,这些就是状态。生活中有无穷无尽的状态。而我们关注的状态有限,所以在代码中,我们将关注的状态放在一个容器中,这个容器就是状态管理器。 刚刚说到,今天是晴天,所以我可以把被子拿出去晒晒。晴天这个状态,我可以看到,任何人都能看到,所以状态是共享的,也是可以限制的。比如说,武汉今天是晴天,可能重庆就在下雨,所以状态也可以有局限性,我们称这种叫做状态的边界。

1.1. 历史中的状态管理

如果从代码中理解状态,大概就是某个服务在运行时所产生的的内存和运算决定的,拥有生命周期。理论上任何时刻的状态都不同,因为它内部必然会有即使细微的变化。 我们在前端开发中,也经常会去使用到状态管理器,通过一段代码来感受一下

const state = true;
if(state){
	$('#tableRef').hide();
  state = false;
}else{
  $('#tableRef').show();
  state = true;
}

这个代码我相信大家或多或少都有写过,在还没有前端框架的时候,我们就通过这种方式来进行状态的判断,然后展现页面某个部分的不同效果。

1.2. Vue的状态管理器Vuex

后来,我们有了前端框架,比如说React、Vue、Angular,我常用的是Vue。Vuex 是Vue官方的状态管理器,它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。 Vuex有state和用来操作state中数据的方法集,以及当我们需要对state中的数据需要加工的方法集等等成员。 成员列表:

  • state 存放状态
  • mutations state成员操作
  • getters 加工state成员给外界
  • actions 异步操作
  • modules 模块化状态管理

而我感觉,在大多数时候,vuex@3/4版本除了网上经常看到的对typescript兼容性体验感不好,体积过大,对中小型和低复杂性应用不够友好以外,还有mutations的冗余,因为mutations能干的事在actions都能干,而且能做的更好,所以官方在对vuex@5版本的提案中计划要删除mutations,并且更好的支持Typescript,不过效果如何,日后在vuex5出来再研究。

1.3 Pinia

Pinia是 2019年11月左右出来的,是一个 Vue 的存储库, 它能让你跨组件/页面共享状态,目的是尝试重新定义Vue中的Store和Components API联合使用时应该是怎样的。不过需要提到的一点是,Pinia的作者同样是Vue团队的,所以不管是文档还是使用方式上,基本上都与Vuex类似,也就是说,学习成本不会太高。 而Pinia避免了在上文中Vuex的问题,但由于出来的时间比较短,所以可能社区生态没有vuex大,解决方案也相对没有Vuex多,但是日渐火热的Pinia也足以看出开发者对它的喜爱。 Pinia 同时对 Vue 2 以及 Vue 3同时有效, 并且并不要求你使用 API,使用API时API 对Vue2和Vue3来说都是一样的,这就给我们使用Pinia来说提供了极大的便利。

优势:

  • 模块热替换
    • 在不要求重新加载页面条件下修改你的 stores
    • 当开发时保持所有存在的状态(state)
  • 插件: 使用插件扩展Pinia功能
  • 为JS用户提供适当的 TypeScript 支持与自动完成功能

2. 安装Pinia

2.1. 安装Pinia依赖

npm install pinia

2.2. 配置Pinia

在src下新建stores,并在store文件夹下新建index.ts

import { defineStore } from "pinia";

export default defineStore("counter", {
  state: () => {
    return { count: 0 };
  },
  actions: {
    increment() {
      this.count++;
    },
  },
});

与vuex不同的是在defineStore中加了一个id属性,在定义好这个模块后,将这个模块进程到这个工程中,编辑main.ts文件

// 仅展示核心代码
import { createPinia } from "pinia";

const app = createApp(App);
app.use(createPinia);
app.mount("#app");

3. 使用Pinia

3.2. State

在默认情况下,可以直接通过实例读、写状态

const store = useStore()
store.counter++

同时也可以将状态重置恢复到初始化定义的值:$reset()

const store = useStore()
store.$reset()

还可以直接替换掉所有的state中的值,但是最好不要这样操作

store.$state = { counter: 666, name: 'Paimon' }

3.3. Getters

Getter 完全等同于store中的state计算值,它们可以用属性来定义,Pinia官方鼓励用箭头表达式的方式来创建state。大多数时候,getter都依赖state,也有可能依赖于其他的getter方法。 在getters中,可以通过this来访问到state中的属性,但是一般情况我们不建议使用,建议使用的方式是定义一个函数时,用入参state来访问state中的属性。然后就可以在任意地方使用getter方法

export const useStore = defineStore('main', {
  state: () => ({
    counter: 0,
  }),
  getters: {
    // 自动推断返回类型是number类型
    doubleCount(state) {
      return state.counter * 2
    },
    // 如果返回类型必须要设定时
    doublePlusOne(): number {
      // 所有的代码和类型都会自动提示 ✨
      return this.counter * 2 + 1
    },
  },
})

相对于getter方法,与计算属性类似,所以我们一般会在方法上编辑JSDoc,当然,一般情况下,必须编辑,便于维护。

3.4. Actions

Actions相当于组件中的方法一样,这些方法在defineStore()中的actions中被定义。和getters一样可以通过this访问state,而且支持自动补全。不一样的是actions支持异步或同步操作,而getters不支持。 理论上,在使用的过程中,可以自动推断出来是什么类型,然后给相应的自动补全,但是只是理论上,具体情况还是要解决,特别是用ts+vscode的时候。在使用其他actions的时候直接使用就好,下面有一个例子。

import { useAuthStore } from './auth-store'

export const useSettingsStore = defineStore('settings', {
  state: () => ({
    // ...
  }),
  actions: {
    async fetchUserPreferences(preferences) {
      const auth = useAuthStore()
      if (auth.isAuthenticated) {
        this.preferences = await fetchPreferences()
      } else {
        throw new Error('用户必须被授权才能访问')
      }
    },
  },
})

在setup中,直接像以下方式使用即可

export default {
  setup() {
    const store = useStore()

    store.randomizeCounter()
  },
}

4. 总结

虽然看上去Pinia挺强大,然后也好用,最起码没有了mutations,然后比Vuex小,而且基本上没有什么学习成本,可以直接上手去用,作者也写了Pinia是为了让社区来测试下一个版本的Vuex该怎样去发展。所以我们选择什么样的技术栈还是根据需求来选择合适的技术栈。 本文没有涉及SSR服务端渲染和Pinia插件,因为这两者的社区和使用人数相对来说群体不是很大,另一个也是跟笔者的技术栈没有什么交集,所以,没有往这两个方向深入。而Pinia的插件,确实有很多很好的插件,但是用的时候或多或少会出现bug,也很难找到解决方案,如果追求稳定性的话,推荐大家还是继续使用Vuex,如果是中小型项目和低复杂度的程序,完全可以用Pinia,要比Vuex更好用