【深入理解 script 标签的 async,defer 属性(同步加载,异步加载,延迟执行)】


1. 异步加载 和 延迟执行 存在的意义 : 避免影响 页面渲染/显示

  • 浏览器解析(渲染/显示)的 阻塞问题
    • 加载和解析 顺序: 浏览器在 解析 HTML 页面代码时, 是 从上到下 依次 加载, 解析、渲染
    • 阻塞 1: 外部脚本 js 文件的 加载和执行, 影响 页面渲染(显示).
      • 如果 , 在<head>中 ,用<scrip> 引用了一个 外部脚本 js 文件
        • <script src="home.js" type="text/javascript"></script>
      • 而且这个 文件很大或者有问题,需要 2 秒的加载和执行时间,那么在这个时间中, 浏览器 会停止渲染页面,页面就会是 白屏显示,什么内容 都没有),2 秒后 脚本加载和执行完成 才会继续渲染页面 ,页面才显示.
      • 用户体验 差: 这个时候 用户体验就很不好,因为有几秒什么也看不见,急脾气的用户,可能会直接关闭 页面.
      • 这种影响 页面解析(渲染,显示)的行为, 就是阻塞。
    • 阻塞 2: 外部 css 文件的加载和执行, 影响 页面渲染(显示).
      • CSS 文件 也一样,因为 CSS 文件 会对DOM的 样式,布局,色彩等外观 产生影响,所以浏览器会等 CSS 文件 下载并执行完成后 继续。
        为了页面的性能,要避免阻塞。

2. 同步 加载和执行 (影响 页面解析/渲染/显示)

  • 默认 同步加载: 脚本 <script>标签 默认用的都是 同步加载。

    • <script src="imagescript.js"></script>
  • 同步模式

    • 又称 阻塞 模式,会阻止 浏览器的后续处理,停止了 后续的文件的 解析
      • 立即加载: 如果async,defer 两个属性 都不存在,则立即加载(获取) 脚本
      • 立即执行: 加载完成后, 立刻执行 脚本, 直到 脚本加载和执行 完成,才能进行 页面的解析/渲染/显示。
      • 这个时候,加载和执行 脚本, 都会 阻塞(打断)解析,多多少少 都会影响页面的显示速度.
  • 使用 同步加载和执行 的时机:

    • 浏览器 需要采用 同步模式,一般因为 加载的 js文件中 有对 dom 的操作,如, 重定向 等 默认行为,所以同步才是 最安全的。
  • <script>标签 放在 </body>前的意义:

    • 一般来说, <script> 都是放在 <head>中,来引用 外部脚本 js 文件的
    • 也会把要加载的 js文件 <script> 放到 </body>之前,使得js 文件 可 在 页面最后 加载,尽量减少 阻塞页面的渲染/显示。这样可以 先让页面显示出来.
  • 同步加载的 加载和执行 过程示例图

    • 加载:不是 并行的,会打断 页面解析
    • 执行: 不是 并行的, 会打断 页面解析
      • 加载和执行,都会 打断页面解析,影响 页面显示.
    • 在这里插入图片描述

  • 示例1: 下图, 是在 淘宝网的代码中 截的图,可以看到 把很多<script>标签 ,放在了 文档底部的位置,在 文档主体 结束标签 </body> 之前.

在这里插入图片描述


  • 示例2: 下图,是在 MDN 网站的截图, 也有 <script>标签,被放在了 文档的底部,</body> 标签之前.

在这里插入图片描述


