HTML加载过程

1. 两个引擎

浏览器的引擎可以分为渲染引擎和 JS 引擎。 JS 引擎相对独立,而渲染引擎又包括 HTML 解释器、 CSS 解释器、布局、图形、视频、图片解码器等。

JS 引擎独立于渲染引擎,而浏览器加载网页需要 JS 引擎和渲染引擎相互协作。一般情况下,当 HTML 解释器遇到 <script> 标签时,浏览器会将控制权交给 JS 引擎,JS 引擎对内联的代码会直接执行,对外部的 JS 文件需要先下载再执行。当 JS 引擎执行完毕,浏览器又会将控制权交给渲染引擎,继续构建 CSSOM 和 DOM。

但这种写作方式显然有问题,当下载 JS 文件时,会阻塞渲染引擎的工作导致页面加载的延迟,所以才有了 async 和 defer。

HTML文档解析的三种情况

就 JS 同步执行而言,HTML 文档的解析会遇到三种情况:

  • HTML

此时,渲染引擎会直接将 HTML 元素解析成 DOM 树然后进行渲染等操作,在 DOM 树生成的同时触发 DOMContentLoaded 事件。

  • HTML+CSS

此时,仍然会将 HTML 元素解析成 DOM树,并在 DOM 树生成的同时触发 DOMContentLoaded 事件,但是不同的是,渲染树的生成在有 CSS 参与时,是通过 DOM + CSSOM 共同来渲染的,所以渲染过程的开始是在 DOM 和 CSSOM 都生成完成之后。

  • HTML+CSS+JS

同步的情况下,解析 HTML 元素时,如果遇到<script>标签,会有两种操作:获取 JS 代码、执行 JS 代码。如果此时正在生成 CSSOM,那么获取 JS 代码的操作可以和 CSSOM 的构建同时执行,但是只有菜 CSSOM 构建完成之后才能执行 JS 代码。如果<script>标签先于 CSS,那么就不存在等待 CSSOM 的过程。

**获取 JS 代码:**获取操作可以和 CSSOM 的构建同时执行。如果是内联 JS ,则不存在下载的操作,就只有执行操作,如果是外部 JS 文件,那么此时会去下载 JS 文件。
**执行 JS 代码:**需要等待 CSSOM 构建完成之后才可以执行。

首屏时间

首屏时间可以理解为 DOMContentLoaded 事件的触发时间。另外,JS 代码写在 HTML 文件的头部和尾部(前)对首屏时间没有影响。其作用是确保 JS 代码执行时, DOM 和 CSSOM树已经解析完毕,在获取元素时不会出现获取为 null 的情况。

普通加载

普通加载就是上文所介绍的同步加载,不再赘述。

写法:

<script src="javascript.js"></script>

async

意义:异步加载 JS 文件,加载完成后直接执行。

写法:

<script src="javascript.js" async></script>

既然只是异步加载,那么加载完成后可能的结果:

  • HTML 文档解析完毕
  • HTML 文档未解析完毕

其实大部分情况下,async 加载的 JS 脚本会在 HTML 解析完毕之后执行。因为涉及到下载,而 HTML 是直接解析当前的 HTML 文件,所以如果 HTML 和 CSS 中的元素太多太复杂,导致 CSSOM 和 DOM 的解析时间过长,这个时候就会出现 JS 文件下载完毕准备执行时,HTML 文档仍然没有解析完毕。

所以,大部分情况下,不想阻塞 HTML 的解析时,使用 async 就够用了,如果涉及到 JS 脚本的执行顺序或者是为了更加严谨起见,可以使用 defer。

defer

意义:异步加载 JS 文件,全部 JS 文件加载完毕后,且 HTML 文档解析完毕之后,按顺序执行 JS 文件,最后触发 DOMContentLoaded 事件。

写法:

<script src="javascript.js" defer></script>

过程如下:

这里有几个点需要注意:

  • 执行 JS 文件是按顺序执行的(特殊情况除外,后文有讲)
  • 执行 JS 文件是在 HTML 文档解析完毕之后,但是实在触发 DOMContentLoaded 事件之前
  • 同样是异步加载 JS 文件,不会阻塞 HTML 文档的解析

验证

验证:
HTML 代码如下:

<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><meta http-equiv="X-UA-Compatible" content="ie=edge"><title>Document</title><script src="script.js" async></script></head><body><script>document.addEventListener("DOMContentLoaded", function() {console.log("DOMContentLoaded");});</script><div><div class="box">盒子1</div>此处省略 n 个 div<div class="last">盒子2</div></div></body>
</html>

JS 代码如下:

console.log("script.js运行")
var lastBox = document.querySelector(".last")
console.log(lastBox)

运行情况如下:

  1. 当 div 个数较少时,打印结果总是如下:
DOMContentLoaded
script.js运行
<div class=​"last">​盒子2​</div>​
  1. 当 div 个数逐渐增加到临界值时,打印结果:
script.js运行
<div class=​"last">​盒子2​</div>​
DOMContentLoaded

或者是:

script.js运行
null
DOMContentLoaded
  1. 当 div 超过临界值时,结果总是:
script.js运行
null
DOMContentLoaded

当修改成 defer 之后,无论 div 个数为多少,结果总是:

