欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

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

程序员文章站 2022-04-29 12:56:50
...


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 文件 可 在 页面最后 加载,尽量减少 阻塞页面的渲染/显示。这样可以 先让页面显示出来.
  • 同步加载的 加载和执行 过程示例图

    • 加载:不是 并行的,会打断 页面解析
    • 执行: 不是 并行的, 会打断 页面解析
      • 加载和执行,都会 打断页面解析,影响 页面显示.
    • 【深入理解 script 标签的 async,defer 属性(同步加载,异步加载,延迟执行)】

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

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


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

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


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

  • 脚本的 异步加载:

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

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

    • 加载:是并行的,不影响 页面解析
    • 执行: 不是并行的, 会打断 页面解析
      • 加载 不打断页面解析,执行 会打断页面解析.【深入理解 script 标签的 async,defer 属性(同步加载,异步加载,延迟执行)】

  • 示例1: 在MDN网站的 文档头部标签<head>中,也出现了 异步加载的 js文件 <script>
    • 【深入理解 script 标签的 async,defer 属性(同步加载,异步加载,延迟执行)】

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

  • 延迟执行: defer

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

    • 加载:是并行的,不影响 页面解析
    • 执行: 不是并行的, 但不会打断 页面解析,因为是在 页面解析完成后 才执行的.
    • 加载和执行,都不会打断 页面解析.
    • 【深入理解 script 标签的 async,defer 属性(同步加载,异步加载,延迟执行)】
  • 延迟执行的 使用时机

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

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

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

  • 冲突事项:

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

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

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


  • 示例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加载 和 执行 示例图

    • 绿色: 页面解析(渲染/显示)
    • 蓝色: 脚本 加载(下载/获取)
    • 红色: 脚本执行
    • 【深入理解 script 标签的 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> 标签.
  • 【深入理解 script 标签的 async,defer 属性(同步加载,异步加载,延迟执行)】

结束语


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

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