Pinia是Vue3官方推荐的状态管理库,具有轻量(约1kb)、完整的TypeScript支持等特点。
安装后需创建Pinia实例并在main.ts中注册,通过defineStore定义store,包含state、getters和actions三部分。
在组件中使用storeToRefs保持响应式,支持直接修改状态或通过actions操作。
与Vuex4相比,Pinia更轻量、模块化设计更简洁。
两者可通过检查package.json、目录结构和使用方式区分,虽可共存但不推荐长期使用。
最佳实践是按功能模块拆分store,使用TypeScript增强类型安全,合理组织代码结构。
Pinia 使用详解
什么是 Pinia?
Pinia 是 Vue 3 官方推荐的状态管理库,是 Vuex 的替代品。它具有以下特点:
-
完整的 TypeScript 支持
-
极其轻量(约 1kb)
-
支持 Composition API 和 Options API
-
模块化设计,无需嵌套模块
-
支持热更新
-
支持 SSR
安装
bash
npm install pinia
# 或
yarn add pinia
# 或
pnpm add pinia
基本配置
1. 创建 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. 定义 Store
// stores/counter.ts
import { defineStore } from 'pinia'
// 定义并导出 store
export const useCounterStore = defineStore('counter', {
// 状态
state: () => ({
count: 0,
name: 'Pinia Store'
}),
// 计算属性
getters: {
doubleCount: (state) => state.count * 2,
doubleCountPlusOne(): number {
return this.doubleCount + 1
}
},
// 操作方法
actions: {
increment() {
this.count++
},
decrement() {
this.count--
},
async incrementAsync() {
await new Promise(resolve => setTimeout(resolve, 1000))
this.count++
}
}
})
Composition API 风格(推荐)
1. 使用 Store
vue
<!-- Counter.vue -->
<script setup lang="ts">
import { useCounterStore } from '@/stores/counter'
import { storeToRefs } from 'pinia'
const counterStore = useCounterStore()
// 直接解构会失去响应性
// ❌ const { count, name } = counterStore
// 使用 storeToRefs 保持响应性
const { count, name, doubleCount } = storeToRefs(counterStore)
// 调用 actions
const increment = () => {
counterStore.increment()
}
// 直接修改 state
const updateName = () => {
counterStore.name = 'New Name'
// 或者使用 $patch
counterStore.$patch({
name: 'New Name'
})
}
// 重置状态
const reset = () => {
counterStore.$reset()
}
// 订阅状态变化
counterStore.$subscribe((mutation, state) => {
console.log('状态变化:', mutation.type, state)
})
// 订阅 actions
counterStore.$onAction(({
name, // action 名称
store, // store 实例
args, // 参数
after, // action 成功后的钩子
onError // action 失败后的钩子
}) => {
console.log(`开始执行 ${name},参数:`, args)
after((result) => {
console.log(`${name} 执行成功,结果:`, result)
})
onError((error) => {
console.error(`${name} 执行失败:`, error)
})
})
</script>
<template>
<div>
<h2>{{ name }}</h2>
<p>Count: {{ count }}</p>
<p>Double Count: {{ doubleCount }}</p>
<button @click="increment">+1</button>
<button @click="counterStore.decrement">-1</button>
</div>
</template>
Options API 风格
vue
<script lang="ts">
import { defineComponent } from 'vue'
import { mapState, mapActions } from 'pinia'
import { useCounterStore } from '@/stores/counter'
export default defineComponent({
computed: {
// 映射 state 和 getters
...mapState(useCounterStore, ['count', 'name', 'doubleCount'])
// 或重命名
...mapState(useCounterStore, {
myCount: 'count',
myName: 'name'
})
},
methods: {
// 映射 actions
...mapActions(useCounterStore, ['increment', 'decrement'])
}
})
</script>
高级用法
1. 多个 Store 组合
// stores/user.ts
import { defineStore } from 'pinia'
interface User {
id: number
name: string
email: string
}
export const useUserStore = defineStore('user', {
state: () => ({
user: null as User | null,
token: ''
}),
actions: {
async login(credentials: { username: string; password: string }) {
// 模拟登录
const response = await api.login(credentials)
this.user = response.user
this.token = response.token
},
logout() {
this.user = null
this.token = ''
}
},
persist: true // 持久化配置
})
// stores/cart.ts
import { defineStore } from 'pinia'
import { useUserStore } from './user'
interface CartItem {
id: number
name: string
price: number
quantity: number
}
export const useCartStore = defineStore('cart', {
state: () => ({
items: [] as CartItem[],
total: 0
}),
getters: {
itemCount: (state) => {
return state.items.reduce((sum, item) => sum + item.quantity, 0)
},
// 访问其他 store
isUserCart: (state) => {
const userStore = useUserStore()
return userStore.user ? `${userStore.user.name}'s Cart` : 'Guest Cart'
}
},
actions: {
addItem(item: CartItem) {
const existing = this.items.find(i => i.id === item.id)
if (existing) {
existing.quantity += item.quantity
} else {
this.items.push(item)
}
this.calculateTotal()
},
calculateTotal() {
this.total = this.items.reduce(
(sum, item) => sum + (item.price * item.quantity),
0
)
}
}
})
2. TypeScript 高级类型
// stores/types.ts
export interface Product {
id: string
name: string
price: number
category: string
}
export interface ProductState {
products: Product[]
selectedCategory: string | null
loading: boolean
error: string | null
}
export interface ProductGetters {
filteredProducts: Product[]
totalValue: number
}
export interface ProductActions {
fetchProducts(): Promise<void>
addProduct(product: Omit<Product, 'id'>): Promise<string>
deleteProduct(id: string): Promise<void>
}
// 使用泛型定义 Store
export const useProductStore = defineStore<
'product',
ProductState,
ProductGetters,
ProductActions
>('product', {
state: (): ProductState => ({
products: [],
selectedCategory: null,
loading: false,
error: null
}),
getters: {
filteredProducts: (state) => {
if (!state.selectedCategory) return state.products
return state.products.filter(
p => p.category === state.selectedCategory
)
},
totalValue: (state) => {
return state.products.reduce((sum, p) => sum + p.price, 0)
}
},
actions: {
async fetchProducts() {
this.loading = true
this.error = null
try {
const response = await api.getProducts()
this.products = response
} catch (error) {
this.error = error.message
} finally {
this.loading = false
}
},
async addProduct(productData) {
const newProduct = {
...productData,
id: generateId()
}
this.products.push(newProduct)
return newProduct.id
}
}
})
3. Store 间通信
// stores/root.ts
import { useCounterStore } from './counter'
import { useUserStore } from './user'
import { useCartStore } from './cart'
export function useRootStore() {
const counter = useCounterStore()
const user = useUserStore()
const cart = useCartStore()
const resetAll = () => {
counter.$reset()
user.$reset()
cart.$reset()
}
const getTotalItems = () => {
return cart.itemCount + counter.count
}
return {
counter,
user,
cart,
resetAll,
getTotalItems
}
}
插件系统
1. 持久化插件
// plugins/persistence.ts
import { PiniaPluginContext } from 'pinia'
export function piniaPersistencePlugin(context: PiniaPluginContext) {
const { store } = context
// 从 localStorage 恢复状态
const savedState = localStorage.getItem(store.$id)
if (savedState) {
store.$patch(JSON.parse(savedState))
}
// 监听状态变化并保存
store.$subscribe((mutation, state) => {
localStorage.setItem(store.$id, JSON.stringify(state))
})
}
// 安装插件
const pinia = createPinia()
pinia.use(piniaPersistencePlugin)
2. 使用官方持久化插件
bash
npm install pinia-plugin-persistedstate
// main.ts
import { createPinia } from 'pinia'
import piniaPluginPersistedstate from 'pinia-plugin-persistedstate'
const pinia = createPinia()
pinia.use(piniaPluginPersistedstate)
// 在 store 中使用
export const useUserStore = defineStore('user', {
state: () => ({
user: null,
token: ''
}),
persist: {
key: 'user-storage',
paths: ['token'], // 只持久化 token
storage: localStorage,
beforeRestore: (ctx) => {
console.log('准备恢复状态:', ctx.store.$id)
},
afterRestore: (ctx) => {
console.log('状态恢复完成:', ctx.store.$id)
}
}
})
最佳实践
1. 目录结构
src/
├── stores/
│ ├── modules/
│ │ ├── user.ts
│ │ ├── cart.ts
│ │ └── product.ts
│ ├── index.ts # 导出所有 store
│ └── types.ts # 类型定义
2. 代码组织
// stores/index.ts
export { useUserStore } from './modules/user'
export { useCartStore } from './modules/cart'
export { useProductStore } from './modules/product'
export { useRootStore } from './root'
3. 测试 Store
// __tests__/stores/counter.spec.ts
import { setActivePinia, createPinia } from 'pinia'
import { useCounterStore } from '@/stores/counter'
describe('Counter Store', () => {
beforeEach(() => {
setActivePinia(createPinia())
})
test('increment', () => {
const counter = useCounterStore()
expect(counter.count).toBe(0)
counter.increment()
expect(counter.count).toBe(1)
})
test('doubleCount getter', () => {
const counter = useCounterStore()
counter.count = 5
expect(counter.doubleCount).toBe(10)
})
})
注意事项
-
避免直接修改状态:尽量使用 actions 修改状态
-
合理拆分 Store:按功能模块拆分,避免单个 Store 过大
-
使用 TypeScript:充分利用 Pinia 的类型支持
-
性能优化:避免在 getters 中执行复杂计算
-
清理订阅 :组件卸载时清理
$subscribe和$onAction
与 Vuex 的对比
| 特性 | Pinia | Vuex 4 |
|---|---|---|
| 大小 | ~1KB | ~10KB |
| TypeScript 支持 | 一流 | 需要额外配置 |
| Composition API | 原生支持 | 通过 setup 支持 |
| 模块化 | 扁平化,自动按需加载 | 嵌套模块 |
| 热更新 | 支持 | 支持 |
| 插件系统 | 简单灵活 | 复杂但功能强大 |
Pinia 是 Vue 3 项目中的首选状态管理方案,它简单、直观且功能强大,适合各种规模的应用。
如何查看或区分项目使用的是 Pinia 还是 Vuex 4
1. 检查 package.json
这是最直接的方法:
Pinia 项目:
json
{
"dependencies": {
"pinia": "^2.0.0",
// 不会有 vuex
}
}
Vuex 4 项目:
json
{
"dependencies": {
"vuex": "^4.0.0",
// 不会有 pinia
}
}
也可能两者都有(迁移中):
json
{
"dependencies": {
"pinia": "^2.0.0",
"vuex": "^4.0.0"
}
}
2. 检查 main.js / main.ts 入口文件
Pinia 配置:
// main.js / main.ts
import { createApp } from 'vue'
import { createPinia } from 'pinia' // 👈 关键导入
import App from './App.vue'
const app = createApp(App)
const pinia = createPinia() // 👈 创建实例
app.use(pinia) // 👈 安装插件
app.mount('#app')
Vuex 4 配置:
// main.js / main.ts
import { createApp } from 'vue'
import { createStore } from 'vuex' // 👈 关键导入
import App from './App.vue'
// 创建 store
const store = createStore({
state() {
return { count: 0 }
},
mutations: {
increment(state) {
state.count++
}
}
})
const app = createApp(App)
app.use(store) // 👈 安装
app.mount('#app')
3. 检查 store 目录结构
Pinia 典型结构:
src/
├── stores/ # 👈 通常命名为 stores
│ ├── user.js / user.ts
│ ├── cart.js / cart.ts
│ ├── product.js / product.ts
│ └── index.js
Vuex 4 典型结构:
src/
├── store/ # 👈 通常命名为 store
│ ├── index.js # 主文件
│ ├── modules/ # 模块目录
│ │ ├── user.js
│ │ ├── cart.js
│ │ └── product.js
│ └── mutation-types.js # 常量文件
4. 检查 store 文件内容
Pinia store 文件特征:
// stores/counter.js
import { defineStore } from 'pinia' // 👈 从 pinia 导入
// 使用 defineStore 定义 store
export const useCounterStore = defineStore('counter', {
state: () => ({
count: 0
}),
getters: {
doubleCount: (state) => state.count * 2
},
actions: {
increment() {
this.count++
}
}
})
Vuex 4 store 文件特征:
// store/modules/user.js
export default {
namespaced: true, // 👈 Vuex 的命名空间
state: () => ({
user: null
}),
mutations: { // 👈 mutations 是 Vuex 特有的
SET_USER(state, user) {
state.user = user
}
},
actions: {
login({ commit }, credentials) {
// ... 登录逻辑
commit('SET_USER', user) // 👈 通过 commit 调用 mutation
}
},
getters: {
isLoggedIn: (state) => !!state.user
}
}
5. 检查组件中的使用方式
Pinia 在组件中的使用:
vue
<script setup>
import { useCounterStore } from '@/stores/counter'
const counterStore = useCounterStore()
// 直接修改 state(Pinia 允许)
counterStore.count++
// 或使用 action
counterStore.increment()
// 访问 getter
console.log(counterStore.doubleCount)
</script>
Vuex 4 在组件中的使用:
Options API:
vue
<script>
import { mapState, mapMutations, mapActions } from 'vuex'
export default {
computed: {
...mapState(['count']),
...mapGetters(['doubleCount'])
},
methods: {
...mapMutations(['increment']),
...mapActions(['fetchData'])
}
}
</script>
Composition API:
vue
<script setup>
import { useStore } from 'vuex'
const store = useStore()
// 访问 state
const count = computed(() => store.state.count)
// 调用 mutation
const increment = () => {
store.commit('increment')
}
// 调用 action
const fetchData = () => {
store.dispatch('fetchData')
}
</script>
6. 查看 import 语句
Pinia 的 import:
javascript
import { defineStore } from 'pinia'
import { storeToRefs } from 'pinia'
import { createPinia } from 'pinia'
Vuex 4 的 import:
javascript
import { createStore } from 'vuex'
import { mapState, mapMutations } from 'vuex'
import { useStore } from 'vuex'
7. 检查控制台中的 Vue DevTools
Pinia 在 DevTools 中的显示:
Vue DevTools
├── Pinia
│ ├── counter
│ ├── user
│ └── cart
Vuex 4 在 DevTools 中的显示:
Vue DevTools
├── Vuex
│ ├── State
│ ├── Mutations
│ └── Actions
8. 运行时的特征检查
创建一个简单的测试脚本:
javascript
// 在浏览器控制台中运行
if (window.__VUE_DEVTOOLS_GLOBAL_HOOK__) {
const devtools = window.__VUE_DEVTOOLS_GLOBAL_HOOK__
// 检查 Vuex
if (devtools.store) {
console.log('检测到 Vuex 版本:', devtools.Vue?.version)
}
// 检查 Pinia(通过 $pinia 属性)
const app = Object.values(devtools.apps)[0]
if (app?.appContext?.config?.globalProperties?.$pinia) {
console.log('检测到 Pinia')
}
}
9. 通过项目创建时间判断
-
Vue 2 项目:通常使用 Vuex 3 或 4
-
Vue 3 项目(2022年前):可能使用 Vuex 4
-
Vue 3 项目(2022年后):更可能使用 Pinia(官方推荐)
10. 查看配置文件中的线索
Vite 配置(vite.config.js):
javascript
// Pinia 项目可能会有热更新插件
import { createPinia } from 'pinia'
import piniaPluginPersistedstate from 'pinia-plugin-persistedstate'
Webpack 配置:
检查是否有相关插件的配置。
总结表格
| 特征 | Pinia | Vuex 4 |
|---|---|---|
| 包名 | pinia |
vuex |
| 创建函数 | createPinia() |
createStore() |
| Store 定义 | defineStore() |
直接配置对象 |
| 状态修改 | 直接修改或 actions | 必须通过 mutations |
| 目录名 | 通常 stores |
通常 store |
| 模块化 | 扁平化,多个 store | 嵌套 modules |
| TypeScript | 完整类型推断 | 需要额外类型定义 |
| Composition API | 原生支持 | 通过 useStore() |
快速检查脚本
创建一个 check-state-management.js 文件:
const fs = require('fs')
const path = require('path')
function checkProject() {
const packagePath = path.join(process.cwd(), 'package.json')
if (!fs.existsSync(packagePath)) {
console.log('未找到 package.json')
return
}
const pkg = JSON.parse(fs.readFileSync(packagePath, 'utf8'))
const deps = { ...pkg.dependencies, ...pkg.devDependencies }
console.log('=== 依赖检查 ===')
if (deps.pinia) {
console.log('✅ 使用 Pinia:', deps.pinia)
} else {
console.log('❌ 未使用 Pinia')
}
if (deps.vuex) {
console.log('✅ 使用 Vuex:', deps.vuex)
} else {
console.log('❌ 未使用 Vuex')
}
console.log('\n=== 目录检查 ===')
const storesDir = path.join(process.cwd(), 'src', 'stores')
const storeDir = path.join(process.cwd(), 'src', 'store')
if (fs.existsSync(storesDir)) {
console.log('✅ 找到 stores 目录 (Pinia 风格)')
}
if (fs.existsSync(storeDir)) {
console.log('✅ 找到 store 目录 (Vuex 风格)')
}
console.log('\n=== 建议 ===')
if (deps.pinia && deps.vuex) {
console.log('⚠️ 项目同时安装了 Pinia 和 Vuex,可能正在迁移')
} else if (deps.pinia) {
console.log('📌 项目使用的是 Pinia(Vue 3 推荐方案)')
} else if (deps.vuex) {
console.log('📌 项目使用的是 Vuex')
} else {
console.log('📌 项目未使用状态管理库')
}
}
checkProject()
通过以上方法,你可以快速准确地判断项目使用的是 Pinia 还是 Vuex 4。
Pinia 和 Vuex 4 可以共存吗?
是的,Pinia 和 Vuex 4 可以在同一个项目中同时存在和运行。但这需要谨慎处理,通常只在迁移过程中使用。
1. 为什么要共存?
-
渐进式迁移:从 Vuex 迁移到 Pinia
-
遗留代码兼容:不能立即重构所有代码
-
团队过渡期:部分团队成员还在学习 Pinia
-
第三方依赖:某些第三方库可能依赖 Vuex
2. 如何实现共存?
安装两个库:
bash
npm install vuex@4 pinia
# 或
yarn add vuex@4 pinia
配置共存:
// main.ts
import { createApp } from 'vue'
import { createPinia } from 'pinia'
import { createStore } from 'vuex'
import App from './App.vue'
// 1. 创建 Vuex store(可选)
const vuexStore = createStore({
state: () => ({
legacyData: '来自 Vuex 的数据'
}),
mutations: {
updateLegacy(state, payload) {
state.legacyData = payload
}
}
})
// 2. 创建 Pinia
const pinia = createPinia()
// 3. 创建 Vue 应用
const app = createApp(App)
// 4. 同时使用两个插件
app.use(vuexStore) // Vuex 4 的 use 方法
app.use(pinia) // Pinia
app.mount('#app')
3. 项目结构建议
src/
├── stores/ # Pinia stores
│ ├── user.ts
│ ├── cart.ts
│ └── index.ts
├── store/ # Vuex store(遗留)
│ ├── index.ts
│ ├── modules/
│ │ ├── legacyUser.ts
│ │ └── legacyCart.ts
│ └── mutation-types.ts
└── shared/
└── adapters/ # 适配器层
├── vuex-to-pinia.ts
└── pinia-to-vuex.ts
4. 在组件中使用
同时使用两个 store:
vue
<!-- LegacyComponent.vue -->
<script setup>
// 同时导入两种 store
import { useStore } from 'vuex' // Vuex
import { useUserStore } from '@/stores/user' // Pinia
const vuexStore = useStore()
const piniaUserStore = useUserStore()
// 访问 Vuex
const legacyData = computed(() => vuexStore.state.legacyData)
// 访问 Pinia
const userName = computed(() => piniaUserStore.name)
// 修改 Vuex(通过 mutation)
const updateVuex = () => {
vuexStore.commit('updateLegacy', '新数据')
}
// 修改 Pinia(直接或通过 action)
const updatePinia = () => {
piniaUserStore.updateName('新名字')
}
</script>
5. 数据同步策略
方案一:单向同步(推荐)
// adapters/vuex-to-pinia.ts
import { watch } from 'vue'
import { useStore } from 'vuex'
import { useUserStore } from '@/stores/user'
export function syncUserFromVuexToPinia() {
const vuexStore = useStore()
const piniaUserStore = useUserStore()
// 监听 Vuex 变化,同步到 Pinia
watch(
() => vuexStore.state.user,
(newUser) => {
if (newUser) {
piniaUserStore.$patch({
user: newUser,
isLoggedIn: true
})
}
},
{ immediate: true }
)
}
方案二:双向同步(谨慎使用)
// adapters/bidirectional-sync.ts
import { watch } from 'vue'
import { useStore } from 'vuex'
import { useCartStore } from '@/stores/cart'
export function setupCartSync() {
const vuexStore = useStore()
const piniaCartStore = useCartStore()
// Vuex -> Pinia
watch(
() => vuexStore.state.cart,
(newCart) => {
piniaCartStore.$patch({ items: newCart.items })
},
{ deep: true }
)
// Pinia -> Vuex
watch(
() => piniaCartStore.items,
(newItems) => {
vuexStore.commit('SET_CART_ITEMS', newItems)
},
{ deep: true }
)
}
6. 创建适配器/桥梁
封装统一的接口:
// stores/unified-store.ts
import { useStore } from 'vuex'
import { useCounterStore } from './counter'
class UnifiedStore {
private vuexStore: any
private piniaCounterStore: any
constructor() {
this.vuexStore = useStore()
this.piniaCounterStore = useCounterStore()
}
// 统一获取用户信息
getUser() {
// 优先从 Pinia 获取
if (this.piniaCounterStore.user) {
return this.piniaCounterStore.user
}
// 回退到 Vuex
return this.vuexStore.state.user
}
// 统一登录方法
async login(credentials: any) {
// 使用 Pinia(新系统)
await this.piniaCounterStore.login(credentials)
// 同步到 Vuex(兼容旧组件)
this.vuexStore.commit('SET_USER', this.piniaCounterStore.user)
}
// 统一登出
logout() {
this.piniaCounterStore.logout()
this.vuexStore.commit('CLEAR_USER')
}
}
export function useUnifiedStore() {
return new UnifiedStore()
}
7. 迁移策略
分阶段迁移计划:
阶段 1:安装和配置
// 安装 Pinia,保持 Vuex 运行
// 创建第一个 Pinia store(如用户 store)
阶段 2:并行运行(当前状态)
// 新功能使用 Pinia
// 旧功能继续使用 Vuex
// 通过适配器同步关键数据
阶段 3:逐步替换
// 按模块迁移
// 1. 迁移用户模块
// 2. 迁移购物车模块
// 3. 迁移产品模块
阶段 4:清理
// 删除 Vuex 相关代码
// 移除适配器
// 卸载 vuex 依赖
迁移工具示例:
// migration-tools/vuex-to-pinia-converter.ts
export function convertVuexModuleToPinia(vuexModule: any) {
const piniaStore = {
state: () => ({ ...vuexModule.state() }),
getters: {},
actions: {}
}
// 转换 getters
Object.keys(vuexModule.getters || {}).forEach(key => {
piniaStore.getters[key] = vuexModule.getters[key]
})
// 转换 mutations 为 actions
Object.keys(vuexModule.mutations || {}).forEach(key => {
piniaStore.actions[key] = function(payload: any) {
vuexModule.mutations[key](this, payload)
}
})
// 转换 actions
Object.keys(vuexModule.actions || {}).forEach(key => {
piniaStore.actions[key] = async function(payload: any) {
return await vuexModule.actions[key](
{ commit: (type: string, payload: any) => {
vuexModule.mutations[type](this, payload)
}},
payload
)
}
})
return piniaStore
}
8. 注意事项和警告
⚠️ 潜在问题:
-
状态不一致:两个 store 可能不同步
-
依赖冲突:组件可能意外访问错误的 store
-
调试困难:需要同时查看两个状态树
-
包体积增大:两个库都会被打包
-
学习曲线:团队成员需要了解两种 API
✅ 最佳实践:
-
明确边界:
typescript
// 在组件中明确注释 // TODO: 迁移到 Pinia(计划在 Q3 完成) // LEGACY: Vuex 代码,不要修改 -
单向数据流:
typescript
// 只允许 Pinia -> Vuex 或 Vuex -> Pinia // 避免双向同步导致的循环更新 -
逐步迁移:
typescript
// 按功能模块迁移,而不是按文件 // 完成一个模块,测试一个模块 -
监控和测试:
typescript
// 添加监控日志 // 编写迁移测试用例
9. 性能考虑
typescript
// 避免频繁的同步操作
export function useOptimizedSync() {
const vuexStore = useStore()
const piniaStore = useCartStore()
// 使用防抖
const debouncedSync = useDebounceFn((items) => {
vuexStore.commit('SET_CART_ITEMS', items)
}, 300)
// 只在必要时同步
watch(
() => piniaStore.items,
(newItems) => {
if (shouldSync(newItems)) {
debouncedSync(newItems)
}
},
{ deep: true }
)
}
10. 何时不应该共存?
避免共存的情况:
-
小型项目:直接完全迁移
-
新项目:直接使用 Pinia
-
没有维护的旧代码:考虑重写而不是迁移
-
严格性能要求:额外的同步可能影响性能
总结
可以共存,但需谨慎:
| 方案 | 适用场景 | 复杂度 |
|---|---|---|
| 完全隔离 | 新旧系统完全独立 | 低 |
| 单向同步 | 从旧到新迁移 | 中 |
| 双向同步 | 需要实时交互 | 高 |
| 统一接口 | 长期并行运行 | 中-高 |
推荐做法:
-
制定明确的迁移计划和时间表
-
创建清晰的代码边界和文档
-
优先迁移高频使用的模块
-
保留回滚方案,直到完全迁移完成
-
及时清理,避免技术债务积累
最终目标:完全迁移到 Pinia,移除 Vuex 依赖。共存只是过渡方案,不应作为长期架构。