Skip to content

前端性能优化


一、性能指标与衡量

Q1:前端常见性能指标有哪些?分别代表什么?

答:

  • FP(First Paint):首次绘制,页面开始有像素渲染。

  • FCP(First Contentful Paint):首次内容绘制,出现文本/图片等真实内容。

  • LCP(Largest Contentful Paint):最大内容绘制,核心体验指标,要求 < 2.5s。

  • FID(First Input Delay):首次输入延迟,用户交互到浏览器响应的时间,要求 < 100ms。

  • CLS(Cumulative Layout Shift):累积布局偏移,页面布局跳动,要求 < 0.1。

  • TTI(Time to Interactive):可交互时间,页面稳定可交互。

  • TTFB(Time to First Byte):首字节时间,网络+服务端响应速度。

Q2:如何做性能监控?常用工具/方案?

答:

  • 实验室数据:Lighthouse、WebPageTest、Chrome DevTools(Performance/Network)。

  • 真实用户监控(RUM)

    • PerformanceObserver 监听 LCP、FID、CLS。

    • PerformanceTiming / Navigation Timing API 收集加载数据。

    • 上报到 Sentry、阿里云 ARMS、自研监控平台。

  • 长任务监控PerformanceObserver 监听 longtask,定位阻塞主线程的任务。

Q3:什么是长任务?如何定位与优化?

答:

  • 长任务:执行时间 > 50ms、阻塞主线程的任务,会导致卡顿、交互延迟。

  • 定位

    • Chrome Performance 面板看长任务条。

    • PerformanceObserver 上报长任务。

  • 优化

    • 拆分长任务:setTimeoutrequestIdleCallbackqueueMicrotask

    • 耗时计算放到 Web Worker。

    • 懒执行非首屏逻辑。


二、网络与加载优化(最常问)

Q4:HTTP 缓存(强缓存/协商缓存)完整流程?

答:

  • 强缓存:不发请求,直接用本地。

    • Cache-Control: max-age=xxx(相对时间,优先)

    • Expires(绝对时间,兼容)

  • 协商缓存:发请求,服务器判断是否可用。

    • 响应:Last-Modified / ETag

    • 请求:If-Modified-Since / If-None-Match

    • 304 用缓存,200 用新内容。

  • 优先级Cache-Control > ExpiresETag > Last-Modified

Q5:HTTP1.1 / HTTP2 / HTTP3 对性能的影响?

答:

  • HTTP1.1:队头阻塞、单连接串行、头信息大。

  • HTTP2:二进制分帧、多路复用、头部压缩、服务器推送,解决队头阻塞。

  • HTTP3:基于 QUIC(UDP),彻底解决 TCP 队头阻塞,0-RTT 握手更快。

Q6:资源加载优化手段(按优先级)?

答:

  1. 减少请求数:雪碧图、资源合并、接口合并、内联小资源。

  2. 压缩体积:Gzip/Brotli、JS/CSS/HTML 压缩、图片 WebP/AVIF。

  3. 懒加载:图片懒加载、路由懒加载、组件懒加载、Intersection Observer。

  4. 预加载/预连接

    • dns-prefetch:提前解析 DNS

    • preconnect:提前建立连接

    • preload:关键资源优先加载

    • prefetch:预测下一页资源

  5. CDN:静态资源上 CDN,就近访问。

  6. 接口优化:合并接口、分页、防抖、缓存接口数据。

Q7:图片优化有哪些手段?

答:

  • 格式:WebP/AVIF/PNG 代替 JPG,SVG 代替小图标。

  • 响应式图片:srcset + sizes,根据屏幕加载对应尺寸。

  • 懒加载:loading="lazy" 或 Intersection Observer。

  • 占位:低质量图片占位(LQIP)、SVG 占位、骨架屏。

  • 压缩:TinyPNG、Squoosh、webpack-image-minimizer。

  • 避免布局偏移:给图片固定宽高或 aspect-ratio


三、渲染优化(重排/重绘/合成)

Q8:重排(reflow)、重绘(repaint)、合成(composite)区别?

答:

  • 重排:修改几何属性(宽高、位置、display)→ 重新布局,开销最大。

  • 重绘:修改样式但不影响布局(color、background)→ 只重画像素。

  • 合成:transform/opacity 等在合成线程处理 → 不触发重排重绘,开销最小。

Q9:如何减少重排重绘?

答:

  • 批量修改 DOM:使用文档片段 DocumentFragment

  • 离线 DOM:先 display: none,修改完再显示。

  • 读写分离:不要“读布局属性 → 立即修改 → 再读”,避免强制同步布局。

  • 动画用 transform/opacity,开启硬件加速。

  • 固定宽高,避免图片/内容导致布局偏移。

  • 用 BFC 隔离渲染区域,减少影响范围。

Q10:什么是布局抖动(Layout Thrashing)?如何避免?

答:

  • 布局抖动:循环中频繁“读取布局属性 + 修改样式”,导致浏览器强制多次重排。

  • 示例:

    JavaScript
    
    for (let i = 0; i < 100; i++) {
      const h = div.offsetHeight; // 读
      div.style.top = h + i + 'px'; // 写 → 每次都触发重排
    }
  • 避免

    • 先批量读,再批量写。

    • 使用 requestAnimationFrame 分批更新。

