尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

2024 Shopee前端笔试复盘:事件循环、缓存与LRU设计题全解析

2024 Shopee前端笔试复盘:事件循环、缓存与LRU设计题全解析 搜“Shopee FE”的时候其实会搜出来不少让人困惑的东西——有人聊的是电商前端岗位有人问的是Doris数据库里FEFrontend节点和BEBackend节点到底怎么分工还有人在讨论“shopee逆向”这种偏灰产方向的词。这里先把话说明白我聊的是2024年秋招Shopee前端工程师FE提前批笔试纯粹站在求职者角度复盘这次笔试的考察思路、题型分布、做题策略以及我考后总结出的备考盲区。先说结论提前批笔试不像很多人想的那样“随便试试、反正还有正式批”恰恰相反提前批往往是最能反映团队当前技术口味的场次。题目不多但每一道都能看出出题人希望你具备什么能力。这篇内容主要给正在准备前端秋招的同学参考尤其是目标锁定大厂、跨境电商或高并发业务场景的人。我会把笔试中遇到的典型题目、我的作答思路、踩过的坑、考后反思全部摊开来讲尽量让后面的人少走弯路。1. 笔试前的情报收集从JD和面经里逆向出考点1.1 先把“逆向”这件事做在正经事上“shopee逆向”这个热搜词我猜大多数人搜的是技术上的逆向工程但我更想把“逆向”用在信息收集上——在参加笔试之前逆向分析公司想招什么样的人比盲目刷题重要得多。我当时做的事很简单把Shopee前端岗位的JD逐条拆开再对照近两年牛客、脉脉、一亩三分地上已有的面经帖把高频考点整理成一张清单。JD里出现最多的关键词是“JavaScript基础扎实”“理解浏览器渲染机制”“有性能优化经验”“熟悉React或其同类框架”“具备良好的工程化意识”。这些关键词直接决定了笔试题的出题方向不会考你背面试题而会考你是否真的理解前端运行时的底层逻辑。这里有一个关键判断Shopee的业务特点是跨境、多语言、多币种、网络环境复杂前端在真实业务里要面对的是弱网、大量列表渲染、国际化文案管理、监控上报等场景。所以笔试题目一定不是单纯的LeetCode刷题而是会把基础题、场景题和算法题混在一起考察你在真实业务环境下的综合能力。1.2 明确考察范围圈定复习优先级根据情报整理我给自己划了四个复习优先级这对于时间有限的秋招党特别重要优先级考点理由P0JavaScript核心机制事件循环、作用域、闭包、原型链笔试和面试最高频一题就能筛掉很大一批人P0浏览器HTTP缓存、渲染流程跨境电商场景网络复杂缓存策略直接决定页面性能P1CSS布局边界情况Flex/Grid响应式方案前端基本功但很多人只会用不会说原理P1算法与数据结构数组、字符串、链表、二叉树一般两道编程题难度中等但边界条件很细P2React或Vue的源码级理解Fiber、diff、响应式依赖收集视岗位方向而定提前批不一定深挖P2工程化Webpack/Vite、CI/CD、代码规范简答题容易出需要能讲出完整逻辑这个清单帮我避开了两个坑一是没有把所有时间花在刷困难算法题上因为前端笔试的算法题通常卡在“中等偏下”难度更看重边界条件的严谨性二是没有忽略基础知识的深度理解因为简答题和不定项选择最喜欢在这些地方埋坑。2. 题型全景与做题顺序先保住确定性的分数2.1 实际遇到的题型构成整套笔试题量不大但时间也不宽裕整体分为三块单选题/不定项选择题、简答题、编程题。选择题大概有10道左右覆盖JavaScript语法细节、CSS布局、浏览器工作原理、网络协议。这里面有不少题是“看起来会一做就错”的类型尤其是不定项选择多选、少选、错选都不得分对概念的精确度要求很高。简答题一般有2道一道偏工程化比如“如何设计一个前端监控系统”或“谈谈你对前端工程化的理解”一道偏场景设计比如“页面加载速度慢如何定位和优化”。编程题一般是2到3道可以在线IDE里写支持JavaScript/TypeScript/Python/Java等主流语言需要自己处理输入输出。难度梯度比较明显第一道相对基础第二道就开始上强度了。2.2 我采用的答题顺序策略我的做题顺序不是从第一题往后做而是先花2分钟把整张卷子扫一遍做三件事确认编程题有几道大致属于什么类型标记出选择题里哪些是知识点盲区先跳过估一下简答题需要多少文字量不要写到一半发现时间不够。然后按照“编程题优先、选择题次之、简答题最后”的顺序来做。原因很简单编程题分值最高、区分度最大而且如果最后写容易因为时间紧张导致思路变形。选择题哪怕靠排除法也能拿一部分分简答题的主观性较强写够要点就能拿基础分放在最后相对安全。实际做题过程中我发现这套策略确实有效。因为第一道编程题虽然基础但需要仔细处理输入解析和边界条件至少花了15分钟第二道编程题需要设计数据结构又花了20分钟。如果先做选择题再做简答很可能编程题会来不及写完那样分数就真的悬了。3. 基础题复盘JS异步顺序、CSS边界布局和缓存判断3.1 JavaScript事件循环与Promise执行顺序的连环坑选择题里印象最深的一道题是关于“async/await、Promise、setTimeout混在一起时输出顺序”的问题。题目大意如下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(promise1); resolve(); }).then(() { console.log(promise2); }); console.log(script end);这是一道非常经典的事件循环题很多人在第一次做的时候都会错。我这里不直接给答案而是分享我的分析思路先区分同步代码和微任务/宏任务。整个script是一个宏任务同步代码按顺序执行遇到await时右侧表达式会立即执行函数剩余部分被包装成微任务遇到Promise的then也是注册微任务setTimeout注册宏任务。所以同步输出的顺序一定是script start、async1 start、async2、promise1、script end然后执行微任务队列中的async1 end、promise2最后执行宏任务setTimeout。这类题目的关键在于你必须要知道await后面的代码等同于Promise.then的微任务而不是同步执行。很多人在第一次做的时候会认为async1 end在async2之后立刻输出这就是最大的认知偏差。我在复习时专门把微任务和宏任务的注册时机、执行时机画在一张表里反复推演了三遍才彻底清楚。还有一道选择题考了“闭包和var/let的区别”核心是for循环里用var声明的变量在setTimeout回调中的输出结果以及改成let之后的结果差异。这种题考的不是会不会写闭包而是知不知道var是函数级作用域、let是块级作用域以及闭包捕获的是变量本身而不是值。3.2 CSS布局题Flex和Grid的边界情况CSS部分让我意外的是没有考简单的垂直水平居中而是考了“Flex布局中子元素在容器空间不足时的收缩行为”和“Grid网格中隐式轨道的尺寸如何确定”。先说Flex收缩问题。题目给了一个容器宽度固定里面有三个子元素分别设置了flex: 1 1 200px、flex: 2 1 200px、flex: 0 1 200px问在容器总宽小于600px时三个子元素各占多少宽度。这道题表面考的是flex-grow、flex-shrink、flex-basis三个属性的综合作用实际上考的是flex-shrink的计算规则。当容器宽度不足时flex: 0 1 200px这个子元素不参与收缩保持200px其余两个子元素按照各自的flex-shrink比例分摊剩余空间。但要注意缩小的基础是flex-basis而不是子元素的内容宽度。如果对flex缩写三个值的作用不熟这道题基本靠蒙。Grid隐式轨道这道题也很有代表性定义一个三列网格但放入了四个子元素问第四个元素放在哪里以及隐式行的高度如何确定。很多人只记得grid-template-columns定义了显式轨道却忽略了一旦内容超出显式轨道就会创建隐式轨道隐式行高默认是auto。这个知识点在中后台表格类业务场景里非常常见考得一点也不超纲。3.3 浏览器缓存的优先级判断网络和性能这块考了HTTP缓存但问得很细一个带Cache-Control: no-store的响应同时响应头里带了ETag和Last-Modified浏览器下次请求时会不会走协商缓存答案是不会。no-store表示完全不缓存那么ETag和Last-Modified根本没有机会发挥作用。与之容易混淆的是no-cache它表示可以存储但不能直接使用每次需要回源校验。很多人把这两个单词搞混认为no-cache就是不缓存实际上恰恰相反。还有一道概率较大的题是强缓存与协商缓存的组合判断给了一个带Cache-Control: max-age600和ETag的响应问10分钟内再次刷新页面网络请求是否会被发起。答案是不会强缓存命中时浏览器直接从本地读取不发请求。但如果用户手动刷新F5行为又会不一样浏览器可能会带上If-None-Match头进行协商缓存。这些细节如果只看概念不实际抓包验证很难真正理解。3.4 简答题的答题框架简答题我遇到的一道是关于“首屏性能优化”这不是一道有标准答案的题而是看你能不能把问题拆解完整。我的作答思路是先定义问题边界——“首屏”到底是页面完全渲染还是用户可交互还是主要视觉区域出现接着从指标定义出发FCP、LCP、TTI再按“服务端优化、网络优化、资源加载优化、渲染优化”四个维度展开。这里有一个很重要的经验简答题不要只写要点一定要有“为什么”和“怎么做”。比如“资源加载优化”不能只写“开启CDN、开启Gzip”要补充为什么CDN对跨境电商场景尤其重要——因为用户分布在多个国家CDN边缘节点可以大幅降低跨国链路的RTTGzip对文本类资源压缩比很高能减少传输体积。把这些讲清楚简答题分数就不会低。4. 编程题全记录从滑动窗口到缓存设计4.1 第一道算法题滑动窗口求无重复字符的最长子串这道题应该是LeetCode原题“无重复字符的最长子串”难度中等很经典但笔试环境里写和本地IDE写完全是两回事。一是不能自动补全二是要自己处理输入输出三是有时间压力。我的解题思路是滑动窗口加哈希集合。维护一个左指针和一个右指针右指针不断向右扩展把字符加入集合当遇到重复字符时移动左指针不断从集合中删除左指针指向的字符直到重复字符被移除。每一步都更新一次最长长度。function lengthOfLongestSubstring(s) { const set new Set(); let left 0; let max 0; for (let right 0; right s.length; right) { while (set.has(s[right])) { set.delete(s[left]); left; } set.add(s[right]); max Math.max(max, right - left 1); } return max; }这道题的关键不是记代码而是理解为什么left要逐字移动而不是直接跳到重复位置的下一位——用Set时没法精确知道重复字符的位置所以只能逐字缩。如果追求更低的时间复杂度可以用Map记录每个字符最近一次出现的位置left可以直接跳跃。笔试时能用最稳妥的Set解法写出来并正确通过测试用例比追求最优解更重要。我在本地测试时花了几分钟处理边界情况空字符串返回0、单字符串返回1、全是重复字符返回1。这些看起来简单但在线编译环境里一旦少了一个边界判断就会导致部分测试用例不过丢分非常可惜。4.2 第二道算法题实现一个带过期时间的缓存这道题不是纯算法题而是偏向设计题很符合业务场景。题目要求实现一个类支持get和put两个方法每个key可以设置过期时间过期后get返回-1缓存容量有限超出容量时按照某种策略淘汰题目里指定了LRU。看到这道题时我意识到它考察的真不是LRU本身而是“如何在O(1)时间内访问和淘汰数据”。标准的做法是哈希表加双向链表。哈希表负责O(1)查找双向链表负责维护访问顺序每次访问一个节点就把它移动到链表头部淘汰时从链表尾部移除。class LRUCache { constructor(capacity) { this.capacity capacity; this.cache new Map(); this.expire new Map(); } get(key) { const now Date.now(); if (this.cache.has(key)) { if (this.expire.get(key) now) { this.cache.delete(key); this.expire.delete(key); return -1; } const value this.cache.get(key); this.cache.delete(key); this.cache.set(key, value); return value; } return -1; } put(key, value, ttl 0) { const now Date.now(); if (this.cache.has(key)) { this.cache.delete(key); this.expire.delete(key); } if (ttl 0) { this.expire.set(key, now ttl); } else { this.expire.set(key, Infinity); } this.cache.set(key, value); if (this.cache.size this.capacity) { const oldestKey this.cache.keys().next().value; this.cache.delete(oldestKey); this.expire.delete(oldestKey); } } }这里有一个必须在笔试中注意的点题目里的过期时间和LRU淘汰机制是有冲突的。比如一个key还没过期但很久没被访问它是应该被LRU淘汰的而一个key即将过期但刚被访问过它又应该被保留到过期时间再失效。处理这种冲突时我是按照“先判断过期过期则删除未过期则更新LRU顺序”的顺序来的这样语义最清晰。而且JavaScript的Map在插入新元素时会把键放在迭代顺序的末尾删除再重新插入同一个键也能把它移到末尾。这就是为什么用Map也能模拟LRU的原因。面试官如果追问底层实现我就再讲双向链表但笔试阶段Map方案已经足够。4.3 笔试环境中的输入输出与边界处理在线编程和本地写的最大区别是输入解析。很多前端同学平时写算法题直接写函数体到了笔试平台发现还要自己拼解析逻辑一下子慌了。我的习惯是先把输入读完用fs.readFileSync(/dev/stdin, utf8)或readline模块处理多行输入然后把每行数据转成目标类型先跑一个最小测试样例确认解析无误再写核心逻辑。千万不要一上来就写function xxx()然后发现根本没把输入接进去。另一个经验是如果题目没有明确说输入一定是合法的就要在代码里加上防御判断。比如判断数组越界、判断空字符串、判断数字范围。这些代码看似多余但在笔试自动化判题里多一行判断可能就多过几个测试用例。5. 考后的诚实复盘失分点、盲区和下一次改进5.1 选择题里我真正失分的点考后我第一时间把选择题里拿不准的题目记下来回到电脑前逐题查资料验证。发现失分主要集中在两个方向一是ES Module和CommonJS的差异细节二是React合成事件与原生事件的执行顺序。ES Module和CommonJS的差异我知道一个是静态导入、一个是动态加载知道同步和异步的区别但笔试里考到了“在CommonJS模块内修改module.exports对象的内容对调用方的影响”以及“ES Module的实时绑定意味着什么”这两个细节我都答得不够精确。实时绑定说直白点就是导入方拿到的不是值的副本而是一个指向导出模块内部绑定的引用导出方后续修改的值导入方也能读到。这个特性和CommonJS的复制导出值有本质区别。React合成事件和原生事件同样容易踩坑。React 17之后事件不再挂载到document上而是挂载到根容器上并且合成事件的执行顺序是在原生事件冒泡到根容器之后。笔试里问了一个场景在某个DOM节点上同时绑定了React合成事件的onClick和原生addEventListener的click点击该节点时哪个先触发。很多人想当然认为原生事件先执行但补了React 17的变化之后这个结论就要分情况讨论。这类细节不实际写demo验证真的容易记错。5.2 简答题的篇幅控制和时间分配失误我在简答题上犯了一个几乎所有人都容易犯的错写得太长了。第一道简答题我为了体现专业性写了完整的性能优化方案包括CDN配置策略、图片压缩方案、懒加载实现、骨架屏设计甚至还画了文字版的流程图。结果第二道简答题只写了大纲级别的回答显得很单薄。考后反思简答题的评分大概率是踩点得分而不是按字数给分。写得再长如果后面的题没写或者写得很浅总分反而会受影响。正确的做法是每道简答题控制在300到500字用小标题或者序号把要点列清楚每个要点一到两句话解释不要展开成长篇大论。如果时间充裕再在最后补一个“如果有需要我可以进一步展开”的说明给面试官留出追问空间。5.3 如果让我重来一次我会调整什么第一我会在复习阶段增加对“设计类编程题”的专项训练。这次的LRU缓存题就是一个信号前端笔试已经不只是考纯算法了而是开始结合业务设计中高频使用的数据结构。像“实现一个带防抖和节流功能的函数”“实现一个可取消的Promise”“实现一个支持并发限制的请求池”这类题目以后会越来越多。第二我会花更多时间在HTTP缓存的实际验证上而不是只看文档。用Node起一个本地静态服务在响应头里分别设置Cache-Control: max-age、no-cache、no-store再到浏览器Network面板里观察请求和响应亲手验证强缓存命中、协商缓存304、完全不缓存这三种状态的差异。记忆的牢固程度和只看文章完全不一样。第三我会把“表达训练”纳入复习计划。笔试里简答题的最终目的是让阅卷人看到你有清晰的思路这不是临时发挥能写好的需要平时就练习用简洁的语言概括技术方案。我在这次笔试里就发现有些技术点我心里明白但用文字表达出来就变得啰嗦且没重点。最后分享一点个人体会整个2024年Shopee FE提前批笔试给我的感觉是不偏不怪但也不轻松。所谓的“提前批”题目反馈出来的信息是团队希望找到基础扎实、能处理真实业务复杂度的人而不是刷题机器。三轮题做下来最核心的能力其实是“在有限时间内把你已经掌握的东西稳定地表达出来”。如果你正在准备下一年的笔试我的建议是与其猜题不如把自己当成一个要解决真实前端问题的工程师多用“为什么这样设计”的视角去审视每一个知识点。这次笔试里让我卡住的从来都不是那些冷门偏题而是我自认为早就掌握、但从来没往深想过的日常概念。把这些日常概念真正吃透比多刷一百道题更有用。
返回列表