ARTICLE DETAIL

资讯详情

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

前端性能优化面试指南:从指标到监控的闭环

前端性能优化面试指南:从指标到监控的闭环 前端面试进入“铜九铁十”这个节点后台收到最多的私信就是问性能优化怎么答。说实话性能优化在面试里的地位一直很微妙基础题问烂了但想把“用过哪些优化手段”聊出信息量很多同学会卡在“知道一堆名词讲不清为什么这么做更拿不出量化结果”。金九银十是求职黄金期铜九铁十更像前端圈自嘲式的硬核竞争——机会不少但筛人的问题越来越刁钻。这篇文章算是我这些年面试候选人和自己准备跳槽时针对性能优化整理的完整思路。不打算罗列一堆“八股文”背诵点而是从指标、网络、渲染、运行时、构建、监控、高频追问七个维度把这条知识链串起来。无论你是刚开始准备面试的应届生还是想系统补漏的进阶开发应该都能找到能直接用上的东西。1. 先把优化目标钉死性能数字不是背给面试官听的1.1 Core Web Vitals 与 2026 年的新常态很多面试者一上来就说“我做过性能优化做了懒加载、压缩图片、webpack优化”。问题在于面试官没办法从这些零散动作里判断你是否理解“性能优化到底为了什么”。性能优化的最终目标是用户体验而体验需要量化。2024年之后Google 把 Core Web Vitals 的指标体系更新了一轮最核心的三个变成了 LCP、INP、CLS。这里有个需要更新的点以前大家张口就是 FID但 FID 只度量首次可交互前的输入延迟且依赖用户真实点击数据噪声大后来 Google 用 INPInteraction to Next Paint替换了它。INP 采集用户在页面整个生命周期中所有点击、键盘操作的长任务延迟最终取一个近似 P75 的值作为整体响应能力指标。面试要记住阈值但更要理解为什么是这三个LCPLargest Contentful Paint最大内容绘制目标是 2.5 秒以内。它回答“用户能不能看到主要内容”的问题。图片、视频、文字块都可能是 LCP 元素需要针对具体元素做优化而不是笼统说“首屏快”。INPInteraction to Next Paint下一次绘制交互延迟目标是 200 毫秒以内。它衡量的是页面能不能快速响应用户操作和 JavaScript 长任务、主线程繁忙强相关。CLSCumulative Layout Shift累计布局偏移目标是 0.1 以内。它衡量视觉稳定性比如图片没有预留宽高、字体加载导致文字跳动、组件插入导致页面塌陷都会拉高 CLS。这三个指标背后对应的是加载性能、交互性能和视觉稳定性正好覆盖了用户体验最关心的三个维度。我在面试里常让候选人讲“你们项目这三个指标是多少”大多数人答不上来。如果你能脱口而出自己负责模块的 LCP 是 2.1s、INP 是 180ms、CLS 是 0.05面试官对你的印象会立刻不一样——因为这代表你真正做过线上测量而不是只在文档里看过概念。1.2 实验室数据与现场采集Lighthouse、WebPageTest 和 Performance API性能指标怎么来其实分两类实验室数据和真实用户监控RUM。实验室数据用 Lighthouse 或 WebPageTest 模拟固定网络环境和设备采集适合开发阶段回归RUM 则从真实用户浏览器上报覆盖更真实但噪声也更多。Lighthouse 几乎是前端面试必提工具。但很多候选人只知道“跑一下出个分”说不清分数怎么来的。Lighthouse 本质上是跑一个 Chromium 实例模拟移动端慢网络比如 Fast 4G从导航开始记录性能时间线再把各类指标乘以权重算出一个综合分。所以你会发现同一页面在不同网络下跑分数差很多因为模拟环境是一致的实际页面加载分布则更复杂。WebPageTest 是更专业的实验室工具能选全球不同位置的真实浏览器查看完整的 Waterfall 时间线、TTFB、每个请求的细节还能做视频回放看页面是怎么逐步渲染出来的。如果你要排查“到底哪个请求拖慢了首屏”WebPageTest 比 Lighthouse 直观得多。如果要在代码里做更精细的采集Performance API 是绕不开的。比如const nav performance.getEntriesByType(navigation)[0]; if (nav) { console.log(DNS耗时:, nav.domainLookupEnd - nav.domainLookupStart); console.log(TCP耗时:, nav.connectEnd - nav.connectStart); console.log(TLS耗时:, nav.secureConnectionStart ? nav.secureConnectionStart - nav.connectStart : 0); console.log(首字节耗时TTFB:, nav.responseStart - nav.requestStart); console.log(DOM完整加载:, nav.domContentLoadedEventEnd - nav.navigationStart); console.log(页面完全加载:, nav.loadEventEnd - nav.navigationStart); }PerformanceObserver 则更适合监控长任务和资源时序比如用 PerformanceObserver 监听 longtask把超过 50ms 的任务收集下来上报这能直接定位主线程瓶颈也是后面讲运行时优化和监控 SDK 的基础。1.3 没有性能预算的优化都是耍流氓给项目定性能预算是我非常推荐在面试和简历里提到的点。原因是它能把“性能优化”从一次性活动变成可持续流程。性能预算可以分两类指标预算比如 LCP 不超过 2.5sINP 不超过 200msCLS 不超过 0.1。这个可以用 Lighthouse CI 或者自建的 Performance Budget 工具在 CI 里跑。资源预算比如首屏 JavaScript 体积不超过 170KBgzip图片总大小不超过 500KB第三方脚本不超过 2 个。有了预算才能防止“下个版本回归”。我经历过一个项目做了两个月的性能优化首屏从 6 秒压到 3 秒结果新同事加了一个地图 SDK 和一个埋点脚本又回到 5 秒。后来我们直接在 CI 里加了一个简单的体积检查超过预算就失败。虽然偶尔会被“关掉”重新调预算但至少每次都有讨论而不是无声无息地退化。这一点面试官很爱听说明你有工程化思维不是单纯“调优”。2. 网络与资源层的提速连接阶段省下的每一毫秒2.1 DNS、TCP/TLS 预连接到底该怎么配面试经常从一个非常经典的环节切入“从输入 URL 到页面展示你能说出几个优化点”大多数人回答到“DNS 解析、TCP 连接、HTTP 请求”但不知道怎么落地。DNS 解析在连接链路里的耗时不可忽视。每次域名解析少则几十毫秒多则上百毫秒。浏览器的 DNS 缓存可以帮助但首次访问或子域名较多的站点仍然需要实际解析。前端能做的动作是link reldns-prefetch让浏览器在后台提前解析关键域名。但 dns-prefetch 只做 DNS 解析如果你估计到后面还要建立 TCP 连接甚至 TLS 握手那就要用 preconnect。link relpreconnect会提前完成 DNS 解析 TCP 握手 TLS 握手。尤其你的页面要请求第三方 API、上传下载文件、加载字体或地图等跨域资源时preconnect 的效果非常明显。一个典型案例页面调用同一个云厂商的 API 网关TTFB 经常在 300ms 以上。加了 preconnect 后DNSTLS 环节从关键路径里被隐藏了TTFB 降到了 180ms 左右。注意 preconnect 也不要滥用因为它会占用并保持连接如果后续资源没有使用反而浪费浏览器的连接池。我一般只对 “首屏一定会请求且尽快需要” 的一两个域名使用。面试时你可以补充一个点preload 和 preconnect 容易混淆。preload 是提前下载当前页面需要的某个具体资源告诉浏览器 “这个任务要优先处理”preconnect 是提前建立连接并不下载资源。两者配合得当能明显改善关键资源的加载顺序。2.2 HTTP 缓存强缓存、协商缓存与前端版本号的默契网络层优化里缓存是性价比最高的动作。面试必问“强缓存和协商缓存的区别”但很多人只会背概念。我建议结合工程实践来回答。强缓存由Cache-Control: max-age31536000控制浏览器在 max-age 内不会再发请求直接走本地缓存。协商缓存则由ETag或Last-Modified控制浏览器先发一个请求给服务器服务器判断资源没变返回 304响应体很小但仍然有一次网络往返。对于静态资源最佳实践是文件名带 hash然后设置Cache-Control: max-age31536000, immutable。因为文件名变化相当于新 URL浏览器自然发起新请求不需要关心“缓存更新”问题。带immutable告诉浏览器在有效期内绝对不要去服务端验证。这是前端工程化里最常见的“hash 长期缓存”模式。但这里有一个我踩过的坑很多人只在 Nginx 层配置了etag on和last_modified on却没有处理好 HTML 的缓存策略。HTML 上设置了max-age3600导致 CDN 或浏览器缓存了 index.html用户部署新版本后刷新页面拿到的还是旧 HTML里面的 JS/CSS hash 全是旧的这就是典型的“缓存吞噬发布”。正确做法是 HTML 设置Cache-Control: no-cache让它每次回源验证但静态资源使用长期缓存。面试追问可能会聊到 Service Worker你可以顺带提一下stale-while-revalidateService Worker 拦截请求后先返回缓存内容再在后台更新缓存下次访问就是新内容。这个策略很适合非首屏接口和图片资源。2.3 图片字体传输层的“隐身术”压缩、响应式与三协议网络资源的主体往往是图片。图片优化的核心不是“压缩一下”而是按需提供合适尺寸和格式。响应式图片用srcset和sizes属性让浏览器根据当前视口宽度选择最合适的图片 URL。如果你用 CDN 且支持图片处理参数可以在 URL 上动态指定宽度和压缩比例。现代格式方面WebP 已经是大势所趋AVIF 更小但要考虑兼容性。我可以提供一个选择逻辑大尺寸图片、轮播图、背景图优先考虑 WebP/AVIF同时保留 JPEG fallback。图标和小 PNG直接转成 SVG 雪碧图或 iconfont。背景图片能切图就不要传整张大图按 1x/2x 输出。字体加载是常被忽略的 CLS 元凶。字体文件没加载完浏览器会先用 fallback 字体渲染等到 Web Font 下载完成再切换这直接导致文字区域宽度和行高变化。解决方案包括字体子集化比如用 fontmin 只保留用到的字符、使用font-display: swap虽然会闪字体但避免白屏、利用preload加载关键字体文件。更进一步的做法是把字体做 encode 到 CSS 里但那只适用于小体积字体通常几百 KB 的字体不建议这么干。传输层协议方面HTTP/2 多路复用已经普及把多个小请求并发到一个连接里。HTTP/3基于 QUIC还在逐步普及面试提到“为什么 HTTP/2 还没彻底解决队头阻塞”可以加分。HTTP/2 的多路复用只在连接层面消除了队头阻塞但 TCP 层仍有丢包重传的队头阻塞HTTP/3 改成 UDP QUIC 后进一步优化。不过面试别硬背核心是展现你对协议演进的理解能落到资源加载策略上。3. 渲染链路优化的硬功夫从 FCP 到 INP 的每一帧3.1 关键渲染路径哪些资源在“绑架”首屏面试题目里最经典的就是“关键渲染路径”。你需要画出一条链路HTML - DOMCSS - CSSOM两者合并成渲染树然后布局、绘制、合成。资源阻塞是重点CSS 是渲染阻塞资源。浏览器在构建 DOM 的过程中遇到link relstylesheet会暂停脚本执行但不会完全阻止 DOM 构建不过 CSSOM 构建完成之前渲染树无法构建所以默认情况下页面不会首帧绘制。因此首屏 CSS 体积必须控制并用媒体属性mediaprint把非视口样式变成非阻塞加载。JavaScript 是解析阻塞资源。普通script会暂停 HTML 解析并且要等前面的 CSSOM 构建完成才能执行。所以常用defer或async。defer保证脚本按顺序在文档解析完成后执行async一旦下载完就立刻执行适合完全独立的第三方脚本。字体和图片会触发重排或延迟加载但不会阻塞首次渲染。我还遇到过面试官问“为什么现在 ES Module 里script typemodule默认是 deferred”因为模块脚本需要先解析依赖浏览器将其设计为默认不阻塞 HTML 解析并且一定会延迟执行这跟defer的行为一致但更严格。优化首屏常见手段是提取关键 CSSCritical CSS把影响首屏样式的 CSS 内联到 HTML 中其余样式文件交给preload异步加载。不过这个方案在工程化里要权衡维护成本。我一般建议用工具自动生成关键 CSS并在 CI 里校验体积。3.2 重排、重绘与合成层的日常操作细节面试环节喜欢让候选人举例说明“如何避免重排重绘”。这里的关键不是背概念而是理解浏览器渲染流程。每次修改 DOM 样式浏览器都会经过 Style - Layout - Paint - Composite具体步骤因属性而异。重排Layout代价最高因为它涉及几何计算会影响子节点和兄弟节点。重绘Paint次之因为需要绘制像素。合成Composite代价最低因为只把已绘制的图层交给 GPU 交叉合成。所以优化的基本原则是能把操作限制在合成层就不要触发重绘能重绘就不要重排。实操清单用transform和opacity做动画不要用top/left/width/height前者走合成器后者走布局绘制。批量读操作、批量写操作避免“读-写交替”导致强制同步布局。比如循环里读offsetWidth再改样式会造成每一轮都要重新布局一次。需要修改多个样式属性时用classList切换一个 CSS class而不是逐个设置style。大量插入 DOM 时使用DocumentFragment或者在display: none的容器里操作完成后一次性显示。will-change可以提前提示浏览器生成独立图层但不能滥用否则 GPU 内存爆炸。这套知识在 Vue/React 项目中依然有效不要以为框架的虚拟 DOM 帮你解决了所有性能问题。虚拟 DOM 优化的是 JavaScript 层面的 diff但最终还是要操作真实 DOM。如果你在 Vue 里直接document.getElementById(app).innerhtml ...再多的虚拟 DOM 也救不回来。3.3 大数据量渲染虚拟列表与时间切片面试常见场景题“后端返回一万条数据前端怎么渲染”如果你直接渲染一万个 DOM 节点页面会卡成幻灯片。结合真实项目经验我会按数据量和交互复杂度分几层回答最简单的方案是分页或加载更多后端一次只给一页。如果必须支持滚动连续加载用虚拟列表只渲染可视区域内的节点上下各加一个缓冲区滚动时通过计算scrollTop和每项高度来更新渲染范围。核心思路是“窗口化渲染”而不是把一万个节点全挂上去。如果业务复杂比如每行有大量子组件和状态考虑把“行容器”固定高度再配合content-visibility: auto让浏览器自动跳过视口外的渲染工作。content-visibility是一个非常实用的 CSS 属性但要注意它和懒加载、虚拟列表的关系不要重复使用。另一个面试热点是“如何解决大数据量一次性渲染导致主线程卡顿”。除了虚拟列表还可以用时间切片把任务切成小块每执行 5ms 左右就让出主线程让浏览器有机会响应用户输入和渲染帧。基础实现是requestIdleCallback配合工作队列或者直接setTimeout(…, 0)。React 里的useTransition和useDeferredValue就是时间切片的框架级实现底层依赖 Fiber 的可中断渲染。这一块如果能在面试中自然带出来说明你不仅知道手写性能方案还理解现代框架的设计方向。4. JavaScript 运行时和内存深水区别把主线程跑满4.1 事件循环与长任务让出主线程比写得更快更有效前端性能的瓶颈越来越集中在“主线程过忙”。浏览器的主线程既要执行 JavaScript又要处理样式、布局、绘制还要响应键盘鼠标事件。一旦某个任务执行时间超过 50ms它就被称为“长任务”用户会明显感觉到卡顿。优化长任务的核心不是“把函数写得更快”而是“把任务切开让出主线程”。常见手段setTimeout(() chunks[i], 0)把大循环拆成多个宏任务。用MessageChannel做微任务/宏任务级别的调度性能比setTimeout(0)更好因为setTimeout嵌套超过 5 层后会有至少 4ms 的节流延迟。现代浏览器支持scheduler.postTask和scheduler.yield可以按优先级调度但目前兼容性仍需关注。使用 Web Worker 把纯计算任务放到独立线程比如大量数据处理、Canvas 像素计算、文件上传前的 hash 计算、JSON 解析等。注意 Web Worker 里不能操作 DOM但可以和主线程通过postMessage通信。面试官问到“Web Worker 在什么场景用过”不要说“我了解”最好能给出一个具体场景。比如我做过“前端使用 Worker 上传大文件分片”主线程读取文件分片把每个分片交给 Worker 计算 hashWorker 再把 hash 返回主线程触发上传请求。这样主线程不会因为计算 hash 卡死用户还能继续操作页面。4.2 V8 的一些“潜规则”与防抖节流手写题V8 引擎对 JavaScript 的优化也有“反直觉”的地方。我整理几个面试可以用上的点对象形状hidden class。V8 会为对象动态隐藏类。如果你频繁给对象增减属性会导致隐藏类变换降低内联缓存命中率。所以推荐初始化时就把所有属性定义好不要再delete obj.name。数组越界和稀疏数组。越界读写会导致数组变成字典模式性能骤降。不要随意留洞比如arr[1000] 1会让 V8 放弃快速数组。函数优化。V8 会对高频函数做内联缓存但如果你参数类型不稳定比如同一个函数一会儿传字符串一会儿传数字优化就会失效。TypeScript 的好处不只是类型提示还在于帮助避免“隐藏类”破坏。防抖节流是“前端面试手写题”里的常客。很多人会背代码但面试官更想听边界情况。防抖的核心是“事件触发后等待一段时间再执行如果期间再次触发则重新计时”节流是“固定时间间隔内只执行一次”。防抖实现时要注意“立即执行”选项和“取消”能力。节流则有两种方式时间戳版立即执行和定时器版延迟执行各有副作用。我在实际项目里更推荐基于requestAnimationFrame实现节流因为 rAF 在页面不渲染时会自动暂停更适合动画和滚动场景。能写出带leading和trailing控制的版本会是一个很好的加分项。4.3 内存泄漏排查从 DevTools 到真实案例内存泄漏会让页面越用越卡在移动端更容易触发崩溃。面试问“如何排查前端内存泄漏”时不要只说“用 Chrome DevTools 看 Heap Snapshot”而要描述一套可执行的排查流程打开 Performance Monitor观察 JS 堆大小和 DOM 节点数是否随时间持续上涨。用 Memory 面板录制 Heap Snapshot过滤Detached。如果出现大量分离的 DOM 节点基本可以确定是事件监听器或闭包引用导致节点无法回收。结合 Performance 面板的长任务记录找哪些事件触发了大量内存分配。常见泄漏源头我总结过全局变量。window.foo data忘了清理。定时器和定时器里引用的外部对象。比如组件卸载后setInterval还在执行回调里的 DOM 引用就永远不会释放。事件监听器未移除。尤其自定义事件、window.addEventListener(resize, handler)但组件销毁时没有removeEventListener。闭包持有一棵大型 DOM 树。React/Vue 中未清理副作用。React 的useEffect如果设置了定时器就必须在 cleanup 里清除。Vue 的onUnmounted同理。此外2023 年之后的浏览器新增了WeakRef和FinalizationRegistry可以排查对象是否被回收。不过这个 API 目前更适合框架级工具使用普通业务代码不建议碰容易增加心智负担。5. 构建与工程化把性能优化嵌进发布流程5.1 包体积从 3MB 到 1MB我做了哪几件事构建层优化是面试“项目难点”的最佳素材。我拿一个真实案例拆解某个后台管理系统首屏 JavaScriptgzip 前3.2MBLCP 长期在 4.5s 左右。我做了四件事最终 1.1MBLCP 降到 2.1s。第一用webpack-bundle-analyzer如果是 Vite 可以用rollup-plugin-visualizer看体积分布。我发现moment占了 400KBecharts全量引入占 800KB还有几个工具库被重复打包。于是把moment替换成dayjs体积减少 90%echarts改成按需引入只保留用到的折线图和柱状图模块。第二检查有没有重复的依赖版本通过resolve.alias或peerDependencies强制统一。第三把 React/Vue 这类基础库改成 CDN 引入并在externals里声明这样项目的 vendor 包可以瘦身一大块。但要注意CDN 引入会带来第三方不可控和服务稳定性问题我建议只在公司内部自建 CDN 或选择可靠性很高的公共 CDN 时做。第四对路由做代码分割每个路由单独打包并配合prefetch预加载下一个可能访问的页面。关键点是每个动作都要有数据依据不要“凭感觉拆”。面试官问你为什么不用xxx你要能说出当时的取舍。5.2 Tree Shaking、sideEffects 与按需加载的坑Tree Shaking 是大家都知道的构建优化但实操里经常失效。它的原理是基于 ES Module 的静态结构在打包时删除未被引用的导出。默认情况下webpack 和 Vite 都会做 Tree Shaking但有一个隐藏条件模块必须是纯 ES Module且引入方式用具名导出。常见失效场景引入 CJS 模块如const lodash require(lodash)无法静态分析。包的package.json没有配置sideEffects: false编译器不敢删除可能导致副作用的模块。babel转译后把 ES Module 变成了 CommonJS。所以在 babel 配置里设置babel/preset-env的modules: false保留 ES Module 语法。像lodash这样的大型工具库即使import { debounce } from lodash仍然可能把整个库打包进去。解决方案是直接用路径引入import debounce from lodash/debounce或使用lodash-es。按需加载code splitting方面除了路由级拆分组件拆分也很重要。React 用React.lazySuspenseVue 用defineAsyncComponent核心都是“需要时才加载”。但代码分割粒度不能过细否则会产生大量小请求反而降低加载性能。实践中我一般只对“体积大、非首屏、进入频率低”的资源做单独拆包像图标库、富文本编辑器、地图 SDK 这类都值得拆。5.3 微前端/模块联邦场景下的性能取舍前端工程化发展到今天微前端也是热门话题。搜索热词里就有“微前端”而它和性能优化不是脱离的。微前端的核心挑战是“应用间共享依赖”和“避免重复加载”。Module Federation模块联邦是 Webpack 5 推出的能力允许多个独立应用在运行时共享模块。比如主应用把react、react-dom暴露出去子应用就可以不打包 react直接从主应用获取。这样可以显著降低子应用的体积。但要注意模块联邦也会带来运行时解析和版本同步的复杂性本地调试和部署都要调整。微前端场景下的性能优化还需要考虑“子应用加载时机”。通常可以按路由匹配去预加载子应用而不是首屏一次性加载全部。qiankun或wujie这类框架都有预加载配置但预加载会占用带宽和 CPU需要根据业务权衡。如果你能在面试中提到“微前端并非银弹”就说明你做过工程架构的决策而非只停留在 API 使用。6. 性能监控与团队落地防止下个版本一夜回到解放前6.1 性能监控 SDK 的“最小可用版本”性能优化不能“优化完就完了”一定要有监控。很多团队没有自建监控体系面试官也未必要求你会写整套 APM但至少要知道一个最小可用方案怎么设计。我建议用 Performance API PerformanceObserver sendBeacon 组合导航计时从performance.getEntriesByType(navigation)[0]拿 TTFB、DOMContentLoaded、Load。资源计时从performance.getEntriesByType(resource)拿每个脚本、图片、接口的具体耗时可以分析慢资源。最大内容绘制和布局偏移用PerformanceObserver观察largest-contentful-paint和layout-shift。长任务观察longtask超过 50ms 就上报。错误捕获window.onerror和unhandledrejection。上报时要注意不能影响性能本身所以用navigator.sendBeacon()在页面visibilitychange或退出时发送。同时做好采样比如只上报 10% 的会话避免打爆后端。真实用户数据RUM和实验室数据要对比看。Lighthouse 显示你的评价是 90 分不代表全量用户都快。用户网络、设备、浏览器、地理位置都会影响真实体验。所以面试时如果能主动区分“实验室数据 vs RUM”会显得更专业。6.2 面试必问的项目陈述怎么讲清楚一次优化这是很关键的一节。面试官喜欢问“你在项目中做过哪些性能优化”不是想听“我用了懒加载”而是想听到一个有前因后果的技术 story。我建议用 STAR 法则组织回答S背景项目是一个 B 端报表系统页面包含大量图表和数据表格用户反馈首次打开要等 4~5 秒。T目标把首屏 LCP 降到 2.5s 以内CLS 小于 0.1JS 体积减小 50%。A行动先量化瓶颈通过 Lighthouse/WebPageTest 发现首屏最大问题是全量引入 ECharts、moment 打包过大以及多个第三方 SDK 未异步加载。然后拆包、按需引入、把非首屏图表用动态加载改 SSR 或预渲染等。加上 CDN 缓存策略和图片格式优化。R结果LCP 从 4.5s 降到 2.1sJS 体积从 3.2MB 降到 1.1MB线上 RUM 数据 P75 达标。注意回答时不要夸大数据面试官如果追问“你怎么知道 LCP 是 4.5s”你能说出工具和当时的截图才有说服力。数据背后最好有监控体系支撑这样就形成了闭环。7. 高频追问与答题避坑听起来对但经不起问的部分7.1 从“输入 URL 到页面展示”延伸出的优化追问这道题几乎是前端面试的“必考大题”一旦展开到性能优化维度问题会非常多。你可以这样组织主链路用户输入地址浏览器进行 DNS 解析命中缓存则快不命中则去本地 DNS 服务器递归查询。可优化点DNS 预解析、CDN 动态加速。建立 TCP 连接可能还叠加 TLS 握手。可优化点preconnect、HTTP/3。发送 HTTP 请求服务端处理返回 HTML。可优化点CDN 加速静态资源、服务端渲染SSR、边缘函数Edge Functions减少首跳延迟。浏览器解析 HTML发现外部资源。可优化点预加载关键资源preload、预连接第三方域名。构建 DOM 和 CSSOM执行 JavaScript。可优化点内联关键 CSS、defer/async 脚本、代码压缩、移除阻塞渲染资源。渲染页面用户开始交互。可优化点减少重排重绘、优化长任务、内存管理、性能监控。面试官可能会深挖某一环比如“为什么 preload 可以快到关键资源”“SSR 性能就一定比 CSR 好吗”你要能说清楚 SSR 的优势是首屏 HTML 直接返回劣势是服务端渲染压力和 TTFB 可能更高所以不是万能答案。7.2 经典误区合并请求、JS 底部加载、CDN 缓存一切很多备考资料里写“减少 HTTP 请求数合并 JS/CSS”这放在 HTTP/1.1 时代是对的但在 HTTP/2 时代要打问号。HTTP/2 多路复用让多个请求可以共用一个 TCP 连接过度合并文件反而破坏缓存粒度。比如你合并了四个页面共用的工具库和首页专用模块首页改了整个文件缓存失效其他页面也要重新下载。所以现在更推荐“合理拆分 缓存友好”而不是盲目合并。另一个常见误区是“JS 应该放在 body 底部”。现在更现代的做法是使用defer或async放头部也可以关键是不要阻塞 HTML 解析。defer脚本在 HTML 解析完成后执行async下载完立刻执行。放在 body 底部只是“兼容旧写法”的替代方案谈不上最佳实践。关于 CDN很多人觉得“CDN 就是缓存一切”。实际上CDN 缓存导致更新延迟静态资源可以通过 hash 文件名解决但接口请求和 HTML 不能简单缓存。另外还有 CDN 回源、跨域、Preflight 请求等问题。回答这个话题时要体现出“缓存是有策略的不是一刀切的”。7.3 现场评估题白屏和卡顿的排查思路面试最后可能会给一个场景“用户反馈某个页面打开白屏你怎么排查”这个问题的开放性很强但如果你有条理地按步骤回答就能拿高分。第一步先确认现场。如果没有线上监控找运营或用户拿 UA、设备、网络、复屏路径。第二步打开 DevTools Network 看资源加载顺序是 HTML 没回来、CSS/JS 挂了还是接口错误。第三步根据白屏范围区分全站白屏大概率是公共基础库或入口脚本挂了单页面白屏可能是路由配置、鉴权失败或该页面拉数据异常。第四步如果资源正常但白屏用 Performance 面板看 JS 执行时间和是否有报错使用 Console 面板直接看 exception。如果 JS 执行过长也要考虑主线程长任务阻塞导致渲染被推迟。整个过程要体现“从现象到数据到原因”的排查思路。卡顿类场景类似但要侧重主线程先看长任务再看是否有大量 layout 和 paint最后检查内存泄漏。如果你能在现场快速定位到某个具体组件或第三方脚本那基本就稳了。最后说一个我自己的习惯。平时写代码时我会在浏览器控制台跑一段简单脚本记录performance.getEntriesByType(resource)里耗时超过 500ms 的资源同时看Largest Contentful Paint和First Contentful Paint。遇到性能问题先复现、量化再动手。铜九铁十的面试也一样真正让你拿到 offer 的不是你背了多少条优化措施而是你能不能把一条优化讲出闭环指标观察到瓶颈方案落到代码数据验证收益流程防止回退。祝你顺利。
返回列表