HTML script defer 属性(超详细)
💡一则或许对你有用的小广告
欢迎加入小哈的星球 ,你将获得:专属的项目实战 / 1v1 提问 / Java 学习路线 / 学习打卡 / 每月赠书 / 社群讨论
- 新项目:《从零手撸:仿小红书(微服务架构)》 正在持续爆肝中,基于
Spring Cloud Alibaba + Spring Boot 3.x + JDK 17...
,点击查看项目介绍 ;- 《从零手撸:前后端分离博客项目(全栈开发)》 2 期已完结,演示链接: http://116.62.199.48/ ;
截止目前, 星球 内专栏累计输出 82w+ 字,讲解图 3441+ 张,还在持续爆肝中.. 后续还会上新更多项目,目标是将 Java 领域典型的项目都整一波,如秒杀系统, 在线商城, IM 即时通讯,权限管理,Spring Cloud Alibaba 微服务等等,已有 2900+ 小伙伴加入学习 ,欢迎点击围观
前言
在网页开发中,JavaScript 脚本的加载和执行方式直接影响页面的性能和用户体验。对于编程初学者和中级开发者来说,理解 HTML script defer 属性
的作用和原理至关重要。这个看似简单的属性,实则隐藏着优化页面加载流程的核心逻辑。本文将从基础概念、工作原理、实际案例到对比分析,逐步拆解 defer
属性的使用场景和最佳实践,帮助读者在实际开发中灵活应用这一工具。
什么是 defer 属性?
defer
是 HTML 中 <script>
标签的一个布尔属性,其核心作用是让浏览器延迟脚本的执行,直到 HTML 解析完成。简单来说,当开发者在 <script>
标签中添加 defer
属性时,浏览器会继续解析 HTML 内容,同时在后台加载脚本文件,但不会立即执行脚本中的代码,而是将执行推迟到页面的 DOM 构建完成后。
形象比喻:像“快递暂存点”一样延迟处理
可以将 defer
比作一个快递暂存点:
- 浏览器在解析 HTML 时遇到带有
defer
的脚本,就像快递员发现包裹需要暂存,不会立即送货上门(即执行脚本)。 - 浏览器会先把包裹(脚本文件)存放在暂存点(内存中),继续完成 HTML 的“收货”(解析)。
- 当 HTML 解析完毕(包裹清单整理完毕),快递员再统一派送包裹(执行脚本)。
基本语法示例
<script src="script.js" defer></script>
以上代码表示:
- 浏览器开始下载
script.js
,但不会阻塞 HTML 解析。 - HTML 解析完成后,浏览器执行该脚本。
defer 的工作原理
要深入理解 defer
,需要从浏览器解析 HTML 和执行脚本的流程入手。以下是关键步骤的拆解:
1. HTML 解析与脚本加载的并行阶段
当浏览器遇到 <script>
标签时,默认会暂停 HTML 解析,优先下载并执行脚本。但若添加 defer
属性,浏览器会:
- 继续解析 HTML,如同忽略脚本暂时存在。
- 在后台下载脚本,但不立即执行。
2. DOM 构建完成后统一执行
当 HTML 解析结束(即 DOM 树构建完成),浏览器会:
- 按照脚本在 HTML 中出现的顺序,依次执行所有 deferred 脚本。
- 执行完成后,触发
DOMContentLoaded
事件。
3. 与 DOMContentLoaded
的关系
defer
脚本的执行时机早于 DOMContentLoaded
事件。因此,若在脚本中监听该事件,事件回调会在所有 deferred 脚本执行完毕后触发。
示例代码
<script defer>
console.log('Deferred Script 1');
document.addEventListener('DOMContentLoaded', () => {
console.log('DOM Content Loaded');
});
</script>
<script defer>
console.log('Deferred Script 2');
</script>
<!-- 控制台输出顺序:
Deferred Script 1
Deferred Script 2
DOM Content Loaded -->
defer 的使用场景
并非所有脚本都需要 defer
,但以下场景尤其适合使用这一属性:
场景 1:非阻塞 HTML 解析的外部脚本
若脚本依赖完整的 DOM 结构(如初始化按钮点击事件),但希望 HTML 解析不受影响,可以添加 defer
。例如:
<!-- 非阻塞 HTML 解析 -->
<script defer src="analytics.js"></script>
场景 2:多个外部脚本的顺序执行
当多个脚本存在依赖关系时,defer
可确保它们按 HTML 中的顺序执行。例如:
<script defer src="utils.js"></script> <!-- 先加载基础工具函数 -->
<script defer src="app.js"></script> <!-- 后加载主逻辑,依赖 utils.js -->
场景 3:避免“渲染阻塞”问题
未加 defer
的脚本会阻塞 HTML 解析,可能导致页面“卡顿”。使用 defer
可提升用户体验。
defer 与 async 的对比
defer
和 async
均用于优化脚本加载,但两者的核心逻辑截然不同。以下是关键区别:
特性 | defer | async |
---|---|---|
执行时机 | DOM 构建完成后,按顺序执行 | 脚本下载完成后立即执行(可能中断 HTML 解析) |
执行顺序 | 保持 HTML 中的脚本出现顺序 | 不保证顺序,可能打乱 HTML 脚本顺序 |
适用场景 | 依赖 DOM 的脚本,需按顺序执行 | 独立脚本,无需依赖 DOM 或其他脚本 |
形象比喻:餐厅点餐 vs. 自助取餐
- defer:像在餐厅点餐后,服务员先继续服务其他顾客(解析 HTML),等所有菜准备好(DOM 构建完成)后,再按点单顺序上菜(执行脚本)。
- async:像自助餐厅,每道菜准备好后立即取用,可能打乱原本的点餐顺序。
实际案例:优化页面加载性能
假设有一个电商页面,需要加载以下资源:
- 第三方分析脚本(非关键,但需尽早加载)。
- 网站核心功能脚本(依赖 DOM)。
- 广告脚本(可延迟加载,不影响核心功能)。
优化前的代码(未使用 defer/async)
<!-- 阻塞 HTML 解析 -->
<script src="analytics.js"></script>
<script src="core.js"></script>
<script src="ads.js"></script>
问题:
analytics.js
和ads.js
可能阻塞 HTML 解析,导致页面渲染延迟。
优化后的代码
<!-- 非阻塞关键脚本 -->
<script defer src="core.js"></script>
<!-- 异步加载非关键脚本 -->
<script async src="analytics.js"></script>
<script async src="ads.js"></script>
效果:
core.js
在 DOM 构建后按顺序执行,不影响 HTML 解析。analytics.js
和ads.js
并行加载且不阻塞页面渲染。
使用 defer 的注意事项
尽管 defer
功能强大,但开发者需注意以下细节:
1. 不支持内联脚本的依赖关系
如果内联脚本依赖外部 deferred 脚本,可能会因执行顺序问题导致错误。例如:
<!-- 错误示例:内联脚本先于外部脚本执行 -->
<script defer src="math.js"></script>
<script defer>
console.log(add(2,3)); // math.js 未执行,add() 未定义!
</script>
解决方法:将内联脚本移至外部文件,或确保其在依赖的脚本之后加载。
2. 浏览器兼容性
defer
属性在现代浏览器中广泛支持,但需注意 IE 8 及以下版本可能存在问题。可通过 Can I Use 验证目标浏览器的兼容性。
3. 避免过度使用 defer
并非所有脚本都需要 defer
。例如,若脚本需立即执行(如检测浏览器环境),则应省略该属性。
结论
HTML script defer 属性
是优化网页性能和提升用户体验的利器。通过延迟脚本的执行,开发者可以平衡 HTML 解析速度与脚本功能的实现。掌握 defer
的工作原理、使用场景及与 async
的区别,能帮助开发者在实际项目中做出更合理的设计决策。
在未来的开发中,建议将关键功能脚本标记为 defer
,并合理使用 async
处理非关键资源,从而构建出加载快速、交互流畅的网页应用。
通过本文的深入解析,希望读者能对 HTML script defer 属性
有全面的认知,并在实际开发中灵活运用这一技术,优化代码性能与用户体验。