3. async 属性: 异步加载 (不是 异步执行,搭配 src 属性,仅限 外部脚本)

  • 脚本的 异步加载:

    • 并行加载(获取): 对于脚本,如果有async属性,那么脚本 将在 页面解析过程中被 并行获取
    • 加载/下载完 立即执行: 并在 脚本文件加载(下载)完成 可用时(可能在 页面解析完成 之前) 进行执行。
    • 执行 会打断解析: 加载 不打断页面解析,但 执行会打断 页面解析,记住,只是异步加载,执行 并不是异步的,还要回到原来的 流水线上.
    • 注意: 搭配 src 事项,异步加载, 仅限 外部脚本
  • 模块脚本(module scripts)的 异步加载

    • 并行获取对于模块脚本,如果有 异步加载async属性,那么将在解析过程中 并行地获取 模块脚本及其所有依赖项
    • 下载完成 立即执行: 并且一旦模块脚本可用(可能在 页面解析完成之前),就会对其进行执行。
      • 否则,模块脚本 及其依赖项 将在解析过程中 并行获取,并在页面完成解析后 进行执行。
    • : 延迟执行 defer属性 对模块脚本 没有影响。
  • 异步加载async的 加载和执行 过程示例图

    • 加载:是并行的,不影响 页面解析
    • 执行: 不是并行的, 会打断 页面解析
      • 加载 不打断页面解析,执行 会打断页面解析.在这里插入图片描述

  • 示例1: 在MDN网站的 文档头部标签<head>中,也出现了 异步加载的 js文件 <script>
    • 在这里插入图片描述

4. defer 属性: 延迟执行 (不是 延迟加载,加载是 并行的,搭配 src 属性,仅限 外部脚本 )

  • 延迟执行: defer

    • 并行获取: 如果 异步加载 async属性 不存在,而 延迟执行defer属性 存在,那么脚本将被 并行获取
      • 加载/获取/下载,都是一个意思
    • 延迟执行: 并在页面完成解析后 进行执行。
    • 注意: 搭配 src 属性,延迟执行, 仅限 外部脚本
  • 延迟执行 defer的 加载和执行 过程示例图

    • 加载:是并行的,不影响 页面解析
    • 执行: 不是并行的, 但不会打断 页面解析,因为是在 页面解析完成后 才执行的.
    • 加载和执行,都不会打断 页面解析.
    • 在这里插入图片描述
  • 延迟执行的 使用时机

    • 延迟执行:
      • 有些 js代码 并不是 页面初始化的时候 就立刻需要的,而在 稍后的某些情况才需要的。
      • 那么一开始 并不执行 这些暂时不用的 js,而是在需要的时候 或稍后 再通过js 的控制来执行,也不会影响。
  • 根据 js 的需要时机 进行分类:

    • js 切分成许多模块,页面初始化时 只执行 需要立即执行的 js 可以使用 同步加载 ,可以放在headscript标签中
    • 其它 js ,可以延迟执行。
      • 比如, 页面有大量不同的模块 组成,很多可能 暂时不用或根本就没用到。
      • 就像图片的延迟加载,在图片出现在可视区域内时(在滚动条下拉)才加载显示图片.
  • 延迟加载: 除了延迟执行,其实也可以设置 延迟加载,就是把 代码片段,写在 文档的底部,</body>的前面,这样 文档按顺序加载和解析的时候,到最后 才会加载这些代码片段.

  • 注意: defer只是 延迟执行,并不是 延迟加载,在页面解析的同时,就并行加载了,也不会影响页面解析.

  • 冲突事项:

    • 用了defer不要使用 document.write() 方法;(为什么 ?因为涉及到 页面显示吗?延迟执行了,会影响页面显示?)

  • 示例1: MDN 网站中,在 文档的 底部,</body>之前,就添加了 延迟执行的 脚本文件.

在这里插入图片描述


  • 示例2: 延迟加载 (无关defer)
    • 如果 页面 初始的渲染, 并不依赖于 js 或者 css, 可以用 延迟加载
    • 即,最后加载js, css代码,把 引用外部文件的代码 写在最后 </body>前面。
    • 比如,一些按钮的 点击事件,轮播图动画的脚本 也可以放在最后。
<html>
   <head>
   </head>
   
   <body>

     <!--延迟加载  -->
     <script type="text/javascript" src="a.js"></script>
     <link href="a.css" rel="stylesheet" />
  </body>
