Skip to main content

Pinia

Pinna 是 Vue 的状态管理库,支持跨组件、页面共享状态。同时支持 vue2 和 vue3。

官网:https://pinia.vuejs.org

和 vuex 的区别

  • 支持组合式 API
  • 可搭配 TypeScript 使用,提供可靠的类型推断
  • mutation 已被弃用
  • actions 中支持同步和异步方法修改 state 状态
  • 每一个 store 仓库都是独立的、扁平化的(不再像 vuex 中的 modules 嵌套),store 之间可以相互调用
  • 更加轻量,压缩之后体积只有 1kb 左右

选项式 API 的写法

import { defineStore } from 'pinia';

const useCounterStore = defineStore('counter', {
state: () => ({
count: 1
}),
getters: {
doubleCount: state => state.count * 2
},
actions: {
increment() {
this.count++;
}
}
});

export default useCounterStore;

组合式 API 的写法

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

const useCounterStore = defineStore('counter', () => {
const count = ref(0);
const doubleCount = computed(() => count.value * 2);
function increment() {
count.value++;
}

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

export default useCounterStore;

在组件中使用:

import useCounterStore from '@/stores/counter';
const counterStore = useCounterStore();

console.log(counterStore);
console.log(counterStore.count);

counterStore 的打印结果是一个 Proxy 对象,包括 $dispose$id$onAction$patch$reset$subscribe$state_hotUpdate_isOptionsAPI等属性和方法,还包括自己在 store 中定义的 countincrement。所以在访问变量时不能写成counterStore.state.count

修改数据

修改数据的方式有三种:

  1. 直接修改 state 的值,如 counter.count++
  2. 使用 $patch 方法,可以在同一时间更改多个属性
  3. 调用 actions 中的方法,如 counter.increment()
import useCounterStore from '@/stores/counter';
const counter = useCounterStore();

counter.count++;

counter.$patch({ count: counter.count + 1 });

counter.increment();

数据解构

下面的方式会丢失响应性:

import useCounterStore from '@/stores/counter';
const { count } = useCounterStore();

可以用 toRefs(),但是代价很大,会把所有属性都变为 ref 包裹。

所以官方推出 storeToRefs 这个 API,创建一个引用对象,包含 store 的所有 state、 getter 和 plugin 添加的 state 属性。 类似于 toRefs(),但专门为 Pinia store 设计, 所以 method 和非响应式属性会被完全忽略。

import { storeToRefs } from 'pinia';
import useCounterStore from '@/stores/counter';

const counter = useCounterStore();
const { count } = storeToRefs(counter);

订阅 state

可以通过 store 的 $subscribe() 方法侦听 state 及其变化。

如果添加第二个参数:{ detached: true },则表示当前组件被卸载时,订阅不会被删除

counter.$subscribe((mutation, state) => {
// 每当状态发生变化时,将整个 state 持久化到本地存储。
localStorage.setItem('count', JSON.stringify(state));
});
tip

可以在组件中使用 watch() 函数侦听整个 state。

import useCounterStore from '@/stores/counter';
import { watch } from 'vue';

const counter = useCounterStore();

watch(
() => ({ ...counter.$state }), // 侦听整个 state
(newState, oldState) => {
console.log('New State:', newState);
console.log('Old State:', oldState);
},
{ deep: true } // 深度监听
);

持久化数据