2026年了:请停止相信“公共 CDN 能提升性能”

时间:2026-07-07 08:48:48 来源:互联网

本次探讨的核心对象是公共 CDN,而非自建或云厂商提供的 CDN 服务。

2026年了,请停止相信“公共 CDN 能提升性能”

公共 CDN 已经没有缓存优势,对用户来说该加载数据量一点不会减少。

公共 CDN 早期提升速度的核心机制相当直接:当用户访问过任意一个采用相同 CDN 资源的站点后,本地便会缓存该库,后续访问你的网站就能直接利用缓存,无需再下载。然而,在当前浏览器机制与前端生态下,这套逻辑已完全失效。

第一,Chrome 86 正式启用了浏览器缓存分区机制,Firefox、Safari 等主流浏览器也同步跟进。不同站点的缓存相互隔离,用户仍需重新下载脚本,因此公共 CDN 的缓存优势早已成为过去。

第二,即便用户使用的是 Chrome 86 以下的老旧版本,如今已不再是千篇一律的 jQuery 时代。当前为前端构建时代,要恰好遇到之前访问过的网站使用了同供应商、同库、同版本、同格式的资源,概率相对较低。

对用户来说,要加载数据量有可能更大

公共 CDN 提供的资源均为固定全量打包产物。现代前端工程的最大优势在于能够基于业务场景进行极致按需构建。进一步说,所有构建优化公共 CDN 都无法享受。

Tree-shaking 功能可剔除项目中未使用的冗余代码与废弃逻辑,大幅精简资源体积。公共 CDN 的全量包包含库的全部功能代码,即使项目只用到 10% 的能力,也需加载 100% 的代码,冗余度极高。

打包工程构建能将多个小型同类依赖合并为单个 chunk,减少请求数量;同时可根据路由、功能拆分资源粒度,实现按需加载。公共 CDN 是固定打包产物,无法适配项目专属的合并与拆分策略。

依赖自身的构建开关:大量主流库提供了环境裁剪与功能开关的构建配置,可按需关闭无用模块及兼容代码。例如 Vue 的 __VUE_OPTIONS_API__。公共 CDN 为适配全网所有场景,只能保留全部功能并开启全量兼容,无法进行定制化裁剪。

按条件加载:现代前端工程可同时输出多份产物,不同浏览器运行不同产物,例如业界常用的 module/nomodule 实现条件加载。公共 CDN 大多只提供统一大包,要么产物臃肿,要么兼容性差。

自动 polyfill 引入:优雅的构建过程会自动扫描依赖并注入特定 polyfill。但外部依赖无法被扫描,要么全局引入 polyfill,要么只能人工判断 polyfill。

深层依赖预加载:在动态 import 时,现代前端工程能够同时请求深层依赖。使用公共 CDN 时,只有当前依赖请求完成,才能知道有哪些深层依赖。

公共 CDN 的网络真就比主站好吗?我看未必

公共 CDN 节点质量不可控,运营商链路不确定。在弱网、跨国或小众地区,它可能比业务自研 CDN 或源站更慢。使用公共 CDN 还会带来额外的 DNS 解析、TLS 握手、TCP 连接建立以及 CORS 校验等开销。

如果主站已采用 HTTP/2 或 3 的多路复用,同域更能提升连接复用效率,进一步拉大差距。从个人上网体验来看,打开慢的网站几乎都在使用 jsDelivr。

增加供应链风险

你的小网站可能不引人注目,但公共 CDN 是黑客眼中的香饽饽。历史上,开源供应链攻击层出不穷,公共 CDN 极易面临劫持、篡改、停服或投毒风险。为防御这些风险,我们被迫使用 SRI(Subresource Integrity)。

但 SRI 是一把双刃剑。一旦 CDN 资源被篡改或哈希不匹配,浏览器会直接拒绝加载。这就像为了防止食物中毒,把所有外卖都倒进垃圾桶。结果是什么?页面白屏。于是,为了用户体验,我们又要编写“降级逻辑”:检测 CDN 挂掉后,加载本地备用资源。

绕了一大圈,不仅代码变得复杂,用户还多走了一条无效的失败路径,速度反而更慢。这真是“捡了芝麻丢了西瓜”。

公共 CDN 和主站的 https 加密协议版本可能不同。

HTTPS 的加密协议在不断更新,但存在一个核心矛盾:协议越安全,支持面越窄;支持面越广,越不安全。当主站与公共 CDN 的 HTTPS 加密协议版本不同时,可能出现主站支持的用户更广,而公共 CDN 支持的用户更窄的情况,导致主站正常,CDN 挂掉,又需要降级。

更常见的是,公共 CDN 的域名不在用户的 HSTS 列表中。这相当于用户相信我们,但不相信别人,同样会导致 CDN 挂掉。

首屏大小

绝大多数人使用公共 CDN 的方式,是在首页直接通过 script 全局引入。这种模式本质上是最原始的手动维护依赖方式,完全违背现代按需加载的工程理念。如果是 jQuery 这类大概率首屏刚需的依赖,全局引入尚可接受。但如果是 lodash、工具类库或非核心组件库,它们大概率是页面二级、三级路由才会用到,或需动态按需引入。

将非首屏依赖强行挂载到首页,会直接增大首屏资源体积、增加首屏请求数、阻塞页面渲染,白白拖慢首屏加载速度,完全得不偿失。

工程复杂度

如果在首屏,你可能通常这样写:

');}

上述代码虽然勉强实现降级,但由于是同步加载,会牺牲性能。如果不在首屏,还需考虑失败降级、SRI、兼容性,整体复杂度提升不少。这里不展开细说,以免文章跑题。通过一个表格就能窥见其复杂度。

场景处理方案
支持 type="importmap": integrity使用 importmap integrity
支持 top-level-await 和 script integrity别名到一个虚拟模块,虚拟模块用 top-level-await 动态创建
不想使用构建技巧,就想使用原生 import基于原生 import 写模块化运行时,fetch 拿数据,运行时校验 integrity,运行时解析转化源代码,生成 blob url,创建 script module,里面使用 import *
不支持 top-level-await 或原生 import使用传统模块化运行时

如果非要处理,我会选择使用 top-level-await,不支持的用传统模块化运行时降级。

总结

公共 CDN 只适合在首屏依赖且工程化不足的情况下使用,例如传统 jQuery 项目:全局引入、人工 babel。但凡工程化能力较强,即使使用 jQuery 也能通过按需构建实现优化。

题外话

在面试场景中,若主考官询问“如何优化前端性能”,仍建议使用公共 CDN。这属于典型的八股问题,若否定公共 CDN,可能不合时宜。