2024-12-20|
HTML性能优化资源加载浏览器

前端资源加载属性对比:async、defer、preload 与 prefetch

对比 async、defer、preload、prefetch、preconnect 等脚本与资源加载属性,并提供可直接使用的 HTML 示例。

前端资源加载属性对比

前端性能优化中经常会遇到两类配置:一类控制脚本什么时候下载、执行,另一类提示浏览器提前建立连接或获取资源。它们解决的问题不同,不能简单地认为“越早加载越快”。

你提到的 refer/sync 通常是指脚本的 defer/async 属性,下面将它们和常用的 Resource Hints(资源提示)放在一起比较。

一张表看懂异同

属性或写法作用对象下载时机执行或生效时机是否阻塞 HTML 解析适用场景主要注意点
<script src="app.js" defer>经典脚本HTML 解析同时下载HTML 解析完成后,按文档顺序执行有依赖关系的业务脚本、入口脚本脚本会在 DOMContentLoaded 前执行;只对外部经典脚本有意义
<script src="app.js" async>经典脚本HTML 解析同时下载下载完成后立即执行执行时会短暂阻塞独立的统计、广告、监控脚本多个 async 脚本执行顺序不确定,不能依赖 DOM 或其他脚本
<script src="app.js">经典脚本解析到标签时下载下载完成后立即执行必须同步执行的极少数脚本会暂停 HTML 解析,通常不建议用于大型脚本
<script type="module" src="app.js">ES Module 脚本默认异步下载默认延迟到解析完成后执行现代模块化应用默认行为接近 defer;模块依赖会继续被解析和加载
<script nomodule src="legacy.js">经典脚本由不支持 Module 的浏览器下载按普通经典脚本规则执行通常会阻塞为旧浏览器提供降级脚本通常与 type="module" 成对使用
<link rel="preload" as="script">当前页面很快需要的资源页面解析后立即高优先级下载资源被真正引用时使用首屏关键字体、CSS、脚本、图片必须填写正确的 as;预加载后长期不用会浪费带宽
<link rel="modulepreload">ES Module 及其依赖尽早下载模块资源模块执行时直接使用缓存资源需要尽快启动的模块化应用只适用于模块资源,不能替代普通 preload as="script" 的所有场景
<link rel="prefetch" as="script">未来页面可能需要的资源浏览器空闲时低优先级下载未来导航或交互需要时使用下一路由、下一步操作的资源资源不一定会下载;不要用它抢占当前页面关键资源
<link rel="preconnect" href="https://cdn.example.com">目标源站连接尽早完成 DNS、TCP、TLS 建连后续请求可复用连接即将请求的跨域 CDN、字体或 API只给真正会访问的源站使用,过多连接会浪费资源
<link rel="dns-prefetch" href="//cdn.example.com">目标域名空闲时优先解析 DNS后续请求复用 DNS 结果仅需提前解析、但不确定是否立即请求的第三方域名只解决 DNS,不建立 TCP/TLS;优先级低于 preconnect
fetchpriority="high"图片、脚本、链接等请求浏览器调度时提高优先级影响资源请求调度LCP 主图、首屏关键资源只是提示,不是强制命令;滥用高优先级会降低整体性能
fetchpriority="low"图片、脚本、链接等请求浏览器调度时降低优先级影响资源请求调度非首屏图片、低优先级脚本不代表一定延迟加载,只是降低调度优先级
<img loading="lazy"> / <iframe loading="lazy">图片、iframe接近视口时再下载下载完成后正常展示或加载长列表、首屏以下图片和 iframe不适合首屏 LCP 图片;首屏图片可使用 loading="eager"
<img decoding="async">图片解码不改变网络下载时机允许浏览器异步解码不希望图片解码阻塞渲染的场景只是解码提示,不等于网络异步加载
blocking="render"脚本、样式等资源按元素自身规则下载下载后允许阻塞渲染直到资源处理完成可能阻塞渲染必须在首屏绘制前完成的动态资源谨慎使用,阻塞渲染会直接影响 FCP/LCP
crossorigin="anonymous"跨域字体、脚本、图片等不改变下载时机以 CORS 模式请求资源跨域字体、需要 Timing-Allow-Origin 的资源preload 和实际引用必须使用一致的 crossorigin,否则可能重复下载
referrerpolicy="no-referrer"资源请求不改变下载时机控制请求发送的 Referer 信息第三方资源、隐私和安全控制它控制来源信息,不控制资源优先级或执行顺序

