在 CRM 项目中,Axios(请求)、Pinia(状态)、Mitt(事件)、qs(参数处理)并非互斥,而是各司其职、协同工作的核心工具。下面按「核心原则→场景化用法→完整流程示例」讲解,让你在实际开发中既能避免冗余,又能保证代码规范。一、核心使用原则(先记牢,再落地)表格
工具 | 核心职责 | 绝对不要做的事 | 最佳实践 |
Pinia | 存储全局响应式数据(持久 / 共享) | 用 Pinia 存储 “一次性动作通知”(如刷新指令) | 按业务模块拆分 Store,Action 封装异步逻辑 |
Axios | 发起 HTTP 请求(对接后端接口) | 让 Axios 存储全局数据(如用户 token) | 封装拦截器,统一处理 token / 错误 / 加载状态 |
Mitt | 传递一次性事件通知(无数据存储) | 用 Mitt 传递需要持久化的数据(如用户信息) | 仅用于无父子关系的组件 / 页面联动 |
qs | 兼容特殊参数格式(数组 / 嵌套对象) | 所有请求都用 qs 处理(简单参数也用) | 仅后端不兼容 Axios 原生格式时使用 |
一句话总结:Pinia 管 “存数据”,Axios 管 “拿数据”,Mitt 管 “传消息”,qs 管 “调格式”—— 各司其职,不越界。二、场景化合理用法(CRM 高频场景)场景 1:用户登录(四大工具协同核心流程)这是 CRM 最基础的核心流程,完美体现四个工具的分工:豆包你的 AI 助手,助力每日工作学习代码落地(关键片段)typescript运行场景 2:订单列表→详情(CRM 核心业务)流程说明列表页点击订单 → Pinia 存储 “当前选中订单 ID”(持久,刷新不丢);跳转到详情页 → 详情页从 Pinia 取订单 ID,调用 Axios 加载详情;详情页修改订单 → Axios 发修改请求,成功后:Pinia 更新订单数据(保证全局数据一致);Mitt 触发 “订单修改成功” 事件(通知列表页刷新);若订单参数含数组(如多个标签)→ 后端不兼容时用 qs 调整格式。代码落地(关键片段)typescript运行场景 3:全局加载状态(Pinia + Axios 配合)CRM 中常需要全局加载状态(如按钮加载、页面加载),用 Pinia 存储,Axios 拦截器自动更新:typescript运行三、避坑指南(新手最容易错的点)1. 不要用 Mitt 传递需要持久化的数据❌ 错误:用 Mitt 传递用户信息、订单数据(页面刷新就丢);✅ 正确:数据存在 Pinia(持久化),Mitt 只传 “XX 数据更新了” 的通知。2. 不要让 Axios 存储全局状态❌ 错误:把 token 存在 Axios 实例的 headers 里(刷新丢失);✅ 正确:token 存在 Pinia(持久化),Axios 拦截器从 Pinia 取 token。3. 不要所有请求都用 qs❌ 错误:简单参数(如 page=1)也用 qs 处理;✅ 正确:仅数组 / 嵌套对象 + 后端不兼容时用 qs,简单参数用 Axios 原生。4. 不要用 Pinia 替代 Mitt❌ 错误:为了 “通知刷新”,在 Pinia 加 refreshFlag 状态,组件监听 flag 变化;✅ 正确:刷新是 “一次性动作”,用 Mitt 触发,Pinia 只存数据不存 “动作指令”。四、完整流程示例(CRM 新增客户)豆包你的 AI 助手,助力每日工作学习五、总结(核心关键点)分工明确:数据存储(用户、订单、配置)→ Pinia(持久化、响应式);接口请求(查 / 增 / 改 / 删)→ Axios(封装拦截器,统一处理);动作通知(刷新、高亮、关闭弹窗)→ Mitt(无父子关系的组件联动);参数兼容(数组 / 嵌套对象)→ qs(仅后端不兼容时用)。流程规范:组件只做 “调用 Store + 渲染 UI”,不直接写请求 / 复杂逻辑;Store 的 Action 封装 “请求 + 数据更新”,是业务逻辑的核心;跨组件联动优先用 Mitt,避免 Pinia 状态冗余。极简原则:能不用 qs 就不用(Axios 原生足够);能不用 Mitt 就不用(父子组件用 props/emit);所有工具都为业务服务,不炫技、不冗余。按这个思路开发,你的 CRM 项目会既规范又易维护 —— 数据存在该存的地方,消息传到该传的地方,请求封装在该封装的地方,不会出现 “工具混用、逻辑混乱” 的问题。pinia相当于一个环境,用户定义全局的变量,同时可以定义更改全局变量的方法或者函数,页面需要的时候进行调用就可以了你的理解非常接近核心!可以说 Pinia 就是一个「全局响应式数据的管理环境」,但要补充几个关键细节,让你的理解更精准(尤其是 CRM 开发中能避开坑)。先肯定你的核心理解(没错!)你说的:Pinia 相当于一个环境,用户定义全局的变量,同时可以定义更改全局变量的方法或者函数,页面需要的时候进行调用就可以了这是 Pinia 最通俗、最核心的概括 ——“全局变量” → 对应 Pinia 的 state(状态):比如 CRM 中的 token、userInfo、activeOrderId 这些需要多组件共享的数据;“更改全局变量的方法 / 函数” → 对应 Pinia 的 actions:比如 login()(修改 token)、setActiveOrderId()(修改选中的订单 ID)、fetchOrderList()(修改订单列表);“页面需要时调用” → 组件通过 useXxxStore() 引入 Store,直接读取状态、调用方法,无需关心数据存在哪、怎么改。补充 3 个关键细节(CRM 开发必知)你的理解已经到位,这 3 个细节能让你用得更规范、少踩坑:1. “全局变量” 是「响应式」的(核心区别于普通全局变量)Pinia 的 “全局变量” 不是 window.xxx 这种普通全局变量,而是响应式的 ——普通全局变量:修改后,组件不会自动更新(比如你改了 window.token,页面上显示 token 的地方不会变);Pinia 状态:修改后,所有用到这个状态的组件会自动刷新 UI(比如 CRM 头部组件显示的用户名,登录后会立刻更新)。CRM 示例对比:typescript运行2. “更改方法” 支持「异步逻辑」(CRM 核心需求)你说的 “更改全局变量的方法”,不仅能做「同步修改」(比如 userStore.token = 'new-token'),还能封装「异步逻辑」(比如调接口、处理延迟)—— 这是 Pinia 最核心的价值之一(也是区别于普通全局变量的关键)。CRM 示例(异步方法):typescript运行
Plain Text复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// Pinia 的 Action 可以封装异步逻辑(调接口+改状态)
export const useOrderStore = defineStore('order', () => {
const orderList = ref([]); // 全局变量(订单列表)
// 更改方法:异步调接口,再修改全局变量
const fetchOrderList = async () => {
// 1. 调 Axios 接口(异步)
const res = await orderApi.getOrderList();
// 2. 修改全局变量
orderList.value = res.list;
};
return { orderList, fetchOrderList };
});
// 组件中调用(一行代码搞定“调接口+更数据+更UI”)
const orderStore = useOrderStore();
orderStore.fetchOrderList();
如果用普通全局变量,你需要自己写 “调接口→改变量→手动刷新组件”,而 Pinia 把这些逻辑封装在 Action 里,组件只需调用方法,无需关心内部逻辑。3. “全局” 可按模块拆分(不是 “一锅乱炖”)Pinia 的 “全局环境” 不是把所有变量都堆在一个地方,而是按业务模块拆分(CRM 开发必做)——比如 CRM 中:user.ts:只存用户相关的全局变量(token、userInfo)和方法(login、logout);order.ts:只存订单相关的全局变量(orderList、activeOrderId)和方法(fetchOrderList、updateOrder);config.ts:只存全局配置(theme、language)和方法(setTheme)。这样做的好处:CRM 业务复杂,拆分后代码不混乱,后期维护(比如改订单逻辑)不会影响用户逻辑。用一句话总结 Pinia(结合你的理解 + 补充细节)Pinia 是一个「按模块拆分的、响应式的全局数据管理环境」:你可以在不同模块里定义需要共享的全局变量(状态),以及封装了同步 / 异步逻辑的修改方法(Action);组件需要时,只需引入对应模块,直接读取变量、调用方法,变量修改后所有用到的组件会自动更新 UI。最后举个 CRM 极简示例(验证你的理解)typescript运行
Plain Text复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
// 1. 定义 Pinia 环境(用户模块)
export const useUserStore = defineStore('user', () => {
// 全局变量(状态)
const username = ref('');
// 更改全局变量的方法
const setUsername = (name) => {
username.value = name; // 同步修改
};
// 带异步逻辑的更改方法
const login = async (form) => {
const res = await userApi.login(form); // 异步调接口
username.value = res.username; // 修改全局变量
};
return { username, setUsername, login };
});
// 2. 组件中调用(页面使用)
const userStore = useUserStore();
// 读取全局变量
console.log(userStore.username);
// 调用同步方法修改
userStore.setUsername('张三');
// 调用异步方法修改
await userStore.login({ username: '张三', password: '123' });
你的理解完全抓住了 Pinia 的核心,补充这 3 个细节后,在 CRM 开发中使用 Pinia 会更规范、更贴合实际业务~