前端性能优化面试题

文章目录

    • 一、CDN
      • [1. CDN 的系统架构](#1. CDN 的系统架构)
      • [2. CDN 的核心作用](#2. CDN 的核心作用)
      • [3. CDN 工作原理:从 DNS 开始](#3. CDN 工作原理:从 DNS 开始)
      • [4. 典型应用场景](#4. 典型应用场景)
    • [二、懒加载 (Lazy Load) 与 预加载 (Preload)](#二、懒加载 (Lazy Load) 与 预加载 (Preload))
    • 三、回流与重绘
      • [1. 回流与重绘的核心概念](#1. 回流与重绘的核心概念)
        • [1. 回流 (Reflow / Layout)](#1. 回流 (Reflow / Layout))
        • [2. 重绘 (Repaint)](#2. 重绘 (Repaint))
      • [2. 如何高效规避回流与重绘?](#2. 如何高效规避回流与重绘?)
    • 四、防抖与节流
    • 五、图片优化
      • [1. 资源替代方案](#1. 资源替代方案)
      • [2. 传输与大小控制](#2. 传输与大小控制)
      • [3. 常见图片格式深度对比与选型](#3. 常见图片格式深度对比与选型)
    • [六、Webpack 性能优化](#六、Webpack 性能优化)
      • [1. 如何提升构建速度](#1. 如何提升构建速度)
        • [优化Loader 的文件搜索与转换范围](#优化Loader 的文件搜索与转换范围)
        • [利用多核 CPU(并行化)](#利用多核 CPU(并行化))
        • 预编译与外部化
      • [2. 如何减小打包体积](#2. 如何减小打包体积)
      • [3. Webpack 3/4 vs Webpack 5](#3. Webpack 3/4 vs Webpack 5)

一、CDN

1. CDN 的系统架构

一个完善的 CDN 系统通常由三个核心子系统协同工作:

  1. 分发服务系统(Cache 层)
    • 核心单元:边缘 Cache 设备
    • 职责:直接响应用户请求,缓存本地内容,并与源站保持同步。其服务能力由设备规模和总带宽决定
  2. 负载均衡系统(调度层)
    • 两级调度 :分为 GSLB(全局负载均衡)SLB(本地负载均衡)
    • 职责:根据用户的地理位置、运营商、节点负载等因素,确定最优的 Cache 服务器 IP
  3. 运营管理系统(支撑层)
    • 职责:处理业务层面的统计、计费、客户管理及网络监控

2. CDN 的核心作用

  • 性能提升
    • 低延迟:用户从最近的数据中心获取内容
    • 减轻源站压力:大量请求被边缘节点拦截,降低了源站服务器的负载
  • 安全防御
    • 防御 DDoS:通过边缘节点的流量分析限制异常请求,避免源站崩溃
    • 防止 MITM(中间人攻击):支持全链路 HTTPS 加密
  • 按需扩展:应对突发流量高峰(如电商促销),实现资源的弹性扩容

3. CDN 工作原理:从 DNS 开始

CDN 与 DNS 系统紧密结合,通过 CNAME(别名记录) 实现流量调度

传统访问 vs CDN 访问流程
步骤 传统方式(无 CDN) CDN 方式
DNS 解析 获取源站主机的 IP 获取 CNAME 指向的 CDN 专用 DNS IP
寻址结果 直接得到服务器 IP 得到经过 GSLB 计算后的边缘节点 IP
请求过程 浏览器直连源服务器 浏览器连接最近的 Cache 缓存服务器
缺损处理 服务器直接返回数据 缓存缺失时,由 Cache 节点向上级或源站发起回源请求
关键术语:CNAME

CNAME(别名记录):它不直接映射 IP,而是将一个域名指向另一个域名。CDN 正是利用这一点,将你的网站域名"重定向"到 CDN 服务商的调度系统。

4. 典型应用场景

  • 静态资源缓存 :将 .js.css、图片等不常变动的资源托管到 CDN,实现首屏秒开
  • 流媒体/直播传送
    • 对于普通文件,节点缺失会"回源"查找
    • 对于大体积的流媒体,通常采用**主动推送(Push)**技术,将内容提前下发到边缘节点,保证播放不卡顿
  • 全站加速:配合 SSL 证书托管,实现全链路的快速、安全分发

二、懒加载 (Lazy Load) 与 预加载 (Preload)

1. 懒加载 (Lazy Load) ------ 按需分配

懒加载指的是在长网页中延迟加载非可视区域的资源,直到用户滚动到该位置时才触发请求

核心价值
  • 节省流量:避免加载用户从未看过的图片
  • 首屏加速:减少首屏 HTTP 请求数,让核心内容更快呈现
  • 减轻服务器压力:并发请求数降低,提升整体吞吐量
实现原理与公式

懒加载的关键在于判断元素是否进入了浏览器可视窗口

逻辑判断公式:

img.offsetTop < window.innerHeight + document.documentElement.scrollTop

  • offsetTop:元素距离文档顶部的距离
  • innerHeight:可视窗口的高度
  • scrollTop:页面滚动的距离
代码实战 (原生 JS)

通过 data-src 存储真实路径,滚动时动态替换 src

javascript 复制代码
function lazyLoad() {
    const imgs = document.querySelectorAll('img[data-src]');
    const viewHeight = window.innerHeight;
    const scrollTop = document.documentElement.scrollTop || document.body.scrollTop;

    imgs.forEach(img => {
        // 判断图片是否进入可视区
        if (img.offsetTop < viewHeight + scrollTop) {
            img.src = img.dataset.src;
            // 加载后移除自定义属性,避免重复处理
            img.removeAttribute('data-src');
        }
    });
}
// 监听滚动事件(建议加节流函数优化)
window.addEventListener('scroll', lazyLoad);

2. 预加载 (Preload)

预加载指的是将所需的资源(图片、脚本、字体等)提前请求加载到本地缓存,当真正需要用到时,直接从缓存获取

核心价值
  • 零等待体验:用户点击或切换时,资源瞬间加载
  • 解决闪烁:防止字体或大图在渲染时出现明显的白块或跳动
常用实现方式
  • HTML 标签<link rel="preload" href="style.css" as="style">

  • JS 对象

    javascript 复制代码
    let img = new Image();
    img.src = "heavy-photo.jpg"; // 浏览器会自动开始下载并存入缓存

3. 深度对比:懒加载 vs 预加载

特性 懒加载 预加载
加载时机 滞后(进入可视区再加载) 提前(空闲或初始化时加载)
对服务器压力 缓解压力,减少并发 增加压力,可能加载无用资源
主要目的 优化首屏性能和流量 提升后续交互的流畅度
适用场景 电商长列表、图片流 游戏素材、大图背景、字体文件

三、回流与重绘

1. 回流与重绘的核心概念

1. 回流 (Reflow / Layout)

本质:重新计算布局。 当渲染树(Render Tree)中元素的尺寸、结构或几何属性发生变化时,浏览器需要重新计算元素在设备视口内的确切位置和大小。由于浏览器采用流式布局,一个节点的改变往往会引起其子节点、兄弟节点甚至整个页面的重新计算

  • 常见触发场景 :改变宽/高、修改边距、增删 DOM 节点、修改字体大小、窗口缩放、获取布局属性(如 offsetTopgetComputedStyle
2. 重绘 (Repaint)

本质:重新像素填充。 当元素的样式发生变化,但不影响其在文档流中的位置和几何尺寸时(例如改变背景颜色、文字颜色、阴影等),浏览器会将受影响的像素点重新绘制到屏幕上

  • 重要规律回流必引起重绘,但重绘不一定引起回流

2. 如何高效规避回流与重绘?

频繁的触发会导致页面卡顿(丢帧)。我们可以通过以下策略进行优化:

  • 操作DOM时,尽量在低层级的DOM节点进行操作
  • 不要使用table布局, 一个小的改动可能会使整个table进行重新布局
  • 使用CSS的表达式
  • 不要频繁操作元素的样式,对于静态页面,可以修改类名,而不是样式
  • 使用absolute或者fixed,使元素脱离文档流,这样他们发生变化就不会影响其他元素
  • 避免频繁操作DOM,可以创建一个文档片段documentFragment,在它上面应用所有DOM操作,最后再把它添加到文档中
  • 将元素先设置display: none,操作结束后再把它显示出来。因为在display属性为none的元素上进行的DOM操作不会引发回流和重绘
  • 将DOM的多个读操作(或者写操作)放在一起,而不是读写操作穿插着写。这得益于浏览器的渲染队列机制

四、防抖与节流

核心概念对比

特性 防抖 (Debounce) 节流 (Throttle)
形象比喻 "电梯等待" :最后一个人进来后等 n n n 秒再走,中途有人进就重新等。 "红绿灯/水龙头" :固定每隔 n n n 秒放行一次,不管中途积压了多少。
执行逻辑 连续触发事件时,只执行最后一次 连续触发事件时,按固定频率执行
重置机制 每次触发都会重置计时器。 计时器运行期间,忽略新的触发。
应用痛点 按钮多次点击、搜索框输入校验(Input)、窗口缩放(Resize)。 页面滚动(Scroll)、抢购点击、拖拽移动(MouseMove)。

原理解析

防抖 (Debounce)

防抖的核心在于**"清空旧的,开启新的"**

  • 如果用户操作很快(短于等待时间),之前的计时器会被 clearTimeout 杀掉,只有最后一次操作能活到计时结束并执行函数
节流 (Throttle)

节流的核心在于**"锁状态"**

  • 时间戳版 :对比当前时间与上次执行时间的差值。优点是立即执行(第一次触发就会运行)
  • 定时器版 :通过判断 timer 是否为空来决定是否开启新的任务。优点是尾随执行(停止触发后还会最后执行一次)

五、图片优化

优化图片的本质是:减少 HTTP 请求次数 + 减小单个文件体积 + 提升感知加载速度

1. 资源替代方案

  • CSS 代替修饰图:圆角、渐变、阴影、几何图形等完全可以用 CSS 实现,零网络请求
  • SVG 代替位图:对于 Icon、Logo、简单插画,SVG(矢量图)具有无限放大不失真和体积极小的优势
  • Iconfont(字体图标):将多个图标打包成字体文件,通过 CSS 控制颜色和大小

2. 传输与大小控制

  • 响应式加载 (Responsive Images) :利用 CDN 动态裁剪功能,根据用户设备的 devicePixelRatio 和屏幕宽度返回对应尺寸的图,避免在手机端加载 4K 原图。
  • Base64 内联:对于 2KB 以下的小图,可以使用 Base64 编码内联在 CSS 或 HTML 中,减少一次 HTTP 往返延迟(RTT)。
  • v雪碧图 (CSS Sprites):在 HTTP/1.1 时代非常重要,将多个图标拼成一张大图,减少 TCP 连接开销(HTTP/2 下优先级降低)。

3. 常见图片格式深度对比与选型

理解不同格式的原理,才能在"清晰度"与"体积"之间找到最佳平衡点

格式 类型 特点 适用场景
JPEG 有损/点阵 色彩丰富,不支持透明 照片、复杂背景图
PNG-8 无损/索引色 体积小,支持透明,仅 256 色 简单 Logo、色彩单一的插图
PNG-24 无损/直接色 高保真,完美透明,体积大 需要半透明效果的高质素材
GIF 无损/索引色 支持动画,仅 256 色 简单动图(复杂动图建议用 MP4)
SVG 无损/矢量 XML 格式,无限放大不失真 Logo、Icon、扁平化插画
WebP 混合/直接色 谷歌推出,体积比 JPEG/PNG 小 30% 现代浏览器首选格式

六、Webpack 性能优化

Webpack 优化的本质是:在开发阶段减少不必要的重复工作,在生产阶段剔除不必要的冗余代码

1. 如何提升构建速度

优化Loader 的文件搜索与转换范围
  • Loader 精度控制 :使用 include 包含业务代码(如 src),使用 exclude 排除三方库(如 node_modules
  • 开启缓存 :给 babel-loader 加上 cacheDirectory=true
利用多核 CPU(并行化)
  • HappyPack:Webpack 3/4 时代的利器,将单线程的 Loader 转换变为多线程
  • thread-loader:Webpack 5 时代的官方推荐,原理类似,放置在开销大的 Loader 之前即可
预编译与外部化
  • DllPlugin:将不常变动的框架(React/Vue)预先打包成静态文件,构建时直接引用,跳过编译
  • externals:直接使用 CDN 引入资源,不让 Webpack 处理这些庞大的类库

2. 如何减小打包体积

剔除冗余代码
  • Tree Shaking :基于 ES Module,在生产模式(production)下自动开启。它能像摇晃树木一样,把没用到的函数(死代码)从最终包里摇掉
  • Scope Hoisting(作用域提升):将分散的模块函数合并到一个作用域中,减少函数声明开销和内存占用
策略性拆分
  • Code Splitting(代码分割)
    • 路由按需加载 :通过 import() 动态导入,实现页面级别的 JS 拆分,首页只加载首页需要的代码
    • SplitChunksPlugin :将多个入口公用的代码抽离成单独的 vendor.js,利用浏览器缓存

3. Webpack 3/4 vs Webpack 5

很多手段(如 HappyPack, DllPlugin)在旧版项目中非常有效,但 Webpack 5 已经内置了更高效的替代方案:

优化点 Webpack 3/4 常用 Webpack 5 推荐
持久化缓存 cache-loader 内置 filesystem 缓存(极其强大)
多线程 HappyPack thread-loader
压缩代码 UglifyJS 内置 TerserPlugin,支持多进程并行
类库加速 DllPlugin 物理缓存性能提升,DllPlugin 优先级降低
相关推荐
点正2 小时前
详解TypeScript项目引用(Project References)中rootDir的坑:composite:true下为何不能指定rootDir
前端·next.js
向前跑丶加油2 小时前
tailwindcss构建执行npm exec tailwindcss init -p 报错
前端·npm·node.js
gustt2 小时前
手写 Mini React:深入理解 createElement 和 render 原理
前端·react.js
陈随易2 小时前
向日葵+AI,远程操控又进化了
前端·后端·程序员
Han.miracle2 小时前
万字详解 Lombok 构造方法注解:@AllArgsConstructor 非空校验实现与最佳实践
java·前端·数据库
Mintopia2 小时前
现代 CSS 使用技巧(进阶篇):从布局到性能的实战方法
前端·css
换个网名有点难2 小时前
Openclaw中NODE踩坑,NPM、PNPM和CNPM有什么区别
前端·npm·node.js
catchadmin2 小时前
Chrome DevTools MCP 让 AI 无缝接管浏览器调试会话
前端·chrome·chrome devtools