文章目录
-
- 一、CDN
-
- [1. CDN 的系统架构](#1. CDN 的系统架构)
- [2. CDN 的核心作用](#2. CDN 的核心作用)
- [3. CDN 工作原理:从 DNS 开始](#3. CDN 工作原理:从 DNS 开始)
-
- [传统访问 vs CDN 访问流程](#传统访问 vs CDN 访问流程)
- 关键术语:CNAME
- [4. 典型应用场景](#4. 典型应用场景)
- [二、懒加载 (Lazy Load) 与 预加载 (Preload)](#二、懒加载 (Lazy Load) 与 预加载 (Preload))
-
- [1. 懒加载 (Lazy Load) ------ 按需分配](#1. 懒加载 (Lazy Load) —— 按需分配)
- [2. 预加载 (Preload)](#2. 预加载 (Preload))
- [3. 深度对比:懒加载 vs 预加载](#3. 深度对比:懒加载 vs 预加载)
- 三、回流与重绘
-
- [1. 回流与重绘的核心概念](#1. 回流与重绘的核心概念)
-
- [1. 回流 (Reflow / Layout)](#1. 回流 (Reflow / Layout))
- [2. 重绘 (Repaint)](#2. 重绘 (Repaint))
- [2. 如何高效规避回流与重绘?](#2. 如何高效规避回流与重绘?)
- 四、防抖与节流
- 五、图片优化
-
- [1. 资源替代方案](#1. 资源替代方案)
- [2. 传输与大小控制](#2. 传输与大小控制)
- [3. 常见图片格式深度对比与选型](#3. 常见图片格式深度对比与选型)
- [六、Webpack 性能优化](#六、Webpack 性能优化)
一、CDN
1. CDN 的系统架构
一个完善的 CDN 系统通常由三个核心子系统协同工作:
- 分发服务系统(Cache 层) :
- 核心单元:边缘 Cache 设备
- 职责:直接响应用户请求,缓存本地内容,并与源站保持同步。其服务能力由设备规模和总带宽决定
- 负载均衡系统(调度层) :
- 两级调度 :分为 GSLB(全局负载均衡) 和 SLB(本地负载均衡)
- 职责:根据用户的地理位置、运营商、节点负载等因素,确定最优的 Cache 服务器 IP
- 运营管理系统(支撑层) :
- 职责:处理业务层面的统计、计费、客户管理及网络监控
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 对象:
javascriptlet img = new Image(); img.src = "heavy-photo.jpg"; // 浏览器会自动开始下载并存入缓存
3. 深度对比:懒加载 vs 预加载
| 特性 | 懒加载 | 预加载 |
|---|---|---|
| 加载时机 | 滞后(进入可视区再加载) | 提前(空闲或初始化时加载) |
| 对服务器压力 | 缓解压力,减少并发 | 增加压力,可能加载无用资源 |
| 主要目的 | 优化首屏性能和流量 | 提升后续交互的流畅度 |
| 适用场景 | 电商长列表、图片流 | 游戏素材、大图背景、字体文件 |
三、回流与重绘
1. 回流与重绘的核心概念
1. 回流 (Reflow / Layout)
本质:重新计算布局。 当渲染树(Render Tree)中元素的尺寸、结构或几何属性发生变化时,浏览器需要重新计算元素在设备视口内的确切位置和大小。由于浏览器采用流式布局,一个节点的改变往往会引起其子节点、兄弟节点甚至整个页面的重新计算
- 常见触发场景 :改变宽/高、修改边距、增删 DOM 节点、修改字体大小、窗口缩放、获取布局属性(如
offsetTop、getComputedStyle)
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 优先级降低 |