Q11:CSS 性能优化要点?

答:

  • 减少选择器层级,避免嵌套过深。

  • 避免通配符、属性选择器慢查询。

  • 关键 CSS 内联,非关键 CSS 异步加载。

  • will-change 提示浏览器提前优化(适度使用)。

  • 减少昂贵属性:box-shadowgradientfilter 频繁重绘。


四、JS 运行时优化(执行、内存、事件)

Q12:JS 执行优化手段?

答:

  • 减少全局变量,避免全局查找。

  • 循环优化:缓存长度、避免循环内 DOM 操作、避免复杂计算。

  • 防抖节流处理高频事件(scroll/resize/input)。

  • 避免闭包滥用导致内存泄漏。

  • 耗时逻辑 Web Worker 化。

  • 懒执行:非首屏逻辑延迟到使用时执行。

Q13:常见内存泄漏场景与解决方案?

答:

  1. 意外全局变量:未声明变量 → 严格模式、ESLint。

  2. 遗忘的定时器/事件监听 → 组件卸载时清除。

  3. 闭包引用 → 及时解除引用。

  4. DOM 引用未清理 → 移除 DOM 同时删除引用。

  5. 控制台打印 → 生产环境关闭 console。

  6. 缓存未限制大小 → LRU 缓存淘汰。

Q14:什么是垃圾回收(GC)?如何减少 GC 压力?

答:

  • JS 自动 GC,标记清除/分代回收。

  • 减少 GC 压力:

    • 避免频繁创建销毁对象(如循环内创建临时对象)。

    • 复用对象/数组,避免频繁扩容。

    • 长列表用虚拟列表,减少 DOM 与内存占用。

Q15:requestAnimationFrame / requestIdleCallback 区别与使用场景?

答:

  • requestAnimationFrame:在下次重绘前执行,适合动画、DOM 更新。

  • requestIdleCallback:浏览器空闲时执行,适合低优先级任务(埋点、日志、预加载)。


五、React 性能优化(高频)

Q16:React 性能优化核心思路?

答:

核心:减少不必要渲染 + 减少渲染耗时 + 减少包体积

Q17:React 避免重渲染手段?

答:

  • React.memo 缓存组件(浅对比 props)。

  • useCallback 缓存函数,避免子组件重新渲染。

  • useMemo 缓存计算结果与复杂对象。

  • 列表用唯一稳定 key,避免 index。

  • 拆分组件:频繁更新与不更新部分分离。

  • 状态下放:把状态放到真正需要的子组件里。

  • 合理使用 Context:拆分 Context,配合 useMemo 缓存 value。

Q18:React.memo / PureComponent / shouldComponentUpdate 区别?

答:

  • 都是浅对比,避免不必要渲染。

  • React.memo:函数组件 HOC。

  • PureComponent:类组件,内置 shouldComponentUpdate

  • shouldComponentUpdate:手动控制,可深对比(不推荐)。

Q19:React 长列表优化?

答:

  • 虚拟列表:react-window / react-virtualized,只渲染可视区域。

  • 固定行高,避免动态计算。

  • 懒加载/分页加载,减少一次性渲染量。

  • 组件复用,避免频繁创建销毁。

Q20:React 18 并发特性与性能优化?

答:

  • 自动批处理:所有场景 setState 合并,减少渲染。

  • useTransition:标记低优先级更新,不阻塞交互。

  • useDeferredValue:延迟非紧急值更新,类似防抖。

  • Suspense 数据获取:更好的加载状态管理,减少抖动。


六、Vue 性能优化(高频)

Q21:Vue 性能优化核心手段?

答:

  • 编译优化:PatchFlag、静态提升、事件监听缓存。

  • 合理使用 v-if vs v-show:频繁切换用 v-show

  • v-for 必须 key,避免 index。

  • 组件懒加载:defineAsyncComponent

  • 长列表:虚拟滚动 vue-virtual-scroller

  • 状态管理优化:拆分 store,避免全局状态滥用。

  • keep-alive 缓存页面,配合 include/exclude

  • 非响应式数据用 markRaw/shallowRef 减少代理开销。

Q22:Vue3 编译优化(PatchFlag、静态提升)原理?

答:

  • PatchFlag:编译时给动态节点打标记,更新时只 diff 动态部分。

  • 静态提升:静态节点提升到渲染函数外,只创建一次,复用。

  • 事件缓存:内联事件函数缓存,避免每次渲染创建新函数。

Q23:Vue 中 computed / watch / methods 性能区别?

答:

  • computed:缓存,依赖不变不重新计算,适合派生状态。

  • watch:监听变化执行副作用,适合异步/复杂逻辑。

  • methods:每次渲染都执行,无缓存,适合事件/无缓存逻辑。


七、工程化与构建优化(Webpack/Vite)

Q24:Webpack 性能优化手段?