</html>

5. 同步加载,异步加载,延迟执行的 联系和区别

  • ⑴ 同步加载,异步加载async,延迟执行 defer加载 和 执行 示例图

    • 绿色: 页面解析(渲染/显示)
    • 蓝色: 脚本 加载(下载/获取)
    • 红色: 脚本执行
    • 在这里插入图片描述
  • 同步加载的 加载和执行 过程

    • 加载:不是 并行的,会打断 页面解析
    • 执行: 不是 并行的, 会打断 页面解析
      • 加载和执行,★ 都会 打断页面解析
  • 异步加载async的 加载和执行 过程

    • 加载:是并行的,不影响 页面解析
    • 执行: 不是并行的, 会打断 页面解析
      • 加载 不打断页面解析,★执行 会打断页面解析.
  • 延迟执行 defer的 加载和执行 过程

    • 加载:是并行的,不影响 页面解析
    • 执行: 不是并行的, 但不会打断 页面解析,因为是在 页面解析完成后 才执行的.
    • 加载和执行,★都不会 打断页面解析.

  • 同时 指定 async,defer 属性: (行为回退,避免 阻塞行为)

    • 行为 回退: 即使指定了 异步加载async属性,也可以指定 defer属性
      • 回退到 延迟执行: 以使 仅支持 延迟执行defer(而不支持 异步加载async)的遗留Web浏览器 退回到 延迟执行defer行为,而不是 默认的阻塞行为。
  • 多个脚本的 执行顺序:

    • 同步加载
      • 按顺序: 放置在<head>
        • 会阻塞<body>的渲染, 会出现白屏,按照顺序 立即执行几个脚本文件
      • ★ 按顺序: 放置在<body>底部
        • <body>中的内容 渲染完毕后, 再加载, 顺序执行js
          • <script>写在<body>底部,没有 兼容性问题,没有 白屏问题,没有 执行顺序问题.
        • 是比较推荐的写法,很多网站都在使用,比如 淘宝网, MDN 网站等.
    • 异步加载: 放置在<head>头部并使用async
      • 先加载完毕的 先执行: 异步 并行加载资源,且加载完JS资源立即执行,并不会按 js 文件的顺序,谁快 谁先上
    • 延迟执行: 放置在<head>头部并使用defer
      • 按 顺序执行: 异步 并行加载资源,在 DOM 渲染后,即,页面解析完毕后, 再按顺序执行 js
      • HTML5 规范要求 脚本按照它们出现的 先后顺序执行,因此 第一个defer 延迟脚本, 会先 于第二个延迟脚本执行,而这两个脚本 会先于 DOMContentLoaded 事件执行。
      • 在实际应用当中,延迟执行的脚本 并不一定会按照 顺序执行,也不一定会在DOMContentLoad 时间触发前 执行,因此最好只包含一个延迟脚本。
    • ★ 异步加载 和延迟执行: 放置在<head>头部, 并同时使用asyncdefer
      • 表现和async一致
      • 不支持async的,支持defer的,会表现得和defer一样
        • 异步加载 比延迟执行的 优先级高些.
      • 这种方法,也是比较推荐的,同时兼容 支持asyncdefer 两个属性的 浏览器
  • 仅限 外部脚本: 脚本<script>标签的 异步加载 async和 延迟执行defer,都必须搭配 src 属性,仅限 外部脚本 使用.


  • 示例1: 在 MDN 网站中,也出现了 同时设置 异步加载和延迟执行的 <script> 标签.
  • 在这里插入图片描述

结束语


  • 转载 请注明出处 ,Thanks♪(・ω・)ノ
    • 作者:Hey_Coder
    • 来源:CSDN
    • 原文:https://blog.csdn.net/VickyTsai/article/details/102841293
    • 版权声明:本文为博主原创文章,转载请附上博文链接!

在这里插入图片描述

  • 14
    点赞
  • 38
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值