Skip to content

[toc]

一、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)
形象比喻“电梯等待”:最后一个人进来后等 秒再走,中途有人进就重新等。“红绿灯/水龙头”:固定每隔 秒放行一次,不管中途积压了多少。
执行逻辑连续触发事件时,只执行最后一次连续触发事件时,按固定频率执行
重置机制每次触发都会重置计时器。计时器运行期间,忽略新的触发。
应用痛点按钮多次点击、搜索框输入校验(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 缓存(极其强大)
多线程HappyPackthread-loader
压缩代码UglifyJS内置 TerserPlugin,支持多进程并行
类库加速DllPlugin物理缓存性能提升,DllPlugin 优先级降低