脚本执行
在 html 中,link 标签的 ref 主要是存在 3 个属性。
preload: 预先加载资源,加载时,会异步执行。
特点
立即加载:浏览器会尽快开始加载指定的资源
高优先级:与当前页面渲染相关的关键资源(如首屏CSS、字体、关键JS)
不执行:只下载不解析/执行(脚本需额外设置as属性)
缓存:会存储在HTTP缓存中供后续使用
使用场景:
首屏关键CSS、首屏必需的JS模块、大型图片
使用示例:
<link rel="preload" href="style.css" as="style"> <link rel="preload" href="script.js" as="script">
modulepreload:
特点
立即加载:浏览器会尽快开始加载指定的资源
高优先级:与当前页面渲染相关的关键资源(如首屏CSS、字体、关键JS)
执行:下载 + 解析模块及其依赖(modulepreload 仅处理静态导入(import '...'),动态导入的依赖需要单独预加载。)不会执行,只会递归解析其内容中的子模块。
使用场景:
预加载路由分割的模块、关键功能模块:如支付流程、首屏渲染所需的模块。
使用示例:
<link rel="modulepreload" href="modules/canvas.js" />
prefetch:
特点
空闲时加载:浏览器会在空闲时间加载资源
低优先级:不会与关键资源竞争带宽
执行:仅下载,不执行
缓存:会存储在HTTP缓存中供未来使用
使用场景:
跨页面:常用于预加载用户可能访问的下一个页面的资源、用户可能点击的链接对应的资源
使用示例:
<link rel="prefetch" href="next-page.html"> <link rel="prefetch" href="next-page.js" as="script">
以上的三个属性,都是预加载,只是加载的方式和实际有所不同。modulepreload在模块加载场景下优先级与preload as="script"相当或略高,但preload的灵活性更高(可通过as和fetchpriority调整)。关键渲染资源应优先用preload,ES模块依赖树用modulepreload。
看了上面的三个属性,应该也有注意到,这个和 script 标签的 async,defer 属性很类似。首先声明,link 的 ref 只是预先加载,而script 的 async 和defer 是下载后会立即执行。
无defer、async: 无属性 同步加载并执行,阻塞 HTML 解析 默认视为 defer(不阻塞,按顺序执行)
无async: 异步加载,执行时机不确定(下载完立即执行) 异步加载,执行时机不确定(忽略依赖顺序)
defer:
异步加载,在 DOM 解析完成后按顺序执行 默认行为(等同于不加 defer)
defer、async 与 ref 属性的本质区别就是,ref 是只下载/解析,并不会执行,但是 defer、async 是会加载并解析执行的。
但是值得注意的是,defer 和 async 会在 window onload 事件触发前完成。也就是说,可以放心的的使用 windowload,不用担心资源找不到的问题。
因此,http优化的:预热、可以是 link 标签的ref。预热时,尽量不要一次性预热过多资源,即使你使用了 http2(多路复用)因为:大多数浏览器/服务器对每个连接的最大并发流数量默认限制在 100 左右。
详见:https://rolldown.rs/guide/in-depth/why-bundlers#reduce-network-requests-and-waterfalls