答:

  • 缩小构建范围exclude/includenoParsecache

  • 多进程thread-loaderHappyPack

  • 代码分割splitChunks 分离公共依赖、路由懒加载。

  • Tree Shaking:ES Module、sideEffects、生产模式。

  • 压缩terser-webpack-plugincss-minimizer-webpack-plugin

  • CDN 引入:大库用 CDN,不打包。

  • DLL 预编译:提前编译第三方依赖。

Q25:Vite 为什么比 Webpack 快?

答:

  • 开发模式:

    • 基于 ESBuild 预构建依赖。

    • 浏览器原生 ESM,按需请求,不打包。

    • 热更新只更新修改模块,秒级更新。

  • 生产模式:Rollup 打包,体积更小。

Q26:Tree Shaking 原理与前提?

答:

  • 原理:静态分析 ES Module,删除未使用代码。

  • 前提:

    • ES Module(import/export)。

    • 生产模式,开启优化。

    • 无副作用(配置 sideEffects)。


八、Electron 桌面端性能优化(专项)

Q27:Electron 启动慢如何优化?

答:

  • 主进程延迟初始化:非必要逻辑放到 ready 后异步执行。

  • 窗口先隐藏,加载完成再 show。

  • 预加载脚本轻量化,只做通信封装。

  • 禁用不必要功能:spellcheck、自动填充等。

  • 依赖裁剪,减小包体积。

Q28:Electron 包体积过大如何优化?

答:

  • 剔除 devDependencies。

  • ASAR 打包,压缩源码。

  • 按需引入依赖,替换大体积库。

  • 分平台打包,只打目标平台。

  • 考虑 Tauri 替代(更小体积)。

Q29:Electron 渲染卡顿如何优化?

答:

  • 关闭不必要的 webPreferences:webSecurity 按需关闭。

  • 开启硬件加速。

  • 长列表虚拟滚动。

  • 主进程与渲染进程通信避免频繁大数据传输。

  • 避免同步 IPC(sendSync),改用 invoke。


九、综合实战题(面试官最爱)

Q30:你做过哪些性能优化?请按项目讲一个完整案例。

答题结构(STAR):

  1. 背景:项目、问题(LCP 超时、白屏、卡顿)。

  2. 分析:用 Lighthouse/Performance 定位瓶颈(资源大、长任务、重排)。

  3. 方案

    • 图片 WebP + 懒加载 + 骨架屏。

    • 路由/组件懒加载,代码分割。

    • 防抖节流,长任务拆分。

    • 虚拟列表,减少渲染。

    • HTTP 缓存 + CDN。

  4. 结果:LCP 从 4s → 1.8s,FID 下降 60%,用户留存提升。

Q31:首屏加载慢,你会怎么一步步排查与优化?

答:

  1. 看网络:Network 面板看资源大小、加载顺序、TTFB。

  2. 看渲染:Lighthouse 看 FCP/LCP/CLS。

  3. 看执行:Performance 看长任务、JS 执行耗时。

  4. 优化顺序

    • 图片优化(WebP、懒加载、响应式)。

    • 关键 CSS 内联,非关键异步。

    • 代码分割、懒加载、Tree Shaking。

    • 接口合并、缓存、预加载。

    • 长任务拆分、Web Worker。

Q32:页面滚动卡顿,如何排查与优化?

答:

  • 定位:Performance 看 scroll 事件是否有长任务。

  • 优化:

    • 给 scroll 加防抖/节流。

    • 避免滚动中 DOM 操作、重排。

    • 用 transform 做滚动相关动画。

    • 长列表虚拟滚动。

    • 避免滚动中频繁请求/计算。


十、高频手写题(性能相关)

Q33:手写防抖(debounce)

JavaScript

function debounce(fn, delay = 300) {
  let timer = null
  return function (...args) {
    clearTimeout(timer)
    timer = setTimeout(() => fn.apply(this, args), delay)
  }
}

Q34:手写节流(throttle)

JavaScript

function throttle(fn, interval = 300) {
  let last = 0
  return function (...args) {
    const now = Date.now()
    if (now - last >= interval) {
      last = now
      fn.apply(this, args)
    }
  }
}

Q35:图片懒加载(Intersection Observer)

JavaScript

function lazyLoad() {
  const imgs = document.querySelectorAll('img.lazy')
  const observer = new IntersectionObserver((entries) => {
    entries.forEach(entry => {
      if (entry.isIntersecting) {
        const img = entry.target
        img.src = img.dataset.src
        img.classList.remove('lazy')
        observer.unobserve(img)
      }
    })
  })
  imgs.forEach(img => observer.observe(img))
}

复习建议

  • 每天按“加载 → 渲染 → JS → 框架 → 工程化”过一遍。

  • 重点背:LCP/CLS/FID、HTTP 缓存、重排重绘、React/Vue 避免重渲染、Webpack/Vite 优化

  • 准备 1~2 个真实项目优化案例,带数据,面试最加分。

如果你需要,我可以再给你做一份:

  • “性能优化面试 10 分钟速记版”(极度精简,适合临考)

  • 或按“每天 30 分钟”给你做一个7 天突击计划

(注:文档部分内容可能由 AI 生成)

最近更新