script.js运行
<div class=​"last">​盒子2​</div>​
DOMContentLoaded

defer不按顺序执行

有时候即使写了 defer 也不会按照顺序执行,但是多个 JS 文件之前又确实存在先后的依赖关系。此时的解决方案是只写一个 defer ,也就是只包含一个延迟脚本。解决方法又两个,一个是写到一个 JS 文件中,使用 defer 加载这个 JS 文件。另外一个方法就是依赖文件使用同步的方式加载,后执行的 JS 文件使用 defer 异步加载。

如:

<script src="first.js" defer></script>
<script src="seconde.js" defer></script>

改成:

<script src="first.js"></script>
<script src="seconde.js" defer></script>

async 和 defer相关推荐

  1. Script标签的async和defer

    之前有写过HTML页面渲染过程,知道了JavaScript是会阻塞DOM解析的,所以我们会把script标签放到底部防止阻塞HTML解析.其实script还有两个属性,async和defer,也是可以 ...

  2. 浅谈script标签中的async和defer

    script标签用于加载脚本与执行脚本,在前端开发中可以说是非常重要的标签了. 直接使用script脚本的话,html会按照顺序来加载并执行脚本,在脚本加载&执行的过程中,会阻塞后续的DOM渲 ...

  3. script标签中的async和defer

    在程序中代码是一行一行执行的,html标签都是由渲染引擎来执行,代码执行时从上往下一行一行执行,当执行到alert(如下图),alert会阻塞后面代码的执行,当点击完确定之后,代码继续往下执行. ja ...

  4. script async和defer

    1.没有async和defer,html解析时遇到script标签,会先去下载js文件,文件加载完成立即执行,执行完了再开始解析后面的html,是一个顺序流的过程 2.async,加载和渲染后续文档元 ...

  5. async与defer

    <script>元素的几种常见属性: async  异步加载,立即下载,不应妨碍页面其他操作,标记为 async 的异步脚本并不保证按照指定的先后顺序执行,因此异步脚本不应该在加载期间修改 ...

  6. script标签中async与defer的区别

    script标签内未设置async或者defer时: (1)script放在<head>,会阻塞HTML代码的解析和渲染,而放在<body>底部时,不会阻塞HTML代码的解析和 ...

  7. script标签async和defer的区别及作用

    作用: 1.没有 defer 或 async,浏览器会立即加载并执行指定的脚本,也就是说不等待后续载入的文档元素,读到就加载并执行. 2.async 属性表示异步执行引入的 JavaScript,与 ...

  8. html中body末尾的script,【笔记】JS脚本为什么要放在body最后面以及async和defer的异同点...

    1.没有defer或async 浏览器遇到脚本的时候会暂停渲染并立即加载执行脚本(外部脚本),"立即"指的是在渲染该 script 标签之下的文档元素之前,也就是说不等待后续载入的 ...

  9. JS中关于async和defer作用与区别

    <script src="script.js"></script> 没有 defer 或 async,浏览器会立即加载并执行指定的脚本,"立即&q ...

  10. HTML5 script元素async,defer异步加载

    原文地址:HTML5′s async Script Attribute (译者注: 异步加载,可以理解为无阻塞并发处理.) (译者再注: 建议使用 defer,但是经测试发现 defer 属性对页面内 ...

最新文章

  1. 352万帧标注图片,1400个视频,亮风台推最大单目标跟踪数据集
  2. Seaborn使用lmplot函数可视化散点图并添加回归曲线以及回归线对应的置信区间(Scatter plot with regression line)
  3. 【2018第五届世界互联网大会】世界互联网领先科技成果发布:带你看看这15项“黑科技”...
  4. MQTT协议之发布订阅
  5. RabbitMq(九) SpringBoot整合RabbitMQ消费者示例代码
  6. ASP.NET Core Web读取appsettings.json中的配置
  7. 推荐几个Python+OpenCV实战项目
  8. android 卡片消息,安卓QNotified 支持xml卡片QQ消息 - 陌路人博客
  9. PG概述及OSD对PG状态的影响
  10. macOS中修改hosts文件
  11. phpMyAdmin4.8.1漏洞复现及利用
  12. 星浩资本-以流程为中心
  13. windows下gromacs中文教程(simulate chain A of insulin (PDB ID: 1ZNI).
  14. java简单的音乐播放器编程_简单实现java音乐播放器
  15. 华为云物联网平台的微信小程序开发
  16. 【QT-3】tableWidget控件
  17. k8s探针检测php,K8S教程(7)使用探针对容器进行健康检查
  18. 透析SpringBoot jar可执行原理
  19. 股市投资必修课二十二---资本回报率
  20. 动手开发自己的mvc-3----容器该帮我们做什么?(非常的重点)

热门文章

  1. Spring Boot 中实现定时任务的常用方式--Quartz
  2. 关于synchronized
  3. objective-C 数据类型转换
  4. UTF-8 encode decode 集合
  5. java基础-基础类型包装类型
  6. Linux进程管理---task_struct
  7. 流式计算之Storm简介
  8. Linux进程线程学习笔记
  9. 安装卸载Windows服务
  10. [Publish AAR To Maven] 注册 Maven 仓库 sonatype.org 账户