十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

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

前端面试八股文:从URL输入到页面展示,构建完整知识网络

前端面试八股文:从URL输入到页面展示,构建完整知识网络 1. 为什么前端面试还要背八股文一个被问麻了的过来人的真实感受先说个我自己的事。去年帮部门做前端终面面到一个简历写了三年经验的候选人。项目聊得不错我问了一个很基础的问题for...in和Object.keys()遍历对象有什么区别对方愣了几秒钟说“这个我平时都是用 for...of”。其实答不上来并不奇怪关键是候选人第一反应不是思考而是条件反射似的跟我说“面试题我背了不少但这个真没遇到过”。这就是八股文最大的问题很多人把它当题库刷却忘了它本质上是一张地图。地图本身不是目的地但你没有地图走到哪儿算哪儿遇到岔路就抓瞎。前端面试里的八股文每一道题背后都对应一个真实的技术决策场景——闭包对应组件内部状态的管理原型链对应继承和实例化方式的选择事件循环对应你写的那段异步代码到底按什么顺序执行。这些从来都不是为了考倒你而是在检验你在真实开发里做选择时脑子里的模型是不是清晰的。而今年这波面试风向尤其明显。从相关的热搜词来看前端面试题、前端八股文汇总、2026前端面试题这几类词的热度一直在线说明大量求职者和在校生都在集中式地刷题。但另一方面热词里出现了“anything-llm 在GitHub上是一个前端应用”“前端使用worker上传大文件”“微前端”“codebuddy常用的前端skill”这类的长尾搜索这说明面试官和候选人都在意识到一个问题纯背已经不够用了得把八股文和工程实践连起来。所以这篇长文不是我列一个题库而是把我脑子里整套前端知识体系怎么互相咬合的按面试官真正想听的逻辑重新讲一遍。为了防止你看着看着迷路先交代一下这篇文章的思路。我不打算按“JS题——框架题——工程题”这种标签分类那样你背完了还是散的。我按的是一条浏览器从输入URL到最后页面可交互的完整链路来串联走完这条链路所有考点都会自然露出来。2. 先把JS底子夯实闭包、原型链、事件循环一张图画清这堆纠缠不清的概念这个标题看起来像劝退但我保证这是全篇最值钱的一章。因为前端八股文里的 JS 题不管包装成什么样子拆到根部就三个东西作用域、原型、事件循环。这三样东西不是孤立的它们互相嵌套就像一张网。2.1 闭包不是“返回一个函数”而是作用域链的留活口很多人背定义函数嵌套函数内部函数可以访问外部函数的变量。这没错但太浅了。当年面试官问我“闭包是什么”我背完定义后面试官紧接着问了一句“那闭包里的变量到底存在哪”当时我就懵了。真实的答案牵扯到执行上下文和作用域链。全局执行上下文在栈底函数每次调用会创建新的函数执行上下文压入栈中。每个执行上下文里有一个[[Scope]]属性指向它的外层作用域。闭包发生时内部函数对象被返回并保持引用它的作用域链没有断所以外部函数的变量对象不会被垃圾回收机制清掉。注意这里说的是变量对象不是整个外部函数。用生活化的比喻闭包就是一间已经退租的办公室里还留着一把原配钥匙。房子外部函数看似已经不可用了但只要钥匙内部函数还在你就能随时打开那个储物柜外部函数的变量。面试时把它往下说一层价值比背书大得多。你可以这么答“闭包的本质是词法作用域链在函数创建时确定和调用时才确定的this不一样。它保证了对上层作用域变量的访问权限同时带来了两个实际影响一是变量可以私有化二是内存不会被立即回收需要在不使用时手动置空引用。”我强烈建议你把下面这段代码手写一遍并且自己边说边写这比背十条定义都管用function createCounter() { let count 0 return { increment: () count, get: () count } } const counter createCounter() counter.increment() counter.increment() console.log(counter.get()) // 2 // 注意count 变量既不能被外部直接赋值 // 也不会因为 createCounter 返回而被销毁如果你能进一步说出“这里的箭头函数让 this 指向外层普通函数的话 this 是 undefined”这个层次那么面试官心里已经给你加分了。2.2 原型链面试官喜欢问“new 一个对象到底发生了啥”原型链的考点最常见的是这三种问法new操作符做了什么手写一个newinstanceof的原理是什么怎么实现继承ES6 class 的继承和原型链是什么关系先说new。网上普遍流传的答案分四步创建新对象把新对象的原型指向构造函数原型执行构造函数并用call绑定this返回值如果是引用类型就返回它否则返回新对象。这个答案可以作为地基但是面试官稍微深挖你就得说出“对象内部的[[Prototype]]指针指向构造函数的prototype对象”这一层。我可以给你一个我总结的手写new比网上的版本更严谨一点function myNew(Constructor, ...args) { // 1. 通过 Object.create 创建一个新对象它的原型指向构造函数的 prototype const obj Object.create(Constructor.prototype) // 2. 执行构造函数this 绑定到 obj const result Constructor.apply(obj, args) // 3. 如果构造函数返回了对象则优先使用它 return (typeof result object result ! null) ? result : obj }关于instanceof它检查的原理是顺着左侧对象的原型链一直往上找看有没有右侧构造函数的prototype。所以obj instanceof Parent为 true不代表obj就是Parent直接创建的它可能是子类的实例因为子类的原型链上挂着Parent.prototype。这个点一定要会说否则对方一句“那我var a []a instanceof Object为什么是 true”你可能会卡住——因为Array.prototype的原型就是Object.prototype链上一直连着。关于继承重点说一个class 的继承本质上是Child.prototype Object.create(Parent.prototype)再加上constructor的修正以及在子类构造函数里先执行super()。而super()干了件很重要的事它会用父类构造函数来初始化子类的实例字段。这就是为什么 ES6 class 继承里子类构造函数里必须先super()再访问this否则拿到的this是未初始化的。2.3 事件循环和微任务一张流程图能讲清楚但我更想让你记住两个反直觉场景事件循环在前端八股文里的地位约等于英语四六级里的高频词。问法一般是说出宏任务和微任务的执行顺序。标准答案先执行同步代码然后清空微任务队列再把下一个宏任务拉出来执行执行完这个宏任务后又清空微任务队列如此循环。但实际面试里真正卡人的是两个反直觉的点。第一是async/await并不是阻塞它会把后续代码包装成微任务。拿这段代码来测async function test() { console.log(1) await Promise.resolve() console.log(2) } test() console.log(3)输出顺序是1 - 3 - 2。你看到的是同步代码先走到await那里把await后面的逻辑交给微任务队列主线程继续往下走console.log(3)微任务队列清空时再执行console.log(2)。第二个反直觉的是setTimeout(() {}, 0)不是“立即执行”而是“至少等到当前任务队列和微任务队列都清空后再执行”。如果你在 React 里写过一个点击事件里面先setState再setTimeout你很容易观察到setState的更新其实可能被 React 批处理掉而setTimeout回调进去的时候拿到的已经是新状态了原因也跟宏任务/微任务隔着队有关。3. 浏览器层面的必考题从URL输入到页面展示缓存、渲染、跨域一次性讲透热词里“前端面试八股文汇总”“前端开发skills”这类词的背后指向一个不变的规律面试官最喜欢用“输入 URL 到页面展示”这道题来串全场。因为这道题可以从网络讲到浏览器原理再讲到渲染最后落到性能优化一题顶十题。你要是自己能把这条链路讲完整面试里基本不会冷场。3.1 完整链路的骨架DNS、TCP、HTTP 请求、解析、渲染、事件触发我把整条链路拆成一个七步骨架你可以按这个顺序记输入 URL浏览器解析 URL判断协议、域名、路径。DNS 解析把域名转成 IP。浏览器会先查本地 DNS 缓存浏览器缓存、系统缓存、hosts 文件没有再向 DNS 服务器发起迭代查询。建立 TCP 连接三次握手如果是 HTTPS 则多一层 TLS 握手包括证书校验和会话密钥协商。发送 HTTP 请求请求行、请求头、请求体服务器处理后返回响应。浏览器拿到 HTML 后解析构建 DOM 树遇到 CSS 构建 CSSOM 树遇到 JS 会阻塞解析这也是为什么 script 通常放底部或加 defer合并成渲染树。布局和绘制计算每个节点的几何位置然后光栅化、合成、显示到屏幕。页面加载完成后触发load事件之后 DOMContentLoaded 早已执行前端逻辑接管。面试时不用死背每一层的细节而是要把每层都挂上对应的考点。比如第三步“三次握手”本身可以引申出“为什么要三次握手”第四步“HTTP 请求”可以引申出缓存策略第五步“CSSOM”可以引申出 CSS 为什么不能频繁用通配符选择器第六步可以引出回流和重绘。这样一步一题面试官会觉得你真的懂而不是背稿子。3.2 HTTP 缓存强缓存和协商缓存别只知道 200 和 304HTTP 缓存的考点本质上问的是你对“资源复用”的理解程度。如果面试官问你“浏览器怎么知道这个资源能不能缓存”你回答“看 Cache-Control 和 Expires”这是及格。想拿高分得把这个表格说完整缓存类型判定的头字段命中后的表现主要场景强缓存Cache-Controlmax-age、Expires直接从本地读不发请求显示 200 (from memory/disk cache)静态资源、图片、字体协商缓存ETag / If-None-MatchLast-Modified / If-Modified-Since发请求服务器返回 304用本地缓存更新频率较低但需要校验的接口禁用缓存Cache-Control: no-cache / no-store每次到服务器校验或重新下载频繁更新的页面、敏感数据我实际项目里踩过一个大坑上线新版本后 UI 样式没变后来发现是两个原因叠加了。一是静态资源文件名没带 hash被强缓存兜住了二是 HTML 页面本身被 CDN 配了Cache-Control: max-age3600导致发版后用户要等一个小时才能看到新页面。后来我把 HTML 改成no-cache静态资源全部带内容 hash发布策略瞬间就稳了。面试时要想显得专业一定要把“文件名带上 hash 来主动更新强缓存”这个说法讲出来因为它是工程里最常见的解法也是八股文和实战的连结点。3.3 跨域CORS、JSONP、代理到底谁在干嘛跨域是被问频率最高的网络题之一。核心原因是浏览器的同源策略页面脚本只能访问同协议、同域名、同端口的数据。但真实项目里前后端分离接口常常在不同域名下所以必须绕过。常见的解决方式我整理如下JSONP利用script标签没有跨域限制的特性动态插入 script 标签向服务器请求数据服务端返回一段调用回调函数的 JS 代码。缺点只支持 GET。CORS后端在响应头里加Access-Control-Allow-Origin等字段浏览器根据这些头来决定是否放行。支持各种方法现代项目的主流方案。分为简单请求和预检请求后者会先发一个OPTIONS请求确认跨域配置。代理开发环境用 webpack/vite 的 devServer.proxy生产环境用 Nginx 反向代理。核心思路是让浏览器以为请求是同源的服务端转发到目标接口。postMessage跨窗口、跨 iframe 通信时用。这里很少有人会往下说一层CORS 里还有个“credentials”的设置。如果你的接口带 cookieAccess-Control-Allow-Origin就不能设为*必须指定实际来源域名并且Access-Control-Allow-Credentials要设为true。我把这个点提出来是因为它是我真实遇到过的“开发环境正常线上跨域报错”的元凶。3.4 渲染机制和回流重绘怎么聊才不像背课本“重排和重绘的区别”这道题标准答案很好背重排reflow是元素几何尺寸/位置变化导致浏览器重新计算布局重绘repaint是元素样式变化但布局没变比如颜色、背景只需要重新绘制。重排必然引起重绘重绘不一定重排。但面试官接下来大概率会追问“怎么减少重排重绘”。这时候你得说几个项目里真能用上的手段而不是背三条耳熟能详的优化口号。我的方案把 DOM 操作批量处理。比如用 DocumentFragment 一次性插入多个节点而不是在循环里 appendChild。用transform代替top/left做动画因为transform可以触发合成器不动布局。缓存布局读取值。比如你在循环里反复读el.offsetHeight每次读都会强制刷新渲染队列造成频繁重排。对复杂动画使用will-change: transform或contain: layout提示浏览器将它提升到独立层。我实际做性能优化时曾把一个长列表拖拽卡顿问题靠把top换成transform直接提升了接近三分之一的流畅度。这种“不光知道是什么还知道怎么用”的答法非常加分。4. Vue 考点根本没变但问法变了响应式原理从 Object.defineProperty 到 Proxy热词里“Vue前端怎么获取天气预报数据”“vue前端开发规范”“前端面试八股文汇总”这些词说明 Vue 仍然是中文前端圈的主流框架。但现在 Vue 的面试题早就不停在“v-if 和 v-show 区别”这种易背题上了面试官更关注响应式原理、diff 算法和异步更新这些底层机制。4.1 响应式原理的版本演变能讲出你的知识陈旧度Vue 2 的响应式核心是Object.defineProperty它会对 data 里的每个属性做 getter/setter 劫持。每个属性维护一个依赖收集器Depgetter 里做依赖收集setter 里触发依赖更新。但 Vue 2 有几个天然缺陷新增属性不响应所以要Vue.set删除属性不响应通过数组索引修改不响应所以要重写数组方法。Vue 3 换成了 Proxy直接代理整个对象可以拦截get、set、has、deleteProperty等操作。所以新增、删除属性都能被追踪。它按需收集依赖不再需要初始化时递归所有属性性能更好而且天然支持数组索引的操作监听。面试官如果问“Vue3 为什么快”你不会只答一个 Proxy 吧。还可以从这几个角度答编译阶段做了静态树提升、事件缓存、动态标记减少不必要的 diff。Proxy 的惰性拦截机制让初始化更快。Fragment 让组件模板可以有多个根节点。源码用 TypeScript 重写类型更安全。4.2 diff 算法的考点key 到底起什么作用Vue 的 diff 是经典中的经典。但大部分人说来说去就是“有 key 可以复用节点提高渲染性能”。这句话对不对对但不完整。面试官要的是你能说出 diff 的具体流程。简化来说Vue 的 diff 是在新旧 VNode 树之间做同层比较。它会从两边的头部和尾部同时开始比较依次处理四种情况头头、尾尾、旧头对新尾、旧尾对新头。如果这四种都不匹配就根据 key 在旧节点中查找可复用的节点找到就移动没找到就新建。整个过程的核心是按 key 建立索引从而避免 O(n^2) 的遍历匹配。如果把 key 随便填成 index问题就来了数组头部插入一条数据后index 全部变了Vue 会认为所有节点都变了要么重建要么把内容错乱地复用。所以面试可以从“key 就是虚拟 DOM 的唯一身份证它决定了节点是被复用还是重建”来切入这段话比背定义要强很多。4.3 生命周期与 nextTick从面试题到真实 bug生命周期题的核心考点是理解每个阶段能干什么。beforeCreate和created之间发生数据响应式初始化beforeMount和mounted之间发生首次渲染beforeUpdate和updated之间是数据更新导致的重新渲染。项目里最常见的坑是在mounted里同步拿 DOM 拿不到或者拿到的宽度不对这时候$nextTick就是救兵。nextTick的原理其实是把回调放到微任务队列Vue 2 里是 microtaskVue 3 里同样是基于 Promise 的微任务。它保证了 DOM 更新完成后执行回调。这个机制的背后正是我前面讲的“事件循环”你改了数据Vue 是异步批量处理的不会在每个数据变更之后立刻更新 DOM而是在同一轮事件循环结束后统一更新。所以你要在 DOM 更新后立刻做点什么就得用自己的回调排队。我记得有一次自己做了一个基于表格滚动的坐标计算方法是在data里更新一个scrollTop然后立刻在mounted里读表格容器的offsetHeight结果读到的永远是旧值。加了$nextTick之后才正常。这就是典型的“背了 nextTick 定义但没把它放在事件循环大局里看”的反面教材。4.4 v-if 和 v-show 背后藏的是渲染开销和初始化开销v-if 和 v-show 的区别人人都知道v-if 是条件渲染不满足直接不渲染v-show 是用 CSS 的display: none隐藏。但推荐场景要说清楚频繁切换用 v-show初始化时不常切换但初始就要使用的用 v-if。因为 v-show 不管初始状态都要渲染一遍v-if 则可以省掉一次首次渲染的开销。从组件层面看v-if 每次切走会销毁组件实例切回来会重新创建所以组件里的生命周期会反复走。v-show 则是一次创建、之后只是样式切换。这个点如果对应到具体业务场景比如 Tab 切换高频切换肯定是 v-show 更合适但需要保持组件内部状态时v-if 反而可能带来期望中的“重置效果”并不全是坏处。这种辩证性的回答会比单纯站队哪个更好要加分。5. 网络场景与工具实战大文件上传、Web Worker、微前端八股文怎么落在业务里这一章我是特意写的。因为最近的热搜词里有几个非常具体的工程关键词“前端使用worker上传大文件”“anything-llm 在github上是一个前端应用”“微前端”“hzero前端开发”“前端 rxdb”。我觉得这释放了一个信号面试官开始把手写“分片上传”这类题当常规题用了因为它既考了 JS 功底又考了网络知识还考了工程经验。5.1 大文件上传分片、并发、秒传、断点续传的完整链路大文件上传的常规做法是先把文件切成若干小块比如每片 5MB然后并发上传多个块服务端按顺序合并最后通知上传完成。这样能避免单个文件过大导致请求超时或内存占用过高。考点在不同阶段分布很广文件切片用File.slice(start, end)切注意Blob.slice的参数单位是字节。上传状态维护用 XMLHttpRequest 或 fetch 的 AbortController 做取消。断点续传没有简单办法必须服务端支持“已存在块”的记录。所以前端要先查一下哪些块已上传只传剩下的。秒传本质不是上传而是基于内容哈希校验服务器发现相同哈希文件已存在直接返回成功。并发控制如果一次性把所有切片都发出去连接数可能爆而且服务端会承压。所以要用一个并发队列限制同时最多 3-5 个请求。说到“并发控制”可以引出一个通用小工具。前端做批量请求的时候Promise.all不是唯一选择我自己常用一个带并发限制的任务队列async function runWithConcurrency(tasks, limit 3) { const results [] const executing new Set() for (const task of tasks) { const promise Promise.resolve().then(task) results.push(promise) executing.add(promise) const clean () executing.delete(promise) promise.then(clean, clean) if (executing.size limit) { await Promise.race(executing) } } return Promise.all(results) }面试重点不是把代码背下来而是能说出“为什么要控制并发”和“怎么控制”这两个核心点。5.2 Web Worker为什么大计算不要阻塞主线程热词里“前端使用worker上传大文件”把 Web Worker 和大文件上传放在了一个场景里。实际上大文件上传里用 Worker 的场景主要是两个一是对文件做 hash 计算比如计算 MD5这是 CPU 密集型操作放在主线程会卡 UI二是切片/合并时的数据预处理。Web Worker 的核心价值是把 JS 放到独立线程执行不阻塞 UI 渲染。它有几个特点不能直接操作 DOM通过postMessage和onmessage通信有自己独立的全局对象可以用importScripts加载外部脚本。不过在 Worker 里做文件处理有一些坑。第一File对象在 Worker 里可以访问但如果你要读文件内容要用FileReader或File.arrayBuffer()需要注意兼容性。第二把大量数据从主线程传给 Worker 是有拷贝开销的如果数据量特别大可以考虑用 Transferable Object把 ArrayBuffer 的所有权转移给 Worker避免拷贝。我做过一个批量图片压缩的项目主线程读文件、丢给 Worker 做 canvas 压缩压缩完再把 Blob 传回主线程。高峰期同时处理几十张 10MB 的图片主线程没有发生卡顿。你要是在面试里能把这个场景讲出来比空说“用 Worker 可以做并行计算”要有说服力。5.3 微前端不是每个团队都要上但面试官越来越爱问微前端几乎是近两年中高级前端面试必问项。我觉得它火的本质原因是大厂内部前端团队复杂、项目迭代太快多个团队需要独立发版、独立维护但又必须在同一个页面壳子里协同。qiankun 是代表方案核心原理包括 JS 沙箱、样式隔离、按需加载子应用。面试回答微前端我建议分三层解决什么问题多团队、多技术栈、独立部署、渐进式迁移。核心原理子应用独立构建母应用通过 route 匹配加载对应子应用的 HTML通过 Proxy 或快照机制隔离全局变量作用域样式通过 Shadow DOM 或自定义前缀处理。代价不是零成本。首屏加载变慢、通信机制变复杂、排错难度上升。如果团队不超过十个前端强行上微前端反而是技术债。我曾经见过一个团队为了微前端而微前端最后把一个十几个页面的项目拆成五个子应用构建产物大得离谱线上排查问题要在子应用和母应用之间来回跳。这个经历让我对“技术选型要先问场景”这句话特别有体会。5.4 前端路由hash 和 history 的原理与差异化选择路由是前端工程化绕不开的话题因为热词里也有“前端路由”相关的隐含需求。hash 路由是用window.location.hash变化来驱动视图更新它不会触发浏览器向服务器请求资源所以兼容性最好、部署成本最低。history 路由利用 HTML5 History API 的pushState和replaceState来改变 URL不刷新页面但需要服务器将所有路由都指向index.html否则刷新页面就会 404。这个知识点里真正能体现水平的细节是pushState本身不会触发hashchange或popstate事件所以现代框架Vue Router、React Router都需要自己维护路由状态监听。此外history 模式有跨域限制不能从 file:// 协议直接运行这也是一些小项目宁可用 hash 的原因。你在面试里如果能补充一句“history 模式下服务端配置 rewrite 是必须的否则刷新子路由会 404”面试官基本就能确定你是真上线过项目的人。6. 工程化与规范前端项目不只是写页面构建、代码规范、组件库设计都要有章法这部分对应的是什么看热词“前端开发规范vue”“前端组件库”“前端项目”“前端seo”这些词说明大家意识到只把八股文背熟并不能直接变成工作能力工程化能力才是“会做事”的关键。所以这一章我讲三块构建工具、代码规范、组件库设计。6.1 构建工具webpack 的底层逻辑你要懂但不能只懂 webpack很多前端新人用 webpack 用了两三年还是只会在配置文件里加 loader 和 plugin。但面试考构建相关的题通常不是让你背诵配置项而是问你理解不理解构建过程的本质。核心要答出这几点打包的本质是把模块依赖图遍历出来把 ES Module、CommonJS 转换成浏览器可执行的代码同时做代码分割、压缩等处理。Loader是转换器比如babel-loader把 JSX/TS 转成 JSstyle-loader把 CSS 转成 JS 模块。Plugin是在构建的生命周期钩子里干活比如HtmlWebpackPlugin自动生成 HTML 文件、MiniCssExtractPlugin提取 CSS、BundleAnalyzerPlugin分析体积。代码分割靠动态导入让首屏加载的 chunk 变小。不过现在面试官更愿意听到的是你对 Vite 的理解。Vite 开发环境用原生 ESM不打包启动快生产构建仍用 Rollup。它的核心思路是用浏览器原生模块能力替代开发阶段的模块打包省掉预打包和转换的时间。你要能解释为什么 Vite 快依赖预构建esbuild、按需编译、利用浏览器缓存。6.2 代码规范commit 规范、lint 和 code review 怎么落地八股文里不考这个但面试官问“你之前项目怎么保证代码质量”的时候这一块是加分项。我自己的实践是三个东西配合ESLint Prettier自动检查语法、风格、错误。Husky lint-staged在 git 提交前跑一遍校验拦住不合格代码进仓库。Conventional Commits提交信息按feat: xxx、fix: xxx的格式来后续可以自动生成 changelog。这里面最容易踩的坑是“ESLint 和 Prettier 冲突”。比如 ESLint 要求双引号Prettier 默认单引号两者就会打架。解决办法是在 ESLint 配置里关闭冲突的规则或者用eslint-config-prettier把格式类规则全部关闭交给 Prettier 处理。6.3 组件库设计从 API 设计到样式隔离支撑“前端组件库”这个热词热词里“前端组件库”出现频率很高。面试如果问你“如果你要从零搭一个组件库你会怎么设计”这是一个开放性问题但对工程能力的考察度极高。我建议从五个维度回答目录结构packages/components 里每个组件一个目录包括组件源码、样式、类型、测试、文档。API 设计组件 props 要尽量少、语义清晰支持受控和非受控两种用法区分默认插槽和具名插槽。样式方案CSS 变量做主题定制BEM 命名规范避免样式污染。构建与发布按需引入要支持 tree-shaking所以源码要用 ES Module 构建不能只发打包后的 UMD 文件。文档和演示Storybook 或者 Vitepress 搭文档站每个组件都给出可交互的 demo。我自己的经验是组件库设计好不好往往在“图标组件”和“表格组件”这两个地方见分晓。图标组件要考虑到按需加载、尺寸、颜色继承表格组件要处理好虚拟滚动、列宽调整、固定列和排序的边界情况。这些细节写出来又是一篇长文但面试时点到为止即可。6.4 前端 SEO不是只有后端能做的事热词里“前端seo”让我想多说两句。很多人以为 SEO 是运营和后端的事其实前端能做的很多。SPA 首屏是空的爬虫拿不到内容所以通常是 SSR服务端渲染或 SSG静态站点生成来解决。但除此之外前端还可以做语义化 HTML标题层级要规范img要写alt。合理的 Meta 信息包括 title、description、keywords、og 标签。结构化数据JSON-LD帮助搜索引擎理解页面内容。页面性能优化因为 Core Web Vitals 里 LCP、INP、CLS 直接影响搜索排名。面试时如果提到“我用 Next.js 做过一个博客站为了 SEO 用了 SSR”这段经历会比堆一堆框架名词有用得多。7. 前端性能优化把“首屏加载”这个故事讲成你自己的项目经验性能优化是前端八股文里最玄幻的板块因为很多人只会背“减少 http 请求、图片懒加载、代码分割”但真问“你们项目首屏加载多少秒优化后降到多少怎么做的”就哑火了。所以这章我换个思路不列优化清单而是带着你走一遍真实项目里最常做的首屏优化流程。7.1 先量化再优化你连指标都没有怎么谈优化优化之前先必须有数据基线。前端常用的性能指标指标含义目标参考FPFirst Paint首次绘制尽量小于 1sFCPFirst Contentful Paint首次内容绘制尽量小于 1.5sLCPLargest Contentful Paint最大内容绘制小于 2.5sTTITime to Interactive可交互时间小于 3sCLSCumulative Layout Shift布局偏移量小于 0.1获取方式很简单Chrome DevTools 的 Performance 和 Lighthouse 就能看线上一般接 Web Vitals。我在实际项目里遇到过一种很典型的首屏慢接口请求过多组件里塞了十几个 fetch导致首屏要等最慢的一个接口返回才能渲染。优化动作不是“把接口并发”而是把首屏必须的数据集中到一个聚合接口里后端一次返回次要数据再分批加载。这一个改动首屏从 4s 压到了 2s 以下。7.2 从传输层下手压缩、缓存、CDN 三件套这部分的八股文答案是“启用 Gzip/Brotli、配置缓存、用 CDN”但我会提醒你三个真实的坑。第一Gzip 压缩的是文本资源对图片、视频没什么效果千万别把所有资源都嘎嘎压一遍。第二CDN 不是越大越好跨区域访问 CDN 也有距离问题而且如果你的资源带 cookieCDN 缓存容易失效。第三缓存策略必须配合文件名 hash否则更新后用户拿旧的你以为是缓存问题其实是文件版本覆盖了但 URL 没变。7.3 渲染层的优化图片、字体、动画我用过最有效的一招是图片懒加载。不是简单加loadinglazy而是根据组件是否进入视口来决定是否渲染图片。用一个很小巧的自定义指令比第三方组件库轻盈得多// 一个极简的懒加载指令Vue 3 风格 const lazyLoad { mounted(el, binding) { const observer new IntersectionObserver((entries) { if (entries[0].isIntersecting) { el.src binding.value observer.unobserve(el) } }) observer.observe(el) } }字体加载也有优化空间。用font-display: swap避免文字不可见或者用 preload 提前加载最关键字体还可以用子集化字体只在页面里引入用到的那几个汉字。我记得做活动页时一个中文字体文件 8MB子集化后切到 200KB首屏直接轻了一大截。8. 关于背八股文的几个真实建议怎么背才能不白背写到最后我没有给你列一道题对应一个答案的题库因为那东西网上一搜一大把。我更想跟你聊聊心态和方法。我见过太多人背八股文背到走火入魔白天背“Vue3 响应式原理”晚上背“HTTPS 握手过程”但一旦把题目放进项目场景就发现没法用。反过来也有一些人什么题都不背只靠写项目结果面试官一问他“那你说说Array.prototype.map和for循环谁快为什么”就答不上来。我的建议是两条腿走路背题是为了形成知识网络不是为了背答案。你每背一道题都要问自己三个问题这个知识点在我项目里出现过吗如果面试官换个角度问我我还能答出来吗这个知识点和其他知识点之间有什么联系比如你背“闭包”可以把它和 Vue 的data函数返回新对象联系起来可以把它和防抖节流实现联系起来可以把它和 React Hooks 的依赖捕获联系起来。一旦能把一道题连到十道题你就不是在背八股文了你是在构建自己的前端知识地图。第二点建议是用输出倒逼输入。我觉得最好的复习方式是给同事或者网友讲一遍。你可以试着写一篇博客把你理解的“Vue3 响应式原理”讲给别人听。写着写着你会发现自己以为懂的概念写出来根本不通顺。这时候回头翻文档、翻源码才是真正的长进。这也是我这篇文章能写出来的原因——边整理边讲比闷头刷题有用得多。第三点也是最后一点面试前花三天集中过一遍“为什么”。以前端路由为例不要只背“hash 和 history 的区别”而是问自己“为什么 history 模式需要服务端配置”因为服务器根路径没有匹配到文件时默认返回 404而前端路由的所有路径都是同一个index.html壳子。一旦回到这个逻辑“部署时配置 rewrite 规则”就不再是死记硬背而是顺理成章的事情。如果你能把八股文背到这个程度那么面试官问你任何一道题你都能从知识网里抽出相关的上下文回答起来有血有肉而不是干巴巴地挤定义。这大概就是“第一次没准备好第二次再来一遍”和“再来一遍依然是白背”的本质区别。
返回列表