脚本加载示例

defer:保持顺序,等待解析完成

HTML
<script src="/js/vendor.js" defer></script> <script src="/js/app.js" defer></script>

两个脚本可以并行下载,但会按照它们在 HTML 中出现的顺序执行。适合 app.js 依赖 vendor.js 的情况。

async:下载完成就执行

HTML
<script src="https://analytics.example.com/analytics.js" async></script> <script src="https://ads.example.com/ads.js" async></script>

两个脚本谁先下载完成谁先执行,因此它们应当彼此独立,不要依赖执行顺序,也不要假设目标 DOM 已经完成渲染。

Module 与旧浏览器降级

HTML
<script type="module" src="/js/app.mjs"></script> <script nomodule defer src="/js/legacy.js"></script>

支持 ES Module 的浏览器使用 app.mjs,旧浏览器使用 legacy.js。Module 脚本默认具有延迟执行特征。

Resource Hints 示例

HTML
<head> <!-- 当前页面首屏一定会用到的字体:高优先级提前加载 --> <link rel="preload" href="/fonts/inter-latin.woff2" as="font" type="font/woff2" crossorigin /> <!-- 当前页面很快会执行的模块 --> <link rel="modulepreload" href="/js/app.mjs" /> <!-- 下一路由可能使用的资源:空闲时再取 --> <link rel="prefetch" href="/js/settings.mjs" as="script" /> <!-- 即将访问的跨域 CDN:提前完成连接握手 --> <link rel="preconnect" href="https://cdn.example.com" crossorigin /> <!-- 只提前解析 DNS,成本低于 preconnect --> <link rel="dns-prefetch" href="//img.example.com" /> </head>

preload 与实际引用要匹配

HTML
<link rel="preload" href="/fonts/inter-latin.woff2" as="font" type="font/woff2" crossorigin /> <style> @font-face { font-family: "Inter"; src: url("/fonts/inter-latin.woff2") format("woff2"); font-display: swap; } </style>

字体的预加载和 @font-face 请求需要保持相同的 URL、CORS 模式和资源类型,否则浏览器可能把它们当成两个请求。

图片加载示例

HTML
<!-- 首屏最大的图片:不要 lazy,适当提高请求优先级 --> <img src="/images/hero.webp" width="1280" height="720" fetchpriority="high" decoding="async" alt="首页主视觉" /> <!-- 首屏以下的图片:接近视口时再加载 --> <img src="/images/article-cover.webp" width="800" height="450" loading="lazy" decoding="async" fetchpriority="low" alt="文章配图" />

图片最好显式设置 widthheight,这样浏览器可以提前预留空间,减少布局偏移(CLS)。

选择建议

  1. 业务入口脚本优先考虑 defertype="module",需要保持顺序时不要使用 async
  2. 独立统计、监控脚本可以使用 async,避免它们影响主业务脚本。
  3. 只有当前页面很快且确定会使用的关键资源才使用 preload
  4. 下一页面或未来交互才会使用的资源使用 prefetch,并接受它可能不执行的事实。
  5. 只对少量确定会访问的跨域源使用 preconnect;其他第三方域名可考虑 dns-prefetch
  6. loading="lazy" 延后非首屏图片,用 fetchpriority="high" 明确首屏关键图片,避免同时把大量资源标记为高优先级。
  7. 这些属性大多是浏览器提示,不是绝对命令;最终效果需要结合 Performance 面板、LCP、CLS 和真实网络环境验证。