ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

360前端面试全复盘:从JavaScript原理到Vue源码与性能优化

2026/8/31 12:57:27 拓冰建站 浏览量
360前端面试全复盘:从JavaScript原理到Vue源码与性能优化 1. 整体流程回顾360技术面的轮次安排与考察逻辑1.1 三轮技术面一轮HR的真实节奏先说结论360前端岗的技术面通常是三轮外加一轮HR面整体节奏比我预想的要紧凑。我面的这个岗位base在北京走的是内推渠道从第一轮技术面到HR面结束前后大约两周时间中间每轮间隔三到四天给了比较充分的准备缓冲。第一轮面试官偏年轻应该是组里的一线开发问的问题集中在JavaScript基础、CSS布局以及简单的算法题时长约50分钟。第二轮面试官明显更资深开场先让我自我介绍然后直接进入项目深挖紧接着追问了一堆Vue源码层和工程化相关的问题时长约70分钟。第三轮是技术终面更像是综合能力考察既有偏向系统设计的开放题也穿插了一些场景类的软性问题面试官应该是团队leader或者技术总监级别。HR面放在最后一轮除了常规的离职原因、薪资期望之外还问了不少关于团队协作、遇到冲突如何处理的场景题。整体来说360的面试流程规范每轮的考察目标很清晰。值得提醒的是每一轮都会有手写代码的环节所以光会聊原理是不够的代码基本功必须扎实。1.2 每轮面试官的考察重心差异把三轮面试对比着看你会发现考察逻辑是有递进关系的。第一轮属于广度覆盖考察面铺得很开从事件循环、原型链到盒模型、flex布局再到一道简单的数组去重说白了就是确认你基础扎不扎实。这个阶段不会问得太深但涉及的知识点特别密基本是连环炮式的提问回答稍有含糊面试官就会追下一个问题。第二轮是深度渗透。面试官会抓住你简历上写的某个项目不断往下挖比如你说用了Vue他会追问响应式的实现细节、nextTick的原理、diff算法的优化策略再往后甚至延伸到编译器的优化思路。如果你能接住这层追问他会继续往工程化方向带比如webpack的构建流程、代码分割的策略、微前端在你们项目里的落地方式。第三轮的风格又不一样更偏向系统视角和综合素质。我记得面试官问了一个大文件上传的设计题要求从前端方案、后端配合、异常处理到用户体验优化全部说清楚。这一轮其实不是为了考察某个具体的API或者语法而是看你能不能站在更高的维度思考问题有没有全局意识。2. JavaScript考点逐层拆解原型链、闭包与Promise的手写深度2.1 原型链与继承面试官让我现场推导一套完整的继承方案第一轮面试开场没多久面试官就抛出了一个非常经典的问题说说JavaScript中原型链是什么class的继承本质是什么这个问题本身不新鲜但关键在后面的追问。我背过标准的定义把__proto__和prototype的关系讲了一遍面试官点了点头接着就出了一个代码题function Parent(name) { this.name name; } Parent.prototype.sayName function() { console.log(this.name); }; function Child(name, age) { Parent.call(this, name); this.age age; } Child.prototype Object.create(Parent.prototype); Child.prototype.constructor Child;他让我解释这段代码的每一行在干什么以及为什么Child.prototype要用Object.create(Parent.prototype)而不是直接赋值成Parent.prototype或者用new Parent()。说实话这个知识点我准备过但面试官要求逐行解释时还是要反应快。我当时的回答思路是直接赋值Parent.prototype会导致子类和父类共享同一个原型对象子类若修改原型方法会污染父类用new Parent()则会多出不必要的实例属性。Object.create生成了一个以Parent.prototype为原型的干净对象既不执行构造函数又保持了原型链的正确指向。这还没完他又追问了ES6 class的super()底层做了什么本质上就是Parent.call(this, name)加上原型链绑定的语法糖。这里我给准备面试的朋友一个建议不要只是背结论要把原型链的内存图在脑子里画出来理解每个对象之间的__proto__指向。面试官一旦往深处问纯背题是扛不住的。2.2 Promise的手写实现从then链到微任务的执行顺序手写Promise几乎是前端中高级面试的家常菜360也不例外。第一轮面试官给的题目很直接用原生JS实现一个Promise支持then的链式调用。我一开始以为写个简化版就够了但面试官的要求更细必须处理then的回调返回Promise的情况并且要保证异步执行的语义。这就涉及几个关键点第一resolve之后注册的then回调必须以异步方式执行。在浏览器环境里就是用MutationObserver或者queueMicrotask来模拟微任务。我用的是queueMicrotask实现小而清晰。第二then方法必须返回一个新的Promise实现链式调用的关键就在这里。我当时的简化实现是这样的class MyPromise { constructor(executor) { this.state pending; this.value undefined; this.reason undefined; this.onFulfilledCallbacks []; this.onRejectedCallbacks []; const resolve (value) { if (this.state pending) { this.state fulfilled; this.value value; this.onFulfilledCallbacks.forEach(fn fn()); } }; // ... reject 类似 try { executor(resolve, reject); } catch (e) { reject(e); } } then(onFulfilled, onRejected) { const promise2 new MyPromise((resolve, reject) { const handle () { try { const x onFulfilled(this.value); resolvePromise(promise2, x, resolve, reject); } catch (e) { reject(e); } }; if (this.state fulfilled) { queueMicrotask(handle); } else if (this.state pending) { this.onFulfilledCallbacks.push(() queueMicrotask(handle)); } // ... 其他状态类似 }); return promise2; } }面试官看完之后没有纠结细节转而问了一个执行顺序的题目Promise.resolve() .then(() { console.log(1); }) .then(() { console.log(2); }); setTimeout(() console.log(3), 0); queueMicrotask(() console.log(4));答案是1、4、2、3。这个如果对微任务队列的理解不深很容易答错。关键点是每个then都会产生一个新的微任务所以第二个then的回调必须等第一个then的回调执行完、新的Promise状态变化后才进入微任务队列。2.3 闭包与作用域一道代码输出题引发的思考第二轮面试官上来先给了一道代码输出题看起来很基础但暗藏了几个踩坑点for (var i 0; i 5; i) { setTimeout(() { console.log(i); }, 1000); }输出是5个5这算是送分题。然后他改了条件for (let i 0; i 5; i) { setTimeout(() { console.log(i); }, 1000); }输出是0到4这也是常规操作。但面试官的追问很有意思你能说说let在这里到底做了什么让每次循环都保留了独立的i吗这个点很多人会背块级作用域的结论但讲不清底层机制。我当时的理解是let声明的变量在每次迭代中都会创建一个新的词法环境循环体内部对i的引用指向的是当前迭代的词法环境中的那个绑定。本质上for循环的每次迭代都相当于把let i重新绑定到了一个新的环境下。这是一层理解但要看穿这层机制建议去了解for (let ...)在规范里的特殊语义它并不是简单地把var替换成let。面试官接着又问如果必须用var有几种方式让输出变成0到4这个就简单了IIFE传参、闭包返回值、.bind传参都可以。这一套组合拳下来考察的就是你对作用域和执行上下文的真正理解而不是会背答案。3. 框架与工程化Vue响应式原理、diff算法与微前端实践3.1 Vue2与Vue3响应式实现对比从defineProperty到Proxy题库里关于Vue的问题360问得相当细。第二轮面试官问了一个让很多人头疼的问题Vue2的响应式为什么在Vue3中被Proxy替代Proxy解决了哪些defineProperty做不到的问题标准的回答就是三点第一Object.defineProperty只能劫持已有属性新增和删除属性检测不到所以Vue2需要用Vue.set和Vue.delete来弥补第二defineProperty需要递归遍历对象的所有属性深层对象初始化性能差第三对于数组需要通过重写数组的push、pop等方法来实现拦截显得很间接。但面试官显然不满足于这个层面的回答他继续追问Proxy的性能是不是一定比defineProperty好这个问题问得就很有水平了。实际上Proxy在惰性劫持方面有优势——只有访问到某个属性时才需要拦截但Proxy本身对每个操作都有额外开销不能武断地说Proxy一定更快。Vue3之所以选择Proxy更多是因为它在能力和代码复杂度上好于defineProperty方案而不是纯粹的性能优势。我在回答的时候补了一个关键细节Vue3的reactive实现还配合了WeakMap缓存用来存储对象对应的代理避免同一个对象被重复代理。另外effect的依赖收集机制用到了track和trigger两级结构收集的是属性级的依赖而不是组件级的所以更新粒度更细。3.2 diff算法与key的作用为什么列表更新不能用index做key这个老生常谈的问题我在面试前觉得已经吃透了但实际被问到的时候还是发现有些细节没有串起来。面试官的问题是Vue更新列表时diff算法是怎么做复用的key的作用到底是什么为什么不能滥用index作为key我按照源码的理解回答了整个diff流程新旧vnode数组会分别用两个头尾指针开始对比首先处理sameVnode的情况然后处理新增、删除、移动节点的情况。当不能通过头尾指针直接匹配时会用key建立旧节点的映射表通过key找到可以复用的节点。关于index作为key的问题我举了一个实战场景一个列表用index作为key当你在列表头部插入一条数据时Vue会认为原来的每一项内容没有变化因为key还是0、1、2但实际上每项的内容已经全部错位了。如果列表项有内部状态比如input输入框、checkbox选中状态、以及子组件自身的数据就会产生状态错乱。面试官听完后点了点头然后问了一个稍微进阶的如果列表项本身只是纯展示的文本用index做key会有什么明显问题吗答案是基本不会。因为纯文本节点每次patch都会比较文本内容并更新。所以不能用index做key这个结论不是绝对的主要看列表项是否承载了需要复用的内部状态。能把这个边界说清楚面试官会觉得你真懂而不是死记硬背。3.3 微前端面试官关注的不只是概念而是落地细节因为360内部有多个中后台产品线很多项目都在往微前端架构迁移所以微前端成了面试中很难避开的话题。第二轮的追问中面试官专门问到了你们做微前端的时候子应用之间的样式隔离和全局变量污染是怎么处理的我提到用的是single-spa或者qiankun方案面试官紧接着问了一个很细的点qiankun的JS沙箱有几种模式分别适用于什么场景这个知识点要是不熟很容易答偏。我整理了一下qiankun的沙箱方案分为三种。LegacySandbox是基于Proxy的快照差异型沙箱适用于单实例场景ProxySandbox也是基于Proxy但每个子应用都有自己独立的代理对象支持多实例SnapshotSandbox是qiankun早期方案通过记录window对象在激活前后的快照差异来还原环境兼容性最好但不支持多实例。面试官并没有停留在沙箱模式而是继续往深挖样式隔离如果不用shadow DOMqiankun是怎么做的子应用的样式泄漏到主应用怎么办这里我讲了实际踩过的坑qiankun默认会将子应用的样式用html标签包裹并设置一个>// main.js const worker new Worker(/hash-worker.js); worker.postMessage({ file, chunkSize: 2 * 1024 * 1024 }); worker.onmessage (e) { if (e.data.type progress) { updateProgress(e.data.percent); } else if (e.data.type done) { const fileHash e.data.hash; checkUploaded(fileHash); } };在Worker内部用FileReader读取每一个分片的ArrayBuffer通过累加的方式计算整个文件的hash值。这里有个关键细节如果文件特别大不建议一次性把整个文件读进内存再计算hash而是逐个分片读取、逐片哈希迭代这样内存占用是可控的。面试官对这个方案比较满意还追问了为什么hash值能唯一标识文件。我回答虽然理论上不同的文件可能算出相同的hash撞hash但在实际业务中概率极低可以作为文件级别的唯一标识。如果业务对正确性要求特别高可以配合文件大小加文件名一起作为辅助标识。这个回答让面试官看到我在考虑概率和业务权衡的问题而不是机械地套方案。6. 项目复盘与反问技巧如何讲述项目经验并给面试官留下印象6.1 用问题-冲突-方案-结果的方式讲项目第二轮的面试官在深入技术问题之前先让我挑一个简历上的核心项目做介绍。这里一定要警惕千万不要平铺直叙地讲我做了某某系统用了某某技术栈实现了某某功能这是最无趣也最无用的介绍方式。我用的结构化方式是背景—矛盾—取舍—量化结果。比如我讲到之前做的一个数据可视化平台先交代背景旧的报表系统每次加载需要8秒以上业务方天天投诉。然后讲矛盾数据量大单接口返回全量数据导致渲染卡顿但业务方又要求看到的是全维度分析不能简单用分页解决。接着讲方案前端引入了按需加载和Web Worker做数据聚合计算配合虚拟滚动渲染同时把大数据量请求切成了多个小的并行请求。最后讲结果首屏加载从8秒降到2.3秒内存占用降低了约40%。面试官大概率会追问你们是怎么定位到性能瓶颈的这个时刻非常关键你要能说出具体的定位过程比如Performance面板里看到Long Task发现是数据处理阶段阻塞了主线程2秒多于是决定把数据聚合逻辑移到Worker中。这种细节的还原度会让面试官相信你真的做过而不是包装出来的项目。6.2 反问环节如何问出有信息量的问题技术面最后通常会给你反问的机会。很多候选人会问公司福利怎么样加班多不多这类问题不是不能问但放在HR面会更合适。在技术面反问阶段我的经验是从以下两个方向提问题。第一个方向是业务技术问题比如360前端团队目前最大的技术挑战是什么团队在微前端落地过程中遇到的最大难题是什么。这个问题隐含了你对团队技术方向的好奇也能从中判断这个团队是偏向业务交付还是基础设施建设。第二个方向是团队协作和工作方式比如前端和后端联动的CI/CD流程是怎么设计的代码review是怎么开展的。这类问题能让你了解团队的工程文化和成熟度一个注重工程质量、有完善review流程的团队通常更值得加入。我在这一轮问的是团队目前对Vue3和React的技术栈选择是怎么考虑的面试官很认真地回答了他们正在从Vue2迁移到Vue3同时微前端基座也正在从自研方案往更成熟的社区方案演进。这个回答让我对团队的技术未来有了清晰的预判。6.3 软性能力协作冲突与跨部门沟通的应对第三轮还出现了一个意料之外的问题你和产品经理对需求理解有分歧怎么处理这不是一个技术问题但答得好能极大加分。我的回答涵盖了三个层次第一先对齐目标和用户价值把分歧点拉回到这个需求到底服务什么场景、解决用户什么痛点上来讨论第二用数据或原型来说话如果你觉得某个交互方案体验不好做一个简易原型或者测一轮用户数据作为佐证第三如果仍然无法达成一致在不影响核心价值的前提下可以先按产品经理的方案实现一个最小版本上线后看数据再决定是否调整。面试官听完之后补充了一句很多前端候选人技术很强但沟通协作能力是短板你能说出这样一套处理流程说明在真实项目里确实经历过类似的冲突。这句话说明软性能力在技术终面中的权重并不低。7. 复盘与建议这次面试暴露出的薄弱点与针对性补强方向7.1 我这次面试踩过的坑虽然整体面试结果比较顺利但复盘之后我还是发现了一些值得记录的问题。第一个坑是手写代码时过于依赖记忆。在实现Promise的时候我虽然搭出了主框架但在处理resolvePromise的边界情况时有点卡壳——特别是当then回调返回的对象恰好是promise2自身的时候需要抛出一个TypeError。这个细节我在准备时知道但一到面试现场脑子里想的是先把手写Promise的大框架搭出来细节部分反而被忽略了。事后想想这种错误很可惜因为框架正确但边界条件缺失在严格的面试官眼里会扣不少分。第二个坑是在讲项目时容易陷入报菜名式的技术罗列比如我用了Vue3、webpack5、微前端、Web Worker这一连串名词但面试官追问某项技术的动机和落地细节时第一反应却是在回忆文档而不是讲自己的权衡过程。后来我把项目的讲述方式从技术清单调整为问题驱动效果明显好了很多。建议准备面试的各位把项目里的每个关键技术点都当成一个为什么来准备而不是一个用了什么的答案。第三个坑是算法题的边界测试不够。360的算法题不会出特别难的动态规划更多是考察逻辑缜密程度的题目比如数组去重、两数之和、链表的反转这一类。但面试官会在你写完代码后要求你手动跑测试用例我在第一轮写链表反转时没有主动考虑空链表和单节点的情况面试官提醒之后才补上。这个问题暴露了我写完就完事的习惯实际上每一道手写代码题都应该有防御性编程的意识。7.2 后续补强计划与准备建议面完之后我给自己定了一个补强计划核心有四个方向。一是把JS基础的知识体系重新用源码手写的方式过一遍不只是背概念而是把new、call、apply、bind、debounce、throttle、Promise、EventEmitter这些经典的手写实现全部默写熟练做到任何一道都能在两三分钟内落笔。二是框架源码阅读要有产出。光看源码不产出等于白看我的做法是每读一个模块就写一篇笔记梳理核心的数据结构、调用链和设计原因。比如Vue3响应式模块要能画出effect、dep、track、trigger之间的关系并能解释为什么依赖收集要设计成属性级而不是组件级。三是工程化工具的配置实战要跟上。webpack的loader和plugin机制、Vite的预构建原理、ESBuild为什么快这些不只是概念要能自己搭一个小项目实际操作一遍。360的面试官明显对微前端、构建优化这些工程化问题有实际经验所以回答的时候一定要带着实践细节不能停留在用过层面。四是要多做模拟面试特别是追问环节的模拟。很多时候我们准备的是标准答案但面试官真正考察的是你能否在压力下快速思考、条理清晰地回答问题。找朋友或者使用AI面试工具进行多轮模拟对提升临场反应有很大帮助。回到360前端面经这个话题除了技术准备之外我对这家公司的整体印象是团队风格偏务实面试官不刁难人但问题密度高、追问深非常看重候选人的真实项目落地能力和对技术原理的本质理解。如果你有意向投360的前端岗位建议把精力重点放在JS基础的手写实现、Vue/React源码深度和工程化实战这三个方向同时复盘好自己项目里每一个技术选型背后的为什么。面试更像是一场高质量的技术交流把心态从被考察调整为双向了解和展示发挥会更稳定。