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 核心概念
- State: 状态,store 的数据源
- Getters: 计算属性,基于 state 的派生状态
- 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 性能优化
- 避免不必要的计算
// 使用 computed 缓存结果
const expensiveOperation = computed(() => {
return store.items.filter(/* 复杂的过滤逻辑 */);
});
- 局部状 态管理
<script setup>
// 仅在需要时使用 store
const store = useStore();
const { specific } = storeToRefs(store);
</script>
5.3 调试
// 开发工具集成
const store = useStore();
store.$state; // 查看状态
store.$reset(); // 重置状态
store.$patch(/* 更新 */); // 批量更新