在 setup 中结合 Pinia 实现跨组件通信,关键在于通过 useXXXStore() 获取 store 实例,再使用 storeToRefs 解构 state 以保持响应式,调用 actions 更新状态,并借助 computed 或 getters 读取派生数据,从而避免因直接解构导致响应式失效的问题。

在 setup 中结合 Pinia 进行跨组件数据共享,核心思路是让各个组件通过 Store 实例统一读取和修改共享状态,同时确保数据响应式始终生效。相比依赖 props 逐层传递或事件链通信,这种方式可以直接连接集中管理的 state、actions 和 getters,更适合 Vue3 中大型项目的状态管理与组件协作。
在 setup 中使用 store 的标准写法
推荐采用组合式 API 风格来调用 Store,并配合 storeToRefs 确保响应式数据不会丢失:
- 调用
useXXXStore()获取 store 实例,例如useUserStore() - 使用
storeToRefs()解构 state 中的响应式属性,避免解构后失去响应能力 - 直接调用 store 上定义的 actions 方法,完成状态更新与业务操作
- 通过
computed或 getters 获取派生状态,例如store.totalCount
示例:
import { defineComponent, computed } from 'vue'
import { useCartStore } from '@/stores/cart'
import { storeToRefs } from 'pinia'
export default defineComponent({
setup() {
const cartStore = useCartStore()
const { items } = storeToRefs(cartStore) // 保持响应式
const totalPrice = computed(() => cartStore.totalPrice) // 使用 getter
const addToCart = (item) => cartStore.addItem(item)
return { items, totalPrice, addToCart }
}
})
避免常见响应式丢失问题
如果直接解构 store.state 中的属性,响应式引用会被打断,因此应使用 storeToRefs 或 toRefs 进行包装:
- ❌ 错误写法:
const { items } = useCartStore()→items会变成普通对象,数据更新后无法触发视图刷新 - ✅ 正确写法:
const { items } = storeToRefs(useCartStore())→ 可以保留 ref 的响应式特性 - 也可以写成
const items = computed(() => cartStore.items),但通常性能稍弱一些,因为每次访问都会经过 getter
多个 store 协同与逻辑复用
在实际开发中,一个组件往往需要接入多个 store,可以按业务需求分别引入并统一组织:
- 分别调用不同的 store,例如
useUserStore()与useThemeStore() - 在 actions 中实现跨 store 调用,但要注意避免循环依赖问题,例如登录成功后,
userStore.login()内部再调用themeStore.setByRole(user.role) - 将公共业务逻辑抽离成独立函数,并把 store 实例作为参数传入,有助于提升复用性与可测试性
配合 watch 和 $subscribe 做副作用响应
当你需要在 store 状态变化时执行副作用操作,例如记录日志、发送请求或进行路由跳转,通常推荐以下两种方式:
- 使用
watch监听具体 ref,例如watch(() => userStore.token, handleAuthChange) - 使用
store.$subscribe监听全部 state 变化,并获取 patch 相关信息,例如cartStore.$subscribe((mutation) => { console.log(mutation.type) }) - 注意:
$subscribe在组件卸载时通常需要手动执行unsubscribe,或者结合onBeforeUnmount做清理
