Skip to main content

Pinia

Pinia 是 Vue 的官方状态管理库,它提供了一个更简单、更类型安全的方式来管理 Vue 应用的全局状态。

1. 基础概念

1.1 Store

Store 是一个保存状态和业务逻辑的实体,在 Pinia 中使用 defineStore() 定义:

import { defineStore } from 'pinia';

// 选项式写法
export const useCounterStore = defineStore('counter', {
state: () => ({
count: 0,
name: 'Counter',
}),
getters: {
doubleCount: (state) => state.count * 2,
},
actions: {
increment() {
this.count++;
},
},
});

// 组合式写法
export const useCounterStore = defineStore('counter', () => {
const count = ref(0);
const name = ref('Counter');

const doubleCount = computed(() => count.value * 2);

function increment() {
count.value++;
}

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

1.2 核心概念

  1. State: 状态,store 的数据源
  2. Getters: 计算属性,基于 state 的派生状态
  3. Actions: 方法,可以包含业务逻辑和异步操作

2. 基础使用

2.1 安装和配置

# 安装
npm install pinia

# 或使用 yarn
yarn add pinia
// main.ts
import { createApp } from 'vue';
import { createPinia } from 'pinia';
import App from './App.vue';

const pinia = createPinia();
const app = createApp(App);

app.use(pinia);
app.mount('#app');

2.2 定义 Store

// stores/counter.ts
import { defineStore } from 'pinia';

export const useCounterStore = defineStore('counter', {
// 状态
state: () => ({
count: 0,
items: [],
user: null,
}),

// 计算属性
getters: {
doubleCount: (state) => state.count * 2,
// 访问其他 getter
doubleCountPlusOne(): number {
return this.doubleCount + 1;
},
},

// 方法
actions: {
increment() {
this.count++;
},
async fetchItems() {
const items = await api.getItems();
this.items = items;
},
},
});

2.3 在组件中使用

<template>
<div>
<p>Count: {{ counter.count }}</p>
<p>Double count: {{ counter.doubleCount }}</p>
<button @click="counter.increment">Increment</button>
<button @click="increment">Increment in component</button>
</div>
</template>

<script setup>
import { useCounterStore } from '@/stores/counter';

const counter = useCounterStore();

// 解构 store(会失去响应性)
const { count, doubleCount } = counter;

// 保持响应性的解构
const { count, doubleCount } = storeToRefs(counter);

// 在组件中使用 action
function increment() {
counter.increment();
}
</script>

3. 高级特性

3.1 订阅状态变化

// 监听状态变化
const unsubscribe = counterStore.$subscribe((mutation, state) => {
console.log('Changed:', mutation.type, mutation.payload);
console.log('New state:', state);
});

// 监听 action
const unsubscribe = counterStore.$onAction(({
name, // action 名称
store, // store 实例
args, // 传递给 action 的参数
after, // 在 action 成功完成后的钩子
onError, // action 抛出错误时的钩子
}) => {
console.log(`Action ${name} was called`);

after((result) => {
console.log(`Action ${name} completed with result:`, result);
});

onError((error) => {
console.error(`Action ${name} failed with error:`, error);
});
});

// 取消订阅
unsubscribe();

3.2 插件系统

// 持久化插件示例
import { PiniaPluginContext } from 'pinia';

function persistencePlugin({ store }: PiniaPluginContext) {
// 从 localStorage 恢复状态
const savedState = localStorage.getItem(`${store.$id}`);
if (savedState) {
store.$patch(JSON.parse(savedState));
}

// 监听变化并保存到 localStorage
store.$subscribe((mutation, state) => {
localStorage.setItem(`${store.$id}`, JSON.stringify(state));
});
}

// 使用插件
const pinia = createPinia();
pinia.use(persistencePlugin);

3.3 组合多个 Store

// stores/user.ts
export const useUserStore = defineStore('user', {
state: () => ({
userId: null,
username: '',
}),
actions: {
async login(credentials) {
// 登录逻辑
},
},
});

// stores/cart.ts
export const useCartStore = defineStore('cart', {
state: () => ({
items: [],
}),
actions: {
async checkout() {
const userStore = useUserStore();
if (!userStore.userId) {
throw new Error('Must be logged in to checkout');
}
// 结账逻辑
},
},
});

3.4 热更新支持

// store 定义中添加热更新支持
export const useCounterStore = defineStore('counter', {
state: () => ({ /* ... */ }),
getters: { /* ... */ },
actions: { /* ... */ },
});

if (import.meta.hot) {
import.meta.hot.accept(acceptHMRUpdate(useCounterStore, import.meta.hot));
}

4. TypeScript 支持

4.1 类型定义

interface State {
user: User | null;
posts: Post[];
loading: boolean;
}

export const useStore = defineStore('main', {
state: (): State => ({
user: null,
posts: [],
loading: false,
}),

getters: {
userPosts(): Post[] {
return this.posts.filter(post => post.userId === this.user?.id);
},
},
});

4.2 自定义类型

// 扩展 store 类型
declare module 'pinia' {
export interface PiniaCustomProperties {
// 添加自定义属性
set: typeof set;
// 添加自定义 state
customRef: string;
}

export interface PiniaCustomStateProperties<S> {
// 为每个 store 添加自定义 state
hello: string;
}
}

5. 最佳实践

5.1 Store 组织

src/
└── stores/
├── index.ts # store 入口
├── modules/ # 模块化 store
│ ├── user.ts
│ ├── cart.ts
│ └── product.ts
└── types.ts # 类型定义

5.2 性能优化

  1. 避免不必要的计算
// 使用 computed 缓存结果
const expensiveOperation = computed(() => {
return store.items.filter(/* 复杂的过滤逻辑 */);
});
  1. 局部状态管理
<script setup>
// 仅在需要时使用 store
const store = useStore();
const { specific } = storeToRefs(store);
</script>

5.3 调试

// 开发工具集成
const store = useStore();
store.$state; // 查看状态
store.$reset(); // 重置状态
store.$patch(/* 更新 */); // 批量更新

参考资源