普通前端秋招复盘:面试准备、算法刷题与offer选择

普通前端秋招复盘:面试准备、算法刷题与offer选择 2019年秋招结束那天我在备忘录里写下了一句话原来“普通”两个字才是大多数人秋招的真实底色。没有ACM金牌没有大厂实习没有开源项目几千star有的只是一年多来老老实实写的业务代码、啃过的源码笔记、和上百道刷了忘、忘了刷的算法题。这篇文章想复盘一下我这个普通前端研究生从7月到10月的完整秋招过程前后投了20多家公司实际参加了10家以上的面试最终拿到几个offer不炫耀也不贩卖焦虑只把每场面试里让我印象深刻的题目、答法和反思整理出来给后来人做个参考。1. 秋招前的战术准备普通前端如何找准自己的定位1.1 认清自己的底牌才能决定往哪个方向使力秋招之前我先给自己做了一次彻底的“体检”。学校是普通一本计算机相关专业研二开始自学前端没有一段正式的互联网大厂实习经历项目做过两个一个Vue的后台管理系统一个基于小程序的校园服务平台。技术栈以Vue为主React只是会写组件、看过文档的水平算法水平是LeetCode刷了不到80题动态规划基本看到就放弃。这个水平放在2019年的秋招池子里确实是标准的中等偏下。但我很清楚一件事秋招不是比谁的技术上限高而是比谁在面试官面前暴露的短板少。我给自己定的策略很简单——基础题不丢分项目题讲细节手写题写正确算法题至少能聊思路。这四条听起来容易但每一条都需要拆成具体的执行动作。前端面试的基础题范围其实非常固定JS原理、浏览器、网络、框架、工程化。既然没有大厂实习背书那我就用“基础扎实”来补。从7月开始我每天花两个小时重看《JavaScript高级程序设计》的闭包、原型、异步章节把每一个知识点都强行问自己一个“为什么”为什么会有闭包为什么原型链能实现继承为什么Event Loop要分宏任务和微任务这个习惯后来在面试里帮了我大忙因为面试官最常做的一件事就是顺着你的答案往下追问如果你只是背了结论一旦问到“为什么这样设计”立刻就会露馅。1.2 三个月冲刺时间表每一天都要有交付物有了定位之后我把7月到9月拆成了三段7月是“基础补漏期”重心放在JS原理、网络、浏览器以及Vue源码的核心流程8月是“刷题强化期”每天雷打不动两道算法题加三道手写题同时把面经里高频的题目整理成自己的答题框架9月是“实战调整期”边投简历边面试每面完一家就当晚复盘把没答上来的题重新查资料弄懂。这里想多说一句时间管理。我不是那种能一天学12个小时的人所以我的方法是给每天设定“最低交付物”两道LeetCode一道简单一道中等、一道手写题、一个知识点的深入理解加笔记、半小时面经阅读。最低交付物意味着哪怕今天再累再慌完成这四件事就算保住底线。事实证明三个月累计下来算法题刷到200道左右手写题练习了40多道面经里高频考点基本都过了一遍。资料选择上我没有贪多。算法只用LeetCode手写题参考网上整理的前端手写题汇总框架方面直接啃Vue 2.x源码中响应式和虚拟DOM相关的部分网络和浏览器就看《图解HTTP》加各种高质量博客。资料越少越容易坚持这个道理和减肥一样计划定得再完美执行不了等于零。2. 简历和项目让每个技术点都扛得住追问2.1 简历里写的每一个词都要准备好讲30分钟秋招前我改过四版简历前三版的问题都一样太喜欢堆技术名词。比如我写过“熟悉Vue全家桶、掌握Webpack配置、了解Node.js”这种写法在HR眼里等于什么都没写在面试官眼里则是一份“追问清单”。第一次模拟面试我就被问住了简历上写着“熟悉Webpack”面试官直接问“Webpack的Loader和Plugin有什么区别手写一个简单的Loader你会怎么写”我当时脑子里只有“Loader处理文件Plugin做额外的事情”这种模糊印象场面相当尴尬。后来我把简历彻底重写了原则只有一条只写自己能展开聊三十分钟以上的内容。技术栈从“熟悉Vue全家桶”改成了“深入理解Vue响应式原理与虚拟DOM diff流程”项目描述里不再写“使用Axios请求数据”而是写“针对首屏加载性能优化实现路由级代码分割与图片懒加载首屏时间降低约40%”。当然写上去的前提是我真的能解释清楚每一项是怎么做的、为什么有效。为了应付“你是怎么做性能优化的”这种问题我把项目里用到的每一个优化点都写了详细备注Computed缓存的计算过程、KeepAlive的作用机制、懒加载对资源请求数量的影响等等。2.2 项目复盘的“三个一”准备法项目部分是我这种没有大厂实习的人最需要打磨的所以我发明了一套笨办法我叫它“三个一”一个讲得最顺的主项目、一个最能体现技术难点的功能点、一个能讲出反思的败笔。先说主项目。我用的是那个Vue后台管理系统从需求背景讲到技术选型再讲到模块划分控制在五分钟内讲完。重点不是功能列表而是我做的几个决策为什么选Vue而不是React当时我的回答是团队原有技术栈是Vue而且项目以表单和表格为主Vue的模板语法和响应式机制更适合这种场景。这个回答未必是标准答案但体现了我做技术选型时会结合业务场景而不是跟风选框架。再说技术难点。我挑的是权限控制模块涉及路由守卫、动态路由、按钮权限指令封装这几个点。面试官大概率会顺着问“动态路由怎么实现的”“刷新页面后权限信息没了怎么办”“你们按钮权限和数据权限是怎么区分的”我把这几个问题全部提前演练过并且真的去查了Vue Router的addRoutes源码确保自己说的是对的。最后是败笔。我准备的是项目里一个Socket通信模块最早用了原生WebSocket后来因为断线重连和心跳机制处理不好改用了Socket.io。复盘的时候我承认自己当时对网络协议的理解不够这次重构才真正搞懂了二进制帧和长连接的意义。这个“败笔”反而让面试官觉得你是一个会反思的人比通篇讲“项目多么牛”要可信得多。3. 十家公司面试实录从一面到终面的完整复盘3.1 阿里连环追问式面试每一个答案都要经得起深挖阿里的面试流程是简历筛选、一面、二面、三面一般都有交叉面或主管面。我投的是某BU的前端岗位一面面试官很年轻问得非常细。开头先让我自我介绍然后直接进入基础题环节第一题是“手写节流函数并说说和防抖的区别”。这题我练过很多遍写完之后面试官追问“如果我把节流函数的最后一次调用延迟执行你怎么实现”这一下就把问题从简单的定时器版升级到了edge case处理。我一边写一边说思路核心是记录上一次执行时间和当前时间通过setTimeout补上最后一次调用。接着是一道很经典的事件循环输出题console.log(start) setTimeout(() { console.log(timeout) Promise.resolve().then(() { console.log(promise in timeout) }) }, 0) Promise.resolve().then(() { console.log(promise) }) console.log(end)答案是start、end、promise、timeout、promise in timeout。这个题考的是宏任务和微任务的执行顺序以及微任务在宏任务内部仍然遵循“清空微任务队列”的原则。我答完后面试官又问“那Node.js环境下的执行顺序和浏览器一样吗”这里是个坑Node 11之后Event Loop行为已经和浏览器趋同我如实说了自己了解到的结论并补充了process.nextTick的优先级问题。阿里的二面是重点问项目三面则开始问场景设计题。印象最深的一道是“如果让你设计一个搜索框的自动补全组件你会考虑哪些问题”我当时从输入防抖、请求竞态、缓存策略、键盘导航、无障碍支持、移动端适配这几个维度展开边说边画草图最后面试官追问了竞态问题——如果先发的请求后返回怎么避免覆盖后发请求的结果这个我正好在项目里遇到过就讲了用请求序号或取消上一个请求的方案。阿里的三面结束后过了大概一周收到了感谢信。复盘下来我的问题出在二面某个问题上回答得不够深入面试官问Vue3的Proxy相比Object.defineProperty的优势我只说了能监听新增属性和数组变化但没有展开讲性能差异的底层原因Object.defineProperty需要递归遍历对象Proxy是懒代理且代理整个对象。这个点在后面面的公司里我做了针对性补强。3.2 字节跳动算法题扛不住前面全白搭字节的面试流程同样很紧凑一面基本是“两题算法加一题JS”。投的是北京的前端岗位一面第一道算法是“三数之和”中等难度。这道题我刷过常规解法是排序加双指针我一边写一边讲思路先排序固定一个数然后用双指针找剩下两个数注意去重。这里有一个小细节去重时要在找到一个解之后同时移动左右指针而不是只移动一个否则会漏解。第二道算法是“手写一个LRU缓存”这道题真的问到我心里了。虽然做过但紧张之下还是写错了Map的遍历顺序。面试官没有直接说错而是提醒我“get操作之后这个key应该变成最新使用”。我反应过来说是实现的问题重新写了一遍把get时先delete再set的逻辑写清楚才算过。字节二面开始考前端工程化和浏览器原理。“从输入URL到页面展示这个过程尽可能详细地讲一遍”——这是我最喜欢的一道题因为它的答题框架可以覆盖DNS解析、TCP连接、HTTP请求、服务器响应、浏览器解析HTML构建DOM树、CSSOM构建、渲染树、布局、绘制以及过程中的优化点。我从导航阶段讲到了渲染阶段中间还提到了强缓存和协商缓存。面试官追问了一个“如果把JavaScript放到head里会怎么样”成功引出了HTML解析阻塞和defer/async的区别。三面有一道设计题“给你一个百万级PV的页面前端需要做哪些优化”这类题没有标准答案考察的是系统思考能力。我当时从网络层CDN、压缩、合并请求、渲染层骨架屏、懒加载、减少重排重绘、缓存层HTTP缓存、localStorage、代码层代码分割、Tree Shaking几个角度展开每个优化都配套讲了一个可量化的指标。这轮结束后我自己感觉相对稳但最终没有走到HR面。字节的一面和第二面考核权重很高尤其算法题写不出来真的很难靠其他环节弥补。3.3 美团、网易、京东业务导向的技术考察美团是让我觉得最“接地气”的一家。一面问的问题基本都围绕真实业务场景“外卖列表页在弱网环境下怎么优化”“如果商品价格实时变动前端如何保证展示一致性”这类问题考察的是你能不能跳出纯前端思维来思考。我当时答了骨架屏加分页加载、WebSocket实时推送价格变动、乐观UI加失败回滚这几个点面试官点头但没有深入可能更想听的是你对业务数据的理解。美团的二面考了Webpack构建优化面试官问“项目打包太慢你怎么排查和优化”我说了先用webpack-bundle-analyzer分析体积分布再做代码分割、按需加载、压缩、缓存。他追问“happyPack为什么能提升构建速度”这个问题涉及Node.js多进程我答得不算好只说了开启多进程并行处理模块但没有提到通信开销和适用场景。美团最终拿到了offer但排序不算高大概率就是被这类细节问题拉低了分。网易的面试则给我敲了一个警钟虽然我是Vue技术栈但大厂不会因为你是Vue系就放过React。一面问“Vue和React的区别”我讲了模板和JSX的区别、响应式机制和setState的区别、组件通信方式的不同。面试官顺着问“有了解过React Hooks吗为什么Hooks不能在循环里调用”我当时只会背规则讲不清楚底层的链表结构原理只能老实说“源码这块还没深入”然后补了一句“但从设计角度看Hooks的顺序一致性是保证状态稳定的基础”。这个回答保住了基本盘但也暴露了技术视野的局限。京东的面试相对务实考了不少小程序相关的问题因为他们当时在推自己的小程序体系。印象深的是“小程序为什么不用DOM而用Exparser”以及“跨端方案为什么会出现”这类问题回答时要注意别陷入具体的某个框架而是从双线程架构、渲染性能、包体积这些角度去谈。3.4 其他公司速写华为、拼多多、快手、B站华为的流程一般比较长现场面试甚至会有性格测试。技术面相对温和问了一些基础的HTTP和数据结构问题手写题也比较常规我记得当时写了“数组去重”和“字符串反转”这类难度。华为的offer排序整体靠后我个人感觉他们更看重学校背景和综合素质如果你有志于去华为可以提前了解一下他们内部的IT产品线和技术栈再决定要不要投。拼多多的前端面试比较强调性能优化和高并发场景。一面问“如果页面需要展示上万条数据你会怎么做”我从虚拟列表、分页、时间切片三个方向各自分析了优缺点最后选择虚拟列表加分页的方案。面试官追问“虚拟列表的固定高度和动态高度分别怎么实现”这里我答得比较扎实因为之前专门研究过动态高度需要预估高度再二次校正的实现。快手的面试体验在秋招里算不错的面试官年轻、交流感强问的问题也比较新。二面直接问了一道LeetCode原题“合并K个升序链表”我用了优先队列的思路但JS没有内置堆就在面试官提示下改用了数组排序的方法保证了正确性但牺牲了时间复杂度。面试官并没有为难我反而说“优先队列的思路是对的以后可以自己实现一个堆”。快手还问了一道“React Hooks为什么需要依赖数组”考察的是对useEffect闭包捕获的理解这对Vue技术栈的我来说算超纲但因为之前补习过勉强答上来了。B站是这几个公司里技术氛围最轻松的问题偏基础手写“深拷贝”的时候我写了递归版本面试官让我考虑循环引用的问题我补充了WeakMap记录已拷贝对象的方案。B站的岗位偏向React技术栈加上我最终的意向不在上海所以面完就没有再推进。4. 前端核心考点的底层逻辑从背八股到讲原理4.1 JS基础面试官想要的不是定义是设计思路到了9月中旬我面了七八家公司之后开始慢慢总结出一个规律面试官真正想考察的不是“你会不会用这个API”而是“你能不能理解这个API为什么存在”。这一点在闭包、原型链、Event Loop这几类题目上体现得特别明显。以闭包为例标准回答是“函数执行完后内部变量被外部引用导致没有释放”。但面试官追问的往往是“闭包的内存泄漏应该怎么避免”或者“闭包在项目里到底用在哪些场景”。我的项目里用闭包做过防抖节流、模块化的私有变量隔离、还有一个循环里绑定事件的经典问题。当你能把概念落到真实场景时面试官就不用再试探你是不是背的。原型链也是同样道理。单纯画一张“__proto__和prototype的指向图”答不了今天的面试。我复习的时候换了一个思路从“设计一个继承方案”出发逐步从构造函数继承讲到原型链继承再讲到组合寄生式继承每一种方案的优缺点都对应着下一个方案的动机。这样一来面试官无论从哪个角度问我都能接得住。Event Loop就更不用说了。不要只背“先执行同步代码、再微任务、再宏任务”的口诀而是要理解浏览器为什么这么设计UI渲染需要让出主线程Promise这样的微任务需要在下一次渲染之前执行完。我用“排队打饭”打了比方宏任务队列是一楼窗口微任务队列是二楼窗口你永远先把二楼的人全部服务完才会看一眼一楼新排的人。面试官听了通常会心一笑这就是所谓“用生活类比解释复杂概念”。4.2 浏览器与网络把经典题目做成自己的答题框架“从输入URL到页面展示”是前端面试的必考题但大多数人的答案都卡在“DNS解析、TCP连接、HTTP请求、渲染”这个骨架讲不出血肉。我后来整理了一个自己的答题框架分成三步第一步讲解析阶段URL解析、HSTS检查、DNS解析浏览器缓存、操作系统缓存、路由器缓存、根DNS服务器的逐级查找再到TCP三次握手、TLS握手。TLS握手里有一个2019年前后特别喜欢考的点——TLS 1.3相比1.2少了哪一次往返RTT答案是1.3只需要一次往返就能完成握手。第二步讲请求与响应HTTP请求行、首部、请求体服务器返回后浏览器怎么判断缓存强缓存命中就直接200-from-cache协商缓存发If-Modified-Since/If-None-Match以及状态码301和302的区别重定向场景。第三步讲渲染流程解析HTML生成DOM树、解析CSS生成CSSOM、合并生成渲染树注意display:none不会进渲染树visibility:hidden会、布局计算、绘制。到这里还要补充一个关键概念——回流和重绘的区别以及CSS属性触发回流还是只触发重绘。有了这个框架无论面试官从哪个环节切入你都可以顺着讲下去。我甚至会在回答的结尾主动说一句“整个过程涉及了很多优化点比如预加载、HTTP/2多路复用、SSR或者预渲染需要我展开讲哪个吗”这个主动引导其实对面试节奏很有帮助能让面试官在你擅长的话题里提问。4.3 Vue框架原理源码不是用来背的是用来理解设计的我学Vue源码的方式不是逐行读而是带着问题去读。核心问题就三个数据变了视图为什么知道要更新视图变了事件从哪里来列表变了DOM为什么没有被全部重建第一个问题对应响应式原理。Object.defineProperty遍历data对象给每个属性绑定getter和settergetter里收集依赖Watchersetter里触发更新notify。我从这个流程里理解了为什么Vue3要用Proxy因为Object.defineProperty无法监听新增属性、无法监听数组的push和pop等操作所以Vue2才需要$set和重写数组方法。这个理解比单纯背“Proxy比defineProperty强”要有说服力得多。第二个问题对应模板编译和事件绑定。模板会被解析成AST然后生成render函数render函数执行时创建虚拟DOM。事件绑定最终在patch阶段通过addEventListener挂载。第三个问题对应diff算法。我画过很多遍新旧vnode树对比的简图同层比较、tag不同直接替换、key的作用是复用节点。这里有个高频追问“为什么不给列表加index作为key”我当时从实际例子出发——在数组头部插入一项index作为key会导致所有后续节点被错误复用状态错乱。框架原理复习透了之后我再去面后面的公司被问烂的“Vue生命周期”这类题都能答得更深不只背每个钩子干了什么而是讲清楚为什么beforeCreate的时候不能访问data、为什么mounted之后才能操作DOM、filters和computed的区别底层是什么。5. 算法与手写代码普通人的应试策略5.1 刷了200道题我总结出的高频题型清单我的算法基础真的很一般属于那种“看一遍题解觉得自己懂了关上屏幕写不出来”的类型。所以我的策略不是追求题量而是把LeetCode里前端岗位的高频题型吃透。刷到200道左右的时候我发现大部分面试算法题都逃不开这些类型字符串类反转字符串、有效括号、最长公共前缀、字符串相加数组类两数之和、三数之和、合并两个有序数组、旋转数组链表类反转链表、合并两个有序链表、环形链表、删除倒数第N个节点二叉树类前中后序遍历、层序遍历、二叉树最大深度、最近公共祖先动态规划爬楼梯、打家劫舍、最长递增子序列我的做法是把每一类高频题整理成一个模板笔记比如链表反转的迭代和递归两种写法、二叉树遍历的递归和非递归两种写法都提前写好面试时直接套模板。不要嫌模板化面试考场上能稳定输出才是第一位的。5.2 前端手写题清单这些必须闭着眼睛写出来除了算法题前端面试还有一类“手写题”是不能丢分的。我整理过一份自己练到吐的清单基本覆盖了十家公司里被问到的所有手写题防抖和节流带不带立即执行、cancel方法深拷贝考虑循环引用、Date、RegExp等特殊类型Promise.all、Promise.race、Promise.resolvecall、apply、bind手写new操作符的实现instanceof的原理数组去重、数组拍平考虑深度对象深比较练手写题的方法特别重要不要直接背答案。我的方法是先理解这个API的输入输出和核心流程然后在纸上自己推导一遍写错了再看答案最后隔两天再默写一遍。以手写new为例核心逻辑就四步创建一个新对象、将其原型指向构造函数的prototype、执行构造函数并绑定this、如果构造函数返回对象则返回该对象否则返回新对象。把这个流程理解了不管面试官加什么花样都能应对。5.3 遇到不会的题怎么“演”下去面试中一定会有你不会的题关键是别急着说“不会”。我后来总结出一套话术“这道题我没有直接写过但基于我的理解它考察的应该是XX我的大致思路是这样……”这句话的作用有两个一是让面试官知道你在思考而不是放弃二是即使思路错了面试官也可以顺着你的思路纠正比冷场强得多。字节面试时被问过“如何实现一个简单的虚拟DOM”我当时确实没写过但我从Vue的render函数和diff流程说开讲了创建vnode对象、新旧对比、最小化更新DOM的基本思路。面试官听完说“大方向是对的”又引导我提了一两个细节这道题就算过了。面试不是考试遇到不会的题把你的分析过程展示出来比死磕出完美答案更重要。6. 踩过的坑与写给后来者的话6.1 这几个坑我替你们踩过了第一个坑是投递时间太晚。我8月中旬才开始集中投简历但其实很多大厂7月份就启动了内推通道到了9月很多岗位的hc已经少了一半。建议后来者6月就开始关注内推信息不要等准备好了再投因为“准备”永远不会彻底准备好。第二个坑是投了太多公司导致精力分散。我前后投了20多家导致前期每场面试都在不同的技术栈之间切换Vue面完面React、React面完面Node状态非常割裂。如果再来一次我会把目标公司分成三档冲一档、稳一档、保一档每档各三到四家集中火力准备。第三坑是HR面没有认真准备。我一度以为HR面就是聊聊天结果腾讯的HR面问“你的预期薪资是多少为什么是这个数”我支支吾吾说了一个自己都不信的数字感觉印象分掉了不少。后来才知道HR面也会淘汰人尤其在多个候选人条件相近的情况下HR倾向于选择表达清晰、有规划的人。提前想好“为什么选择我们公司”“未来三年的规划”“能接受加班吗”这三个问题不要临场发挥。6.2 秋招时间线复盘与offer选择整个秋招的时间线我是这样走过来的7月到8月中旬集中补基础、刷算法8月下旬开始内推投递同时保持每日刷题9月初进入面试密集期基本每周都有两到三场面试9月底一批结果出来拿到美团、京东和一家独角兽公司的offer10月上旬字节的流程走到了终面最终没有发offer10月中旬结束秋招签了最终选择。说到offer选择的逻辑我当时纠结了很久一个大厂的非核心部门一个独角兽公司的核心前端团队薪资后者略高一点。我最终选了核心团队原因是这个团队的技术氛围更浓厚、产品处于成长期、能接触到的技术挑战更多。大厂虽然平台大但如果是边缘业务很容易陷入写运营页和活动页的循环。这个选择可能有争议但对我来说年轻的时候多经历一些有挑战的事情比平台背书更重要。6.3 最后想分享的一点心里话秋招结束之后回头看其实大多数人的秋招都不是“大神收割offer”的爽文而是踩坑、补救、焦虑、自我怀疑交织的普通故事。我也曾在深夜翻牛客和各类面经越翻越觉得自己什么都不行也曾面完一家公司走出大楼就开始背下一家的八股。但回过头来最有效的准备永远是朴素的基础题不求多但求深项目里写过的每一行代码都要能说清动机算法题把高频题型练到肌肉记忆面试之后当晚立刻复盘。这篇文章里所有题目和回答都是我真实经历过的希望其中的某个答案、某个思路、某个坑能在你秋招的路上帮上一点忙。无论最后去了哪家公司这个过程的成长才是真正属于你的东西。