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>  

以上代码表示:

  1. 浏览器开始下载 script.js,但不会阻塞 HTML 解析。
  2. 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 的对比

deferasync 均用于优化脚本加载,但两者的核心逻辑截然不同。以下是关键区别:

特性deferasync
执行时机DOM 构建完成后,按顺序执行脚本下载完成后立即执行(可能中断 HTML 解析)
执行顺序保持 HTML 中的脚本出现顺序不保证顺序,可能打乱 HTML 脚本顺序
适用场景依赖 DOM 的脚本,需按顺序执行独立脚本,无需依赖 DOM 或其他脚本

形象比喻:餐厅点餐 vs. 自助取餐

  • defer:像在餐厅点餐后,服务员先继续服务其他顾客(解析 HTML),等所有菜准备好(DOM 构建完成)后,再按点单顺序上菜(执行脚本)。
  • async:像自助餐厅,每道菜准备好后立即取用,可能打乱原本的点餐顺序。

实际案例:优化页面加载性能

假设有一个电商页面,需要加载以下资源:

  1. 第三方分析脚本(非关键,但需尽早加载)。
  2. 网站核心功能脚本(依赖 DOM)。
  3. 广告脚本(可延迟加载,不影响核心功能)。

优化前的代码(未使用 defer/async)

<!-- 阻塞 HTML 解析 -->  
<script src="analytics.js"></script>  
<script src="core.js"></script>  
<script src="ads.js"></script>  

问题:

  • analytics.jsads.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.jsads.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 属性 有全面的认知,并在实际开发中灵活运用这一技术,优化代码性能与用户体验。

最新发布