在橙星云技术团队维护的 B 端心理云平台中,随着业务模块逐渐增多,前端代码量不断增加。在这个 Vue 3 单页应用里,我们需要在顶部导航、左侧菜单、个人中心和消息通知等多个跨路由组件之间,实时共享管理员的“未读预警消息数”。
以往面对这种跨组件状态共享,引入 Vuex 通常是标准做法。但在最近的架构梳理中,我们决定将 Vuex 从项目依赖中移除,转向更为轻量的 Pinia。本文将从代码维护成本和类型安全的角度,分享这次技术选型的考量。
繁琐的 Mutation 与类型推导瓶颈
Vuex 的设计在早年间解决了很多问题,但随着 Vue 3 的普及,它的一些机制显得不够简洁。例如,要更新一个未读消息数,在 Vuex 中需要经历一套比较繁杂的流程:定义 state,编写 mutation 同步修改,如果有异步请求还要再封装一层 action。在组件层面,通常还要使用字符串形式的 dispatch('fetchUnreadCount') 来触发状态更新。
这种代码结构带来了额外的开发成本。更重要的是,基于字符串的派发方式让 TypeScript 的类型推导在这一层难以发挥作用,很容易产生类型丢失或者 any 类型泛滥的问题。
契合组合式 API 的 Pinia
相比之下,Pinia 的设计更加贴合 Vue 3 的 Composition API。它去除了 Mutation 的概念,让状态管理变得非常扁平化。
“`javascript
import { defineStore } from ‘pinia’;
import { ref } from ‘vue’;
// 直观的 Store 定义
export const useMessageStore = defineStore(‘message’, () => {
const unreadCount = ref(0);
// 使用普通 async 函数处理异步与状态更新
async function fetchUnreadCount() {
const res = await api.get(‘/messages/count’);
unreadCount.value = res.data;
}
return { unreadCount, fetchUnreadCount };
});
“`
上述代码避免了冗长的模板代码。在组件中调用时,体验也更加直接:
“`javascript
import { useMessageStore } from ‘@/stores/message’;
const messageStore = useMessageStore();
messageStore.fetchUnreadCount();
console.log(messageStore.unreadCount);
“`
完善的 TypeScript 支持
改用 Pinia 的另一个重要原因在于其内置了优秀的 TypeScript 支持。由于放弃了字符串 dispatch,所有的 Store 方法都变成了标准的 JavaScript 函数调用。这使得 TS 能够在业务代码中提供精准的代码补全和严格的类型校验。如果调用时出现拼写错误或参数不匹配,编译器会在开发阶段直接抛出错误。
在复杂的 B 端 SaaS 前端工程中,减少样板代码并引入强类型约束是提升工程质量的有效手段。淘汰 Vuex 的繁琐仪式并拥抱 Pinia 的组合式状态机,不仅降低了开发者的心智负担,也为我们的响应式数据流提供了更稳固的底层保障。
