前言
在Web开发中,HTML标签和它们的属性在构建页面的结构、样式以及行为方面起着至关重要的作用。而资源加载,尤其是如图片、脚本、样式表等外部资源的加载策略,直接影响着页面的性能和用户体验。
HTML标签属性用于定义元素的行为和外观,而资源加载则决定了页面内容的呈现速度。理解标签属性与资源加载之间的关系,可以帮助开发者优化页面加载时间、提高性能,尤其在复杂的Web应用或大型网站中尤为重要。
Link 标签属性
1. href
-
描述:指定链接资源的地址,通常用于指向 CSS 文件或网站图标等。
-
示例:
<link rel="stylesheet" href="styles.css">
2. rel
-
描述:定义当前文档与目标资源之间的关系。与资源加载直接相关的属性值包括:
stylesheet:表示链接的是一个样式表。preload:表示该资源应当被预加载,以便稍后在需要时使用,高优先级。常用于提前加载字体、图片等资源。prefetch:表示该资源是将来可能需要的,浏览器可以提前加载,低优先级。preconnect: 向浏览器声明需要提前建立到目标域的网络连接。这种提前连接能够减少页面加载时间(提前完成 DNS 查询、TCP 握手和 TLS(SSL)握手),尤其是针对外部资源(如第三方 API、CDN、字体等)或需要频繁访问的跨域资源。dns-prefetch:告知浏览器预解析某个域名的 DNS,减少后续请求的延迟。icon:指定网站的图标。
-
示例:
<link rel="stylesheet" href="styles.css"> <link rel="preload" href="font.woff2" as="font" type="font/woff2" crossorigin="anonymous">
preload 与 prefetch 属性的区别:
preload 的使用场景
-
字体文件:避免文字闪烁(FOIT/FOUT)。
<link rel="preload" href="font.woff2" as="font" type="font/woff2" crossorigin="anonymous"> -
关键 CSS 或 JS:提前加载影响页面渲染或功能的文件。
<link rel="preload" href="styles.css" as="style"> <link rel="preload" href="script.js" as="script"> -
媒体文件:首屏需要的图片、视频。
<link rel="preload" href="hero.jpg" as="image">
prefetch 的使用场景
-
下一页面的资源:当用户可能点击某个链接时提前加载相关资源。
<link rel="prefetch" href="next-page.js"> -
渐进增强:加载低优先级的脚本或样式表。
<link rel="prefetch" href="animations.js">
3. type
-
描述:指定目标资源的媒体类型(MIME 类型),帮助浏览器判断如何处理该资源。常用于
rel="stylesheet"的情况。 -
示例:
<link rel="stylesheet" type="text/css" href="styles.css">
4. as
-
描述:与
rel="preload"配合使用,指定加载的资源类型。常见的值有script、style、font、image等。此属性有助于浏览器正确地处理预加载的资源。 -
常见的
as属性值包括:script:表示预加载的是 JavaScript 文件。style:表示预加载的是 CSS 文件。font:表示预加载的是字体文件。image:表示预加载的是图像资源。audio/video:表示预加载的是音频或视频文件。fetch:表示预加载的是一个资源,通常用于fetch()请求。document:表示预加载的是一个 HTML 文档。
-
示例:
<link rel="preload" href="styles.css" as="style"> <link rel="preload" href="script.js" as="script">
PS:指定
as属性:浏览器能够根据资源的类型采取适当的优化措施,如:
- 正确的缓存策略:浏览器会使用不同的缓存策略来缓存不同类型的资源。例如,
style类型的资源通常会使用缓存,而script资源可能需要更频繁地检查更新。- 优先级处理:浏览器会根据资源类型优化加载顺序。例如,
style和font文件可能会优先加载,因为它们会影响页面的呈现。- 预设请求头:浏览器可以为
crossorigin请求设置适当的请求头,尤其是对于跨域资源。
5. media
-
描述:用于指定加载资源时的媒介条件(如屏幕大小或设备类型)。通常与 CSS 文件的加载相关,用于指定样式表仅在满足特定条件时加载。
-
示例:
<link rel="stylesheet" href="styles.css" media="screen">
6. crossorigin
-
描述:用于控制跨域资源的加载,尤其是当使用
preload或font类型的资源时。它决定了浏览器如何处理跨域请求的权限。anonymous:不发送凭证(如 cookies 或 HTTP 认证信息)。use-credentials:发送凭证。
-
示例:
<link rel="preload" href="font.woff2" as="font" type="font/woff2" crossorigin="anonymous">
Script 标签属性
1. src
-
描述:指定外部 JavaScript 文件的路径。
-
作用:告诉浏览器加载和执行指定的外部脚本。
-
示例:
<script src="script.js"></script>
2. type
-
描述:定义脚本的 MIME 类型。
-
作用:帮助浏览器正确识别和处理脚本文件。
- 默认值为
text/javascript,可以省略。 - 如果设置为
module,脚本会作为 ECMAScript 模块加载。
- 默认值为
-
示例:
<script src="module.js" type="module"></script>
3. async
-
描述:指定脚本文件以异步方式加载,脚本加载完成后立即执行。
-
作用:不会阻塞 HTML 文档的解析,但执行顺序可能与 HTML 中的顺序不同,可能影响
DOMContentLoaded事件。 -
适用场景:适合独立运行的脚本,如广告或统计脚本。
-
示例:
<script src="analytics.js" async></script>
4. defer
-
描述:指定脚本文件以延迟方式加载,并在 HTML 解析完成后按顺序执行(即
DOMContentLoaded事件触发后执行)。 -
作用:不会阻塞 HTML 文档的解析,适合依赖 DOM 内容的脚本。
-
适用场景:推荐用于页面功能相关的脚本。
-
示例:
<script src="main.js" defer></script>
5. nomodule
-
描述:指示脚本仅在不支持模块化的浏览器中加载。
-
作用:用于向旧版浏览器提供降级支持。
-
示例:
<script src="legacy.js" nomodule></script>
6. referrerpolicy
-
描述:指定加载脚本时的
Referer请求头策略。 -
作用:控制
Referer的内容以保护隐私或优化性能。- 常见值:
no-referrer、origin、strict-origin。
- 常见值:
-
示例:
<script src="script.js" referrerpolicy="no-referrer"></script>
7. charset (已很少使用)
-
描述:指定脚本文件的字符编码。
-
作用:用于非 UTF-8 编码的脚本资源(现代浏览器默认使用 UTF-8)。
-
示例:
<script src="script.js" charset="ISO-8859-1"></script>
8. fetchpriority (实验性属性)
-
描述:设置脚本加载的优先级。
-
作用:通过
fetchpriority="high|low|auto"指定脚本的网络加载优先级。 -
示例:
<script src="script.js" fetchpriority="high"></script>
最佳实践
如果想让一个HTML页面尽可能快的被渲染出来,HTML文件中的 link 和 script 标签的顺序应该怎样放置呢?
要弄明白这一点,得先知道浏览器是如何渲染一个 HTML 页面的,这一块资料非常多,本文就不赘述了(推荐文章:how browsers work)。
HTML 的解析过程
简单来说,浏览器解析HTML时有两个重要阶段:构建 DOM 树和构建 CSSOM 树。
构建 DOM 树时,浏览器会对 HTML 进行分词,根据 html 标签从上往下构建,最后形成一个树的结构。由于 script 标签内的脚本可能会修改 dom 结构,因此遇到 script 标签时,会等 js 代码执行完,再继续 html 的解析。script 标签会阻塞 html 的解析,除非带有 defer/async 标签(带有async标签的脚本也可能会阻塞取决于它下载完成的时机)。
CSSOM 树是与 DOM 树并行构建的,因此 CSS 文件并不会阻塞 DOM 的解析。但是由于 js 代码可能存在操作 css 样式的可能,当某个 css 文件标签的位置在 script 标签之前时,此时 css 会阻塞 js 的执行。
Preload scanner
While the browser builds the DOM tree, this process occupies the main thread. As this happens, the preload scanner will parse through the content available and request high-priority resources like CSS, JavaScript, and web fonts. Thanks to the preload scanner, we don't have to wait until the parser finds a reference to an external resource to request it. It will retrieve resources in the background so that by the time the main HTML parser reaches the requested assets, they may already be in flight or have been downloaded. The optimizations the preload scanner provides reduce blockages.
在构建 DOM 树时,html 中的资源标签不会等解析到它了才开始下载。浏览器会有一个聪明的机制是会提前遍历出 html 中核心资源开始并行下载,因此标签在 html 中的位置可能并不会影响它的下载时机,但是当网络状况不佳时,浏览器会根据对标签设置的优先级来加载资源。
结论
从上文可以看出 css 与 js 资源对 html 解析与渲染的影响:
1. CSS不会阻塞DOM的解析,但会阻塞DOM的渲染
2. CSS会阻塞JS的执行,但不会阻塞JS的下载
3. JS会阻塞DOM的解析,也就会阻塞DOM的渲染
想要让 HTML 尽快的渲染出来,与 link 和 script 标签的顺序没有太大关系,核心在于其它方面的处理:
-
提高核心 css/js/fonts/image 等资源的加载优先级
- 尽可能的放在 html 的头部,因为浏览器在接受到第一个 TCP 数据包时就会开始解析 html
- 设置
preload或者fetchpriority等属性
-
对 script 脚本设置 defer 属性防止其阻塞 dom 的解析
-
非核心脚本放在 html 的末尾执行
-
预热资源的域名且减少域名个数
-
...
Web 的性能优化是一个很大的话题,以上只是冰山一角,更多内容还是建议多读读 MDN 的官方文档 Web performance。