今日面试实录:当Pinia成为我的面试修罗场

301 阅读3分钟

程序员面试

刚结束一场“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  
}  

(心虚找补):“后端接口字段经常变,用类型太麻烦...”
面试官(冷笑):“知道PartialOmit吗?试试这个——”
随手改成了:

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(),真正的状态管理应该自己控制初始化逻辑!”


菜鸟の复盘笔记

  1. Pinia不是银弹:过度依赖store会导致组件抽象层次混乱
  2. TS是照妖镜:项目里的any迟早变成技术债
  3. 异步不只是async/await:竞态处理、取消机制才是实战关键
  4. 插件生态:没用到persistedStatedevtools插件是我的减分项

今日最大收获:面试官推荐的《Pinia设计模式精要》电子书,以及他临走时说的那句——“工具库的API文档要看,但背后的设计思想更要琢磨。”


新人互助区
🆘 你在用Pinia时踩过最大的坑是什么?
🆘 求分享靠谱的Pinia+TS实战教程!
🆘 面试被问“为什么选Pinia不选Vuex”,怎么回答才不显得背答案?

(此刻的我在咖啡厅疯狂搜索如何优雅地给Pinia写单元测试...)