JavaScript | async、defer与动态加载

原文:JavaScript | async、defer与动态加载
前言:一直不太理解script标签中的async、defer相关的关键字,所以打算写一篇随笔记录下这块的知识点

常见的script标签有两种,一般分为内联脚本以及外联脚本:

  • 内联:

    html 复制代码
    <script>
      alert('明华大鼬');
    </script>
  • 外联:

    html 复制代码
    <script scr="../a.js"></script>

是上面这种引入方式都算是直接引入的,是同步加载的脚本,而在html解析的过程中遇到同步脚本,停止解析,加载脚本,执行脚本,完成后再继续解析html文档。

同步脚本的解析:

注意:这也是为什么不推荐把script标签写在<head>中,对于需要很多 JavaScript 的页面,这会导致页面渲染的明显延迟,在此期间浏览器窗口完全空白,为解决这个问题,现代 Web 应用程序通常将所有 JavaScript 引用放在<body>元素中的页面内容后面。------《JavaScript高级程序设计(第4版)》

defer

从HTML 4.01开始<script>元素定义了一个叫 defer 的属性

  1. 只对外部脚本文件有效果 (外部资源中不可以使用document.write
  2. 表示脚本立即下载(异步,不影响文档的加载,不会造成阻塞),但延迟到文档完全被解析和显示之后再执行
  3. DOMContentLoaded事件之前执行
  4. HTML5 规范要求defer脚本应该按照它们出现的顺序执行
html 复制代码
<script scr="../a.js" defer></script>
<script scr="../b.js" defer></script>

上面这种情况b.js就会在a.js之后执行

async

HTML5为<script>元素定义了 async 属性

  1. 只对外部脚本文件有效果 (外部资源中不可以使用document.write

  2. 表示脚本立即下载(异步,不影响文档的加载),异步下载完Js文件之后,立即执行,阻塞了文档的解析

  3. 在一些情况下,如果Js文件还没下载完成,而此时文档已经加载完毕后,也是立即执行,但是此时不会阻塞文档的解析

  4. 属性为 async 的脚本并不保证能按照它们出现的次序执行,完全依赖于网络传输结果,谁先到执行谁

  5. 通过2、3两个点可以看出使用 async 属性的外部脚本执行时间不确定,如果在异步(async) js 脚本中获取某个 DOM 元素,有可能获取到也有可能获取不到,所以不推荐通过此种方式引入的js文件中包含操作dom元素的代码

  6. 一定会在页面的 load 事件前执行,但可能会在 DOMContentLoaded 事件之前或之后执行

async与defer的区别

  • async 标志的脚本文件一旦加载完成,会立即执行,会在页面的 load 事件前执行,但可能会在 DOMContentLoaded 事件之前或之后执行,对于那些可以不依赖任何脚本或不被任何脚本依赖的脚本来说却是非常合适的,最典型的例子:Google Analytics
  • 使用了 defer 属性的脚本文件,在 DOMContentLoaded 事件之前执行(load 事件的执行事件也是在DOMContentLoaded 事件之前)
  • HTML5 规范要求defer脚本应该按照它们出现的顺序执行,async脚本并不保证能按照它们出现的次序执行,完全依赖于网络传输结果,谁先到执行谁

预解析

chrome浏览器有预解析的机制,会在解析文档之前查看有没有外部文件,如果有就交给下载进程同步下载

当渲染引擎收到字节流之后,会开启一个预解析线程,用来分析 HTML 文件中包含的 JavaScript、CSS 等相关文件,解析到相关文件之后,预解析线程会提前下载这些文件。

预解析和 async 的区别:

  1. 下载时机
    1. 预解析线程可以在解析文档线程之前就解析到HTML页面是否有引用外部资源,如果有的话,就立即交给下载进程下载
    2. async 只有在解析文档线程解析到有外部资源,才去发起下载
  2. 作用对象
  3. 预解析对js文件,和css文件都有效
  4. async 只能用于js资源
  5. 预解析存在的意义
  6. 当文档解析到js资源的时候,解析文档的工作会被阻塞。如果js资源是外部引入的,阻塞的时间就包括js资源下载的时间
  7. 预解析就是对于没有添加 async 属性的script的标签,做的优化

预解析和 async 的相同点:

  1. 下载js资源的时候,不会阻塞文档的解析

动态加载脚本

除了<script>标签,还有其他方式可以加载脚本。

以前没有async以及defer的时候,此时一般都是使用JavaScript提供DOM相关的API动态的向页面上添加指定的脚本文件:

javascript 复制代码
let script = document.createElement('script');
script.src = 'a.js';
document.head.appendChild(script); 

一些注意的点:

  • 默认情况下这种方式创建的script标签是异步加载的,相当于async的形式,在页面执行到添加script标签之前是不会发送请求的。

  • 早期一些浏览器不支持async属性,如果要统一动态脚本的加载行为,可以明确将其设置为同步加载:

    javascript 复制代码
    let script = document.createElement('script');
    script.src = 'a.js';
    script.async = false;
    document.head.appendChild(script); 
  • 通过动态加载的方式加载脚本可能会影响性能,可以在文档头部显示声明,提前告诉浏览器此文件的存在

    html 复制代码
    <link rel="preload" href="abc.js">

参考文献

相关推荐
小远yyds15 分钟前
前端Web用户 token 持久化
开发语言·前端·javascript·vue.js
阿伟来咯~1 小时前
记录学习react的一些内容
javascript·学习·react.js
吕彬-前端1 小时前
使用vite+react+ts+Ant Design开发后台管理项目(五)
前端·javascript·react.js
学前端的小朱1 小时前
Redux的简介及其在React中的应用
前端·javascript·react.js·redux·store
guai_guai_guai1 小时前
uniapp
前端·javascript·vue.js·uni-app
也无晴也无风雨1 小时前
在JS中, 0 == [0] 吗
开发语言·javascript
bysking2 小时前
【前端-组件】定义行分组的表格表单实现-bysking
前端·react.js
王哲晓3 小时前
第三十章 章节练习商品列表组件封装
前端·javascript·vue.js
fg_4113 小时前
无网络安装ionic和运行
前端·npm
理想不理想v3 小时前
‌Vue 3相比Vue 2的主要改进‌?
前端·javascript·vue.js·面试