最近一段时间作为面试官面前端,我有个感受越来越强:
很多问题,已经不能再按以前的方式问了。
不是说候选人都不行,也不是说 AI 把面试彻底毁了。
更准确一点说,是以前那套靠标准答案筛人的方法,现在越来越不管用了。
前段时间面了一个三年经验的,简历上写着熟悉 React。
我问他:
useEffect的清理函数什么时候执行?
对面答得很顺:
组件卸载时会执行,下一次 effect 执行前也会执行,通常用来清理副作用,避免内存泄漏。
这个答案没问题,甚至挺标准,所以我再接着问:
如果在
useEffect里发了一个请求,组件很快卸载了,这个请求会怎么样?
他回答:
请求会被取消。
我继续追问
fetch 会自动取消吗?
他停了一下,说:
哦对,我的意思是用 AbortController 的话可以取消。
但我其实没问 AbortController。
我问的是那个请求本身会怎样。
如果你真写过这种场景,第一反应通常不是“请求会取消”,而是:
请求还在跑,只是响应回来以后,这个组件可能已经没了。
如果你还 setState,就可能有问题。
如果要取消,需要自己处理,比如 AbortController,或者用请求序号避免旧结果覆盖新结果。
这不是什么特别高级的知识点。
但区别就在这里。
一个人背过答案,和一个人真的在项目里被这个坑烦过,说出来的东西是不一样的。
很多回答不是错,是太干净了
我现在面试时最怕听到的,不是答错。
答错反而好处理,知道哪里不行就完了。
比较麻烦的是那种特别完整、特别顺、特别“正确”的答案。
比如问性能优化。
有些候选人会从代码分割讲到懒加载,从 CDN 讲到缓存,从防抖节流讲到虚拟列表,再讲到 Tree Shaking、SSR、Web Worker。
你不能说他说错。
基本都对。
但听多了以后,会觉得这个答案很像一份整理好的笔记。
里面没有画面感,没有踩过的坑,也没有"我当时为啥这么干"。
真正做过的人,有时候反而讲得没那么完整。
他可能会说:
我们之前有个后台页面,列表一次返回很多数据,客户那边机器又比较老,滚动特别卡。一开始我们以为是接口慢,后来用 Performance 看了一下,主要时间耗在渲染上。最后没搞太复杂,先分页,再把几个计算字段缓存掉,效果就已经明显了。
这种回答不一定高级,但我会觉得可信。
因为它不像是在背“前端性能优化有哪些方法”,而是在讲一个具体问题怎么被定位、怎么被解决。
而且真实项目里很少有人上来就把所有优化手段都铺开。
大多数时候都是先看痛点的地方在哪里,然后用一个够用的办法把它压下去。
我现在会更在意追问
以前我也问八股。
闭包是什么?
事件循环怎么走?
React diff 大概怎么做?
Vue2 和 Vue3 的响应式有什么区别?
这些问题不是完全没价值。
如果一个人完全答不上来,当然也说明问题。
但现在的问题是,这些题太适合被准备,也太适合被 AI 回答了。
远程面试的时候,对面到底有没有开着 AI,我其实不知道。
就算是现场,也很难百分百防住。
现在手机录音、语音转文字、AI 总结,都太方便了。
所以我后来不太纠结“他是不是用了 AI”。
我更关心另一件事:
这个答案往下追,还能不能站得住。
比如候选人说:
我们做过首屏优化。
我不会停在这里。
我会继续问:
优化前首屏大概多慢?
你们怎么量的?
是 Lighthouse,还是自己打点?
最后主要问题在 JS 体积、接口、渲染,还是资源加载?
你说做了代码分割,具体拆了哪些?
拆完以后有没有请求数变多的问题?
这些问题单独看都不难。
但连在一起,就会变成一个真实经历的链条。
如果确实是亲手做的,他可能有些数字记不清,但大体能讲出过程。
如果只是背过"首屏优化方案",很快就会开始说套话:
一般来说,首屏优化可以从资源加载、缓存策略、代码拆分几个方面入手……
一听到"一般来说",我就觉得悬了。
不是说这句话一定有问题,而是很多空话都是从这里开始的。
真经历里通常有麻烦
我现在越来越喜欢问一些“不漂亮”的问题。
比如:
你做过最失败的一次组件封装是什么?
这个问题比“你怎么理解组件封装”有意思得多。
因为“组件封装”这个词太好回答了。
职责单一、可复用、可维护、避免过度设计。
这些话谁都会说。
但如果问失败经历,情况就不一样了。
真做过的人可能会说:
我之前封过一个表格组件,一开始想做得通用一点,列配置、搜索项、按钮、权限、分页、批量操作都塞进去。刚开始挺爽,几个页面很快就接上了。后来业务差异越来越多,props 越加越多,最后谁都不敢改。现在回头看,当时不应该把业务逻辑也塞进去,最多抽视觉结构和一些稳定交互。
这比“组件要保持职责单一”有信息量多了。
因为它讲到了后果。
技术判断很多时候不在“知不知道最佳实践”,而在“知道这个最佳实践什么时候会有问题,会引起复杂性问题”。
类似的问题还有:
你印象最深的一次线上事故是什么?
你有没有做过一个后来自己都不想维护的设计?
哪个技术选型你现在回头看觉得选错了?
哪次优化最后证明没什么效果?
这些问题没那么体面。
但工作本来就不是一直体面的。
如果一个人所有项目都顺风顺水,所有方案都合理,所有优化都有明显收益,所有事故都跟他无关,那我反而不知道该怎么判断。
代码 review 比背概念好用
我现在会准备一些小代码,让候选人看。
比如这种:
1 2 3 4 5 6 7 8 9 10 11
function UserList({ keyword }) {
const [list, setList] = useState([]);
useEffect(() => {
fetch('/api/users?keyword=' + keyword)
.then(res => res.json())
.then(data => setList(data));
}, []);
return list.map(item =><div key={item.id}>{item.name}</div>);
}
这段代码不复杂,但可以聊很多东西。
最基础的,依赖数组少了 keyword。
再往下,keyword 要不要 encodeURIComponent。
请求有没有竞态问题。
组件卸载以后怎么办。
有没有 loading 和 error。
空状态怎么展示。
接口慢的时候用户看到什么。
连续输入的时候旧请求会不会覆盖新结果。key 靠不靠谱。
如果数据量大怎么办。
这些点 AI 当然也能列出来。
所以我不会只看他能列出多少问题。
我会看他怎么展开。
有人会像念 checklist 一样,把所有可能问题都说一遍。
也有人会先抓最关键的:
这个组件最大的问题是 keyword 变了不会重新请求。修完以后第二个问题是竞态,比如用户先搜 a 再搜 ab,a 的请求后回来,可能把 ab 的结果覆盖掉。这个场景在搜索框里挺常见,所以我会先处理这两个。
后者更像真实写代码的人。
因为他知道问题有优先级。
工程里很少是“把所有最佳实践都加上”。
更多时候是先判断哪个问题现在最可能发生,哪个问题发生了后果最严重。
我不太相信“最佳实践”这四个字了
不是说最佳实践没用。
而是面试里只讲最佳实践,信息量太低。
比如问状态管理。
以前我可能会问:
Redux 和 Zustand 有什么区别?
现在我更愿意问:
一个中后台系统,页面很多,筛选条件很多,权限也比较复杂。你会把哪些状态放 URL,哪些放全局 store,哪些留在组件里?
这个问题没有标准答案。
但能听出一个人干活的路子。
我比较期待的回答大概是:
筛选条件、分页、tab 这种需要分享、刷新、前进后退保留的状态,可以考虑放 URL。
用户信息、权限、全局配置,适合放全局 store。
弹窗开关、局部 hover、局部表单过程状态,没必要全局化。
服务端数据也不一定要塞进 store,可以交给 React Query 这类工具。
表单状态如果全局化,要小心同步问题,尤其是复杂表单。
他不一定要跟我选一样的方案。
但他得讲得出为什么。
我现在更想听到的是:
我这么选,是因为这个状态需要被谁消费,需要不会随刷新丢失,要不要支持分享,未来改起来会不会麻烦。
而不是:
Zustand 更轻量,Redux 生态更成熟。
这些对,但太像简介。
AI 不是唯一的问题
说实话,把锅全甩给 AI 也不公平。
以前没有 AI 的时候,也有人靠背题过面试。
背闭包、背原型链、背 React 生命周期、背 webpack 优化。
那时候的问题其实已经存在了:
很多面试考的是“你有没有准备过面试”,不是“你能不能把活干好”。
AI 只是把这件事放大了。
以前背题还需要自己整理,现在让 AI 生成一份“前端面试高频题答案”,几分钟就够了。
以前候选人临场编不出来,现在 AI 可以帮他把话说得很顺。
但从另一个角度看,这也逼面试官改问题。
如果一个问题 AI 五秒钟能答得比候选人还完整,那这个问题可能本来就不该作为核心判断依据。
比如:
Vue2 和 Vue3 有什么区别?
如果只听到 Proxy、Composition API、Fragment、Tree-shaking,这就很难判断水平。
我更愿意换成:
如果你手上有一个 Vue2 老项目,现在要迁 Vue3,你会先看哪些风险?
哪些代码最容易出问题?
你会一次迁完,还是渐进迁?为什么?
这个问题就没那么容易背。
因为它涉及项目规模、依赖、团队节奏、上线风险、兼容策略。
同样,“说说微前端”也很容易变成背概念。
不如问:
你们当时为什么需要微前端?
如果只是几个团队协作,有没有更轻的方案?
微前端引入之后,最烦的问题是什么?
真做过的人大概率不会只讲好处。
他会讲样式隔离、运行时隔离、路由、权限、部署、子应用通信,甚至会讲组织结构的问题。
有些人还会说:
后来发现我们的问题不一定要靠微前端解决,主要还是团队边界和发布流程没理清。
这种回答我反而更信。
远程面试防不住,也没必要装作防得住
远程面试能不能防 AI?
我觉得很难。
你可以要求共享屏幕,可以要求开摄像头,可以限制切屏。
但这些都只能提高成本,不能真正解决问题。
而且我也不太喜欢把面试搞得像监考。
大家都累。
所以我的做法比较简单:
不试图证明他有没有用 AI,而是让答案必须和他的经历绑定。
比如他简历写:
负责前端工程化建设。
那我不会问:
前端工程化包括哪些内容?
这个问题太容易答了。
我会问:
你们当时为什么要做这件事?
是构建慢,还是规范乱,还是发布容易出问题?
ESLint 规则是谁定的?
有没有规则推不下去?
有没有哪个包升级以后出过问题?
构建速度优化前后大概多少?
如果团队有人不愿意改,你们怎么处理?
这些不是为了为难人。
而是"自己干的"和"听别人说的",区别就在这些地方。
自己干过的人会讲麻烦。
听说过的人只会讲方向。
我现在更喜欢现场推演
有时候我会给一个很普通的需求:
做一个搜索框,用户输入关键词后请求接口。要求防抖,避免旧请求覆盖新结果,有 loading,有错误提示。
这题不难,但挺能看出习惯。
有人会直接写一个 debounce。
有人会想到竞态。
有人会想到 AbortController。
有人会说 loading 不能简单粗暴地设 true/false,因为并发请求时可能乱。
有人会区分主动取消和真实报错。
有人会问结果要不要缓存,搜索条件要不要进 URL,用户清空输入时要不要立即清空结果。
这些都没有唯一答案。
但聊天过程里能看出他有没有处理过类似交互。
我还会临时改需求:
如果现在产品说,返回结果要缓存一分钟呢?
如果用户点返回,希望搜索词还在呢?
如果接口偶尔很慢,loading 要不要延迟出现?
如果这个搜索框在多个页面都要用,你会抽成什么?
这比单独问"防抖和节流有啥区别"有用多了。
因为工作里的需求就是这样变的。
不是一道题答完就结束,而是你刚写完,旁边又来一个条件。
pair programming 其实最省事
如果条件允许,我觉得最有效的方式还是一起写点代码。
不用搞很难。
二三十分钟,修一个小 bug,或者实现一个小交互就够了。
让他打开编辑器、浏览器、DevTools。
看看他怎么读陌生代码,怎么定位问题,遇到报错怎么查,改完以后会不会验证。
很多东西在聊天里很难判断,但写代码时很明显。
比如:
他是不是一上来就大改。
会不会先复现问题。
会不会看控制台。
会不会加日志。
改完以后会不会考虑边界。
不知道的时候会不会承认不知道。
能不能把问题拆小。
这东西比背答案真实多了。
当然,这种方式对面试官要求也更高。
你得准备合适的题,不能拿一个脱离业务、毫无意义的玩具题糊弄人。
你还得愿意花时间看过程,而不是只看最后结果。
但如果真的想判断一个人能不能干活,这可能是最直接的办法。
我现在不太急着下判断
面多了以后,我反而没以前那么爱贴标签了。
候选人回答像 AI,不一定说明他作弊。
有些人就是准备得很充分,说话也比较模板化。
有些人紧张的时候,就开始背课文了。
还有些人嘴皮子利索,答案天然就很完整。
反过来,回答不流畅,也不一定说明不会。
有的人做得多,但不擅长总结。
有的人需要看到代码才能进入状态。
所以我现在更愿意多换几种方式看:
聊经历。
抠细节。
看代码。
做推演。
问取舍。
实在不行就一起写一小段。
如果这些环节都能对上,我就比较放心。
如果每个问题一开始都答得很好,但一追到项目细节就开始说套话,那我就会打个问号。
最后
AI 确实让面试变麻烦了。
但我现在觉得,它也只是把一个老问题暴露得更明显了:
我们以前太爱问那些有标准答案的问题了。
标准答案当然方便。
面试官省事,候选人也知道怎么准备。
但工作不是这么回事。
工作里更多的是:
这个需求到底要不要做?
这个 bug 先怀疑哪一层?
这个抽象现在该不该抽?
这个状态放哪里以后最不容易出事?
这个方案看起来很优雅,但团队维护得住吗?
这些问题没有标准答案。
也正因为没有标准答案,才更接近真实工作。
所以现在面试时,我不太期待听到一个完美回答。
我更想看到的是:当标准答案说完以后,这个人还能不能继续往下走。
能不能讲出自己踩过的坑。
能不能承认不确定。
能不能根据场景做取舍。
能不能在一段乱糟糟的代码里找到问题。
能不能把一个看似简单的需求,处理得稳一点。
至于他有没有用 AI,我当然在意。
但我更在意的是,离开 AI 给出的最佳实践,他还剩下多少自己的判断。
夜雨聆风