刚结束一场“Pinia浓度超标”的前端面试,现在坐在咖啡厅手还在抖——原来用了一年多的Pinia,在面试官眼里我竟然是个“只会用storeToRefs的萌新”。记录下这场“知识漏洞大普查”,给各位同段位的朋友提个醒。
Round 1:你的Pinia是全局变量吗?
面试官(翻着我的GitHub仓库):“看你项目里用Pinia做了权限管理,说说和直接用全局变量的区别?”
我(表面镇定):“呃...Pinia是响应式的?还能支持TypeScript...”
面试官(死亡追问):“如果我在localStorage里存状态,和Pinia有啥本质区别?为什么非要用它?”
这一问直接戳中我的知识盲区。支支吾吾答了“方便调试、有DevTools”,结果面试官甩出一个暴论:
“很多新人把Pinia当加强版Vuex用,但它的组合式API集成能力才是核心价值。”接着让我手写一个不用Pinia的响应式状态共享方案,我憋出了个用reactive+provide/inject的残血版,他说“看,这就是Pinia帮你封装掉的东西”。
Round 2:TypeScript的痛击
面试官(打开我写的store):“你这个UserStore里为什么用any?”
interface UserState {
list: any[] // 被重点标记的any
}
我(心虚找补):“后端接口字段经常变,用类型太麻烦...”
面试官(冷笑):“知道Partial和Omit吗?试试这个——”
随手改成了:
type SafeUser = Partial<User> & { id: string }
然后补刀:“你们项目没配置接口类型生成工具?建议试试openapi-typescript。” 我默默记下这个救命关键词。
Round 3:致命异步陷阱
白板题:“用Pinia实现购物车商品批量删除,要求防重复提交和错误处理。”
我自信写下:
actions: {
async batchDelete(ids: string[]) {
try {
this.loading = true
await api.batchDelete(ids)
this.list = this.list.filter(item => !ids.includes(item.id))
} catch (err) {
console.error(err)
} finally {
this.loading = false
}
}
}
面试官(邪魅一笑):“如果连续点击两次,第一次请求比第二次慢返回,会发生什么?”
我愣住三秒,突然想起经典的竞态条件问题。最后在他的提示下,用AbortController给请求加了取消功能:
const controller = new AbortController()
await api.batchDelete(ids, { signal: controller.signal })
// 在onUnmounted里调用
controller.abort()
Round 4:来自灵魂的终极拷问
面试官(合上电脑):“最后一个问题——如果让你删掉Pinia的一个功能,你选什么?”
我(大脑宕机):“可、可能是$patch?感觉直接赋值也行...”
他摇头大笑:“我会删store.$reset(),真正的状态管理应该自己控制初始化逻辑!”
菜鸟の复盘笔记
- Pinia不是银弹:过度依赖store会导致组件抽象层次混乱
- TS是照妖镜:项目里的
any迟早变成技术债 - 异步不只是async/await:竞态处理、取消机制才是实战关键
- 插件生态:没用到
persistedState和devtools插件是我的减分项
今日最大收获:面试官推荐的《Pinia设计模式精要》电子书,以及他临走时说的那句——“工具库的API文档要看,但背后的设计思想更要琢磨。”
新人互助区:
🆘 你在用Pinia时踩过最大的坑是什么?
🆘 求分享靠谱的Pinia+TS实战教程!
🆘 面试被问“为什么选Pinia不选Vuex”,怎么回答才不显得背答案?
(此刻的我在咖啡厅疯狂搜索如何优雅地给Pinia写单元测试...)