Pinia 学着上手

1,108 阅读4分钟

前言

Pinia读音:/paɪnæ/,是Vue官方团队推荐代替Vuex的一款轻量级状态管理库。 它最初的设计理念是让Vue Store拥有一款Composition API方式的状态管理库,并同时能支持 Vue2.x版本的Option API 和 Vue3版本的setup Composition API开发模式,并完整兼容Typescript写法(这也是优于Vuex的重要因素之一),适用于所有的vue项目。

Pinia具备以下优点:

  • 完整的 TypeScript 支持:与在 Vuex 中添加 TypeScript 相比,添加 TypeScript 更容易
  • 极其轻巧(体积约 1KB)
  • store 的 action 被调度为常规的函数调用,而不是使用 dispatch 方法或 MapAction 辅助函数,这在 Vuex 中很常见
  • 支持多个Store
  • 支持 Vue devtools、SSR 和 webpack 代码拆分

Pinia与Vuex代码分割机制

Pinia轻量有一部分体现在它的代码分割机制中。

举个例子:某项目有3个store「user、job、pay」,另外有2个路由页面「首页、个人中心页」首页用到job store,个人中心页用到了user store,分别用Pinia和Vuex对其状态管理。

image.png

先看Vuex的代码分割:  打包时,vuex会把3个store合并打包,当首页用到Vuex时,这个包会引入到首页一起打包,最后输出1个js chunk。这样的问题是,其实首页只需要其中1个store,但其他2个无关的store也被打包进来,造成资源浪费。

image.png

Pinia的代码分割: 打包时,Pinia会检查引用依赖,当首页用到job store,打包只会把用到的store和页面合并输出1个js chunk,其他2个store不耦合在其中。Pinia能做到这点,是因为它的设计就是store分离的,解决了项目的耦合问题。

Pinia的常规用法

1. 安装

yarn add pinia
# or with npm
npm install pinia

2. 挂载全局实例

import { createPinia } from 'pinia'

app.use(createPinia())

3. 创建第一个store

src/store/counterForOptions.ts创建你的store。定义store模式有2种:

  • 使用options API模式定义,这种方式和vue2的组件模型形式类似,也是对vue2技术栈开发者较为友好的编程模式

    import { defineStore } from 'pinia';
    
    // 使用options API模式定义
    export const useCounterStoreForOption = defineStore('counterForOptions', {
      // 定义state
      state: () => {
        return { count1: 1 };
      },
      // 定义action
      actions: {
        increment() {
          this.count1++;
        }
      },
      // 定义getters
      getters: {
        doubleCount(state) {
          return state.count1 * 2;
        }
      }
    });
    
  • 推荐使用setup模式定义,符合Vue3 setup的编程模式,让结构更加扁平化,个人推荐推荐使用这种方式。

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

// 使用setup模式定义
export const useCounterStoreForSetup = defineStore('counterForSetup', () => {
  const count = ref<number>(1);
  function increment() {
    count.value++;
  }

  function doubleCount() {
    return count.value * 2;
  }

  return { count, increment, doubleCount };
});

上面2种定义方式效果都是一样的,我们用defineStore方法定义一个store,里面分别定义1个count的state,1个increment action 和1个doubleCount的getters。其中state是要共享的全局状态,而action则是让业务方调用来改变state的入口,getters是获取state的计算结果。

之所以用第一种方式定义是还要额外写gettersaction关键字来区分,是因为在options API模式下可以通过mapState()mapActions()等方法获取对应项;但第二种方式就可以直接获取了(下面会细述)。

4. 业务组件对store的调用

src/components/xxx.vue目录下创建个组件。

<script setup lang="ts" name="component-xxx">
import { storeToRefs } from 'pinia';
import { useCounterStoreForSetup } from '@/store/counterForSetup';

// setup composition API模式
const counterStoreForSetup = useCounterStoreForSetup();
const { count } = storeToRefs(counterStoreForSetup);
const { increment, doubleCount } = counterStoreForSetup;
</script>

<template>
  <div class="box-styl">
    <h1>Setup模式</h1>
    <p class="section-box">
      Pinia的state: count = <b>{{ count }}</b>
    </p>
    <p class="section-box">
      Pinia的getters: doubleCount() = <b>{{ doubleCount() }}</b>
    </p>
    <div class="section-box">
      <p>Pinia的action: increment()</p>
      <button @click="increment">点我</button>
    </div>
  </div>
</template>

<style lang="less" scoped>
  .box-styl {
    margin: 10px;
    .section-box {
      margin: 20px auto;
      width: 300px;
      background-color: #d7ffed;
      border: 1px solid #000;
    }
  }
</style>

  • Pinia在setup模式下的调用机制是先install再调用
  • install这样写: const counterStoreForSetup = useCounterStoreForSetup();,其中 useCounterStoreForSetup就是你定义store的变量;
  • 调用就直接用 counterStoreForSetup.xxx(xxx包括:state、getters、action)就好。
  • 代码中获取state是用了解构赋值,为了保持state的响应式特性,需要用storeToRefs进行包裹。

兼容Vue2的Options API调用方式可以到 这里

5. 良好的编程习惯

state的改变交给action去处理: 上面例子,counterStoreForSetup有个pinia实例属性叫$state$patch是可以直接改变state的值,但不建议怎么做。一是难维护,在组件繁多情况下,一处隐蔽state更改,整个开发组帮你排查;二是破坏store封装,难以移植到其他地方。所以,为了你的声誉和安全着想,请停止游离之外的coding😇😇。