ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

携程资深前端面试复盘:从事件循环到虚拟滚动的技术深挖

携程资深前端面试复盘:从事件循环到虚拟滚动的技术深挖 2. 一面技术面基础题考的不是答案是推导5. 我踩过的坑和给后来人的建议先交代下背景我大概两年半经验主攻方向是 Vue 技术栈也写过一段时间的 React 项目。这次投的是携程的资深前端工程师岗位机缘巧合之下走完了整个面试流程趁着记忆还热乎赶紧把全过程整理出来。整体感受先说结论携程的面试风格整体偏务实不像某些大厂那样抠偏题怪题但很看重你对技术方案的理解深度和边界把控。面试流程走得很快从初筛到拿到offer一共两周时间一共四轮电话初筛、一面技术面、二面项目深挖面、三面交叉面和HR面。每一轮的考察侧重点都不一样而且明显是逐层递进的。这篇文章我尽量还原面试官问的每一个问题、我当时是怎么答的、以及事后复盘时觉得哪些地方答得不好。不管你是准备跳槽还是在积累面试经验我觉得都有参考价值。因为我把大部分精力花在准备前端八股文上结果发现真正的面试重点压根不在这里——面试官更在意你面对一个开放性问题时能不能给出清晰的思路和理由。1. 投递背景和整体流程节奏我是在招聘软件上更新了简历之后被携程的HR主动找上来的。岗位描述写得很泛就是资深前端工程师负责的业务方向是商旅相关的企业级中后台系统。我当时没有抱太大希望毕竟携程的岗位要求里写着精通Vue或React有大型项目经验这些条件我硬实力勉强算沾边但简历上没有特别突出的亮点。HR约的第一轮电话沟通其实很简短大概二十分钟左右。她主要问了三个问题现在的工作内容是什么、为什么不考虑内部晋升、对携程这边有没有什么了解。这轮基本不聊技术主要是确认你的意愿度。我如实地说了自己想去业务更复杂、流量更大的场景里锻炼也聊了聊我之前出差经常用携程订机票酒店对产品本身比较熟悉。电话结束后第二天HR就加了微信约了一面的时间。流程时间线大概是这样的轮次面试形式时间侧重点电话初筛电话沟通20分钟稳定性、意愿度、基本信息一面视频面90分钟前端基础知识、代码能力二面视频面60分钟项目细节、技术方案、系统设计三面HR面现场视频90分钟综合素质、思维深度、薪资期望一面到二面中间隔了三天二面到三面隔了五天如果面试官对你满意推进速度是非常快的。反之如果一面之后超过一周没消息大概率就是挂了。这一点大家心里有个预期就好不用频繁去催HR因为没有意义。我觉得这次面试最大的一个感受是携程的面试官对基础知识的考察并不是要你背概念而是给了你一个具体的场景让你去分析和解决。这和我之前面试过的一些公司很不一样后者往往是直接抛八股文然后看你能背出多少条文。携程的面试官会一直追问为什么直到你说出问题的本质。2. 一面技术面基础题考的不是答案是推导一面安排在某个工作日的下午面试官看起来年纪不大自我介绍是商旅事业部的前端组长。整个面试过程用的是视频会议先让我做了两分钟的自我介绍然后直接进入正题。第一个问题就很开门见山Array.prototype.map和forEach的区别是什么这个问题是个新手都能答上来map会返回新数组、forEach不会。但面试官的追问来了如果我在map的回调里直接修改原数组的元素原数组会变吗如果回调里返回的引用类型新数组里的对象和老数组里的对象是同一个吗这两个追问直接考察你到底有没有真正理解数组方法背后的引用语义。我当时答得还行但说实话如果对JavaScript引用类型理解不深的人很容易在这里翻车。接着问到了JavaScript的事件循环机制。这题我准备过很多遍宏任务微任务、Node环境下的setImmediate和process.nextTick的区别都背得很熟。但面试官给的这个例子比较刁钻async function async1() { console.log(async1 start); await async2(); console.log(async1 end); } async function async2() { console.log(async2); } console.log(script start); setTimeout(() { console.log(setTimeout); }, 0); async1(); new Promise((resolve) { console.log(promise); resolve(); }).then(() { console.log(promise then); }); console.log(script end);这题本身不难但关键在于面试官问了一句如果await后面跟的async2返回的是一个rejected Promise后面的代码还会执行吗。这个追问考察的是Promise错误处理机制你得知道await表达式会把rejection抛出来并且如果不捕获的话会影响后续代码。我当时的回答是会抛异常导致async1后面的代码不执行但不会中断整个进程因为这是一个未处理的Promise rejection面试官点头表示认可。一面还问了很多网络相关的问题因为现在面试基本上绕不开HTTP。比如浏览器缓存的机制有哪些强缓存和协商缓存分别由哪些响应头控制从输入URL到页面展示的完整过程HTTP/1.1和HTTP/2的区别以及HTTP/3有什么改进。这块我平时开发中一直在用答起来比较轻松。但有一个问题是之前没太思考过的面试官问如果页面有多个HTTP/2请求其中有一个请求特别慢会不会阻塞其他请求这就是HTTP/2多路复用的边界问题理论上不会因为同一条连接多个stream是并发的但如果TCP层出现拥塞控制还是会有影响。我当时结合WebSocket和HTTP短连接的区别做了解答面试官表示这个理解可以。一面最后环节是手写代码用的是在线编辑器两道题。第一道是手写一个函数防抖和节流要求支持leading和trailing参数。这个题目难度不大但要注意细节比如this指向、取消功能。我大概花了十分钟写完面试官看了之后问了一个问题你这个防抖的取消功能极端情况下会有什么问题我愣了一下去想然后面试官提示说如果在setTimeout已经添加到事件队列之后再去cancel实际上是阻止不了回调执行的。这个点确实容易忽略我当时用clearTimeout确实没法保证回调不执行得配合一个flag变量来控制。面试官说这种边界情况他之前就在生产环境踩过所以特意拿来考我。第二道题是手写一个发布订阅事件中心要求实现on、off、emit、once这4个API。这题我十分钟写完面试官看了以后没有追问细节直接说进入下一部分。后来回想我写的实现里对off的判断方式是准确的用filter而不是splice不会出现数组index错乱的问题这可能是加分项。如果大家平时用过EventEmitter或者Vue的$on/$off这道题其实就是围绕这些类型的源码逻辑来出的理解底层原理会比较好写。一面结束前面试官问我有没有想问的我问了两个问题一是团队目前对微前端的态度因为我有看到热搜词里微前端讨论很火携程这种大体量业务的系统肯定有这个诉求二是目前前端团队在工程化上的侧重点。面试官很详细地介绍了他们正在做的事情包括统一构建链路、面向业务的中后台组件库建设、以及低代码平台的实践。从这也能看出来携程的前端团队算是比较卷的不是那种只写页面的团队。3. 二面项目深挖面试官追问的每个细节都是考察点二面的面试官是技术总监级别聊天节奏比一面更慢但每一个问题都问得更深。这一轮几乎没有考知识点从开场到结束全部聚焦在简历上的项目经历。我的简历上写了一个复杂表格系统的项目大概是支持虚拟滚动、单元格合并、列冻结等功能的动态配置系统这个项目在逻辑上和面试官所在业务有很强的关联。面试官直接打开了我的项目演示链接一边看一边问这种方式压迫感很强因为没法背答案。他问的第一大块是性能优化。你做了虚拟滚动那你的虚拟滚动是怎么实现按需渲染的如果表格高度变化了怎么办你的行高是固定的还是动态的动态行高你用什么方案去估算我当时项目里用的是固定行高配合二段式渲染第一屏先渲染初始数据滚动过程中实时计算startIndex和endIndex然后对不可见区域用占位div撑高度的方式避免表格滚动条高度异常。动态行高这个点我确实没有做特殊处理就如实说了面试官就接着问如果你的数据里有内容特别长的单元格撑高了行高度会导致滚动白屏吗我承认了这块的风险并补充说明在项目里加了ResizeObserver监听行内内容变化如果高度超阈值就强制刷新当前视口范围内的数据。面试官表示这种兜底方案可以接受。他问的第二大块是数据流。因为我的表格支持可配置列、筛选、排序、分组而且配置是需要持久化到服务端的所以数据流设计很关键。面试官问用户在界面上调整了列顺序这个操作产生的状态变化是怎么同步给其他组件的如果用户同时打开了两个浏览器标签页去编辑同一个报表配置你会怎么做第二个问题我确实当时没有仔细考虑过因为项目里没有这个需求。我坦诚说没有做过跨标签页同步但现场想了一下可以给配置对象存一个版本号用visibilitychange事件在标签页激活时重新拉取配置或者用BroadcastChannel做跨标签页通信。面试官听完后说思路是对的实际项目里用WebSocket做服务端推送也可以但需要加上防止用户覆盖的提示机制。第三个大块是工程化相关。我的项目是一个基于Vue 3 TypeScript的组件库面试官问你组件库的按需引入是怎么做的如果引用方只需要表格组件打包的体积能压缩到什么程度这里面涉及到一个点就是很多人组件库打包用了ES Module但最后还是被完整引入了根本原因是没有正确配置sideEffects。面试官问的其实是这个package.json里的sideEffects字段你配置了没有如果配置了为什么还会被全量打包如果没配置就会有什么问题。这个问题我答得比较充实因为实际踩过坑项目里一开始没配sideEffects导致打包出来有200多KB的冗余代码后面配了sideEffects: [*.css]之后配合babel-plugin-import实现按需引入体积才降下来。面试官说这个坑他团队之前也踩过现在他们把核心组件库的按需引入已经做到了极致。简历上还提到用过Web Worker处理大文件上传的切片计算面试官也追问了细节。他问你的worker是怎么进行资源分片的分片大小是怎么定出来的如果计算hash的过程需要和主线程通信你如何限制通信频率避免阻塞主线程我说分片大小是5MBhash计算用的SparkMD5的增量计算方式通过postMessage把进度回传给主线程回传频率做了节流每计算完10个分片才发一条消息。面试官说这个方案里如果涉及超大文件比如5GB在分片循环计算hash的时候把文件读进内存会导致内存溢出建议用FileReader的readAsArrayBuffer配合流式读取或者改用WebCodecs API来做更底层的流式处理。我承认这块是短板回去后查了一下WebCodecs的相关资料确实是我之前没有接触过的新方向。二面最后面试官问了一个开放性问题如果让你设计一个面向酒店商家端的数据看板你会怎么拆解需求和技术方案这个问题我答得比较长思路是先从业务方角度分层第一层是决策者关注的核心指标比如入住率、RevPAR、客户满意度趋势第二层是运营者关心的明细数据比如订单量趋势、退改率、差评分布第三层是系统层面的稳定性、实时性要求和控制面成本。然后对照这些需求去选技术方案数据量小的话用REST接口定时拉取实时性要求高的话要用WebSocket推送图表选型考虑ECharts还是AntV甚至考虑大数据量场景下的降级方案比如聚合服务端渲染成图片再输出。面试官听完后点评说能够从业务价值出发反推技术选型而不是一上来就讨论用哪个图表库这个思路是他比较欣赏的。二面整体下来一个多小时聊得很快因为整个过程都在讲真实做过的事情。我的经验是如果你的项目自己真正搭过核心逻辑而不是纯调接口面试官很快能感受到。反过来如果一份简历里堆了一堆项目但追问下来只有增删改查这种面试其实是很难撑过二面的。4. 三面交叉面与HR面开放性问题背后的考察逻辑三面是交叉面面试官来自另一个事业部可能是机票或者度假那边的资深工程师。交叉面在有些公司就是走流程但在携程这里完全不水问题偏原理深度和场景解决能力不给写代码纯聊。第一个问题是你们项目如何做权限控制的按钮级别权限怎么做我讲了基于自定义指令v-permission的方案通过一个全局指令去判断当前用户是否有权限如果没有就把DOM元素移除。面试官追问了一个我没想到的问题如果用户在DOM中手动加回这个按钮元素或者通过控制台修改了当前用户的权限数据你的方案要怎么规避这个问题很有穿透力因为前端权限本质上只做体验控制安全性必须由后端接口权限兜底。我答了后端接口做二次校验、返回数据时直接过滤无权限字段等方案。面试官说前端权限再怎么封装只能把它当成一种交互策略真正的安全性永远不可以在前端做。第二个问题是关于运行时错误监控的。面试官问你的项目如何收集前端报错如果你的监控代码本身报错了你怎么处理自己的监控代码问题这算是我遇到过最接地气的面试题之一。我说平常项目里用的方案是通过window.addEventListener(error, handler, true)捕获运行时错误通过window.addEventListener(unhandledrejection, handler)捕获Promise异常配合日志上传。但这个方案有个局限就是跨域脚本的错误信息会被浏览器拦截显示成Script error.需要给script标签加crossoriginanonymous属性并且服务器返回Cross-Origin-Resource-Policy响应头。面试官说我的方案比较完整他遇到过一堆人只知道监听error却不知道Script error是怎么出现的。关于监控代码本身的稳定性我们聊了给监控代码做白屏检测如果页面整体崩溃了怎么通过心跳机制上报这个属于比较靠后的优化点能答出来会加分。第三个问题是看你的思维深度的WebSocket为什么可以在服务器端主动推送消息和HTTP长轮询相比它做了哪些不同的事情如果多个WebSocket连接对应的用户分布在不同的机器上你想要给所有用户推送同一条消息这个过程你如何设计这其实是一个分布式消息广播问题不是单纯的前端问题。我答了服务端需要维护一个连接注册中心和订阅关系映射推送时先通过消息中间件做广播各节点拿到消息后再根据本地维护的连接列表去下推。面试官没有在前端面试里把这个追问得太细但这个问题说明中高级前端面试对后端知识的涉猎范围是有要求的。三面后面紧接着就是HR面基本上是聊薪资期望、到岗时间、当前薪资结构。HR还问了一个有点意思的问题如果你是面试官你会招聘什么样的人我答了三点一是基础扎实出了问题能直接定位到原因而不是靠试错二是业务理解能力知道技术是为业务服务的三是不懂就问不会装懂因为装懂的人进了团队坑最深。我说完HR笑了笑说这个答案比较朴实。HR面主要是确认你的意愿度和薪资匹配不会太难但如果之前在技术面有任何一票否决项到这一轮基本已经没戏了所以不用特别担心HR面本身会刷人。5. 我踩过的坑和给后来人的建议面试流程走完之后我在等结果的几天里把整个过程的问答重新梳理了一遍收获还是挺大的。这里想集中写几个具体的经验尤其是那些我踩过坑、面试当下面临时没有答好的地方。第一个坑是简历上写了熟练使用但实际不熟的东西。我简历里写了熟悉TypeScript类型体操面试官其实没有直接问但他问了一个泛型相关的问题type AT T extends string ? ... : ...我虽然答出来了但明显没有那种游刃有余的感觉。面试官可能感受到了没有再往深处追问。恰恰是这个没有再追问让我事后挺紧张的——如果一个点让面试官觉得你只是背了概念但没到熟练的程度整体评价会打折。后来我学乖了简历上写任何技术点之前先自己问自己三个问题我什么时候用的它我遇到的最大坑是什么如果让我重新设计我会怎么改如果三个问题都答不出来那就不写或者改成了解。第二个坑是对项目里的技术选型缺少备选方案思考。二面聊虚拟滚动时面试官问我为什么用固定行高而不是动态测量。我当时的回答是因为实现简单、性能好但其实如果面试官再深挖一句动态行高会怎么影响滚动定位我就会卡住。说白了面试官要的不是你的方案完美无缺而是你能不能在方案A不行的时候说出方案B以及为什么方案B在某些情况下更好。我后来把所有项目里技术选型都推演了一遍备选方案比如虚拟滚动如果动态行高怎么办、状态管理为什么选了Pinia而不选Redux Toolkit、构建工具为什么用Vite而不是Webpack。这些备选方案的思考对面试帮助极大因为面试官一旦顺着你的回答往深处走你就有了地图而不是背了一条路。第三个坑是一个很基础但容易丢分的地方面试官让我口述Array.prototype.reduce的实现原理然后问我如果这个数组是稀疏数组中间有空洞reduce遍历会怎么处理。我直接按常规循环写了一个for循环版本没有处理稀疏数组的情况面试官说这个实现不完全符合规范。后来我查了一下ES规范reduce对稀疏数组的处理是跳过的for循环会原样遍历空槽并报错需要用array.length配合自定义逻辑来模拟。这个点其实日常开发里根本不会遇到但面试官考察的是你对基础API实现是否符合规范的在意程度。我自己复盘的时候意识到基础知识的准备不能只背概念最好去看规范原文或者经典库的源码哪怕只能记住几个关键边界条件也是加分项。给准备面试的同学几个建议项目复盘一定要有数字和对比。比如我的表格从5000行渲染要300毫秒优化到首屏只需要40毫秒这个数字本身就有说服力。面试官一天面很多人能记住的往往是具体数字和结果而不是性能提升了这种模糊表述。手写题不要光写出来就完要在写完以后主动说时间复杂度和边界情况。这次一面手写发订阅中心我写完后主动说了off的实现用filter是O(n)复杂度如果对性能要求高可以用Map存储回调列表来优化面试官当时明显感兴趣。反问环节认真准备不要问公司有没有加班团队氛围怎么样这类问题可以问团队目前最大的技术挑战是什么新人进来后第一个月的期望是什么前者能让面试官觉得你在乎技术成长后者能让面试官觉得你在乎落地价值。要提前了解公司的技术氛围和业务方向。携程的商旅、机票、酒店、旅游这些核心业务线技术栈差异不小如果你的简历项目和对方业务方向贴合通过率会更高。我这次能走到三面和表格项目恰好符合商旅业务的后台管理场景有很大关系。整个过程走下来我最大的体会是前端面试到资深层级已经不怎么考八股文了。面试官更想看到的是——你遇到过一个真实问题想办法解决了并且理解了解决路径上的每一个决策背后的理由。技术方案没有绝对的对错但推导过程最能反映一个人的真实水平。我觉得这次面试对我自己的帮助不只是拿到一个offer更多的是让我看清了接下来要补的几个方向WebCodecs处理流式文件、TypeScript高级类型在项目里的实际应用、以及在系统设计时对跨端同步和权限边界这些非功能需求的考虑。虽然很多知识点我日常开发中真的用不到但提前理解它们能让你在机会来的时候更有底气。
返回列表