js异步加载

浏览器渲染页面的过程

从耗时的角度,浏览器请求、加载、渲染一个页面,时间花在下面五件事情上:

  • DNS 查询
  • TCP 连接
  • HTTP 请求即响应
  • 服务器响应
  • 客户端渲染

主要说说第五个部分,即浏览器对内容的渲染,这一部分(渲染树构建、布局及绘制),又可以分为下面五个步骤:

  • 处理 HTML 标记并构建 DOM 树。(dom)
  • 处理 CSS 标记并构建 CSSOM 树。(cssom)
  • 将 DOM 与 CSSOM 合并成一个渲染树。(render tree)
  • 根据渲染树来布局,以计算每个节点的几何信息。(layout)
  • 将各个节点绘制到屏幕上。(paint)

需要明白,这五个步骤并不一定一次性顺序完成。如果 DOM 或 CSSOM 被修改,以上过程需要重复执行,这样才能计算出哪些像素需要在屏幕上进行重新渲染。实际页面中,CSS 与 JavaScript 往往会多次修改 DOM 和 CSSOM,下面就来看看它们的影响方式。

阻塞渲染:CSS 与 JavaScript

现代浏览器总是并行加载资源。例如,当 HTML 解析器(HTML Parser)被脚本阻塞时,解析器虽然会停止构建 DOM,但仍会识别该脚本后面的资源,并进行预加载。
同时,由于下面两点:

  • 默认情况下,CSS 被视为阻塞渲染的资源,这意味着浏览器将不会渲染任何已处理的内容,直至 CSSOM 构建完毕。
  • JavaScript 不仅可以读取和修改 DOM 属性,还可以读取和修改 CSSOM 属性。

存在阻塞的 CSS 资源时,浏览器会延迟 JavaScript 的执行和 DOM 的解析。另外:

  • 当浏览器遇到一个 script 标记时,DOM 构建将暂停,直至脚本完成执行。
  • JavaScript 可以查询和修改 DOM 与 CSSOM。
  • CSSOM 构建时,JavaScript 执行将暂停,直至 CSSOM 就绪。

所以,script 标签的位置很重要。实际使用时,可以遵循下面两个原则:

  • CSS 优先:引入顺序上,CSS 资源先于 JavaScript 资源。
  • JavaScript 应尽量少影响 DOM 的构建。

异步加载js方式有哪些

defer和async是script标签的两个属性,用于在不阻塞页面文档解析的前提下异步加载脚本,控制脚本的下载和对应的执行时机
js异步加载

异步加载脚本是相对于html文档解析

1
<script src="../XXX.js" defer or async></script>

默认的script脚本

1
<code><script type="text/javascript" src="x.min.js"></script></code>

当浏览器遇到 script 标签时,文档的解析将停止,并立即下载并执行脚本,脚本执行完毕后将继续解析文档。也就是说 脚本文件的下载和执行是与文档解析同步进行,也就是说,它会阻塞文档的解析,如果控制得不好,在用户体验上就会造成一定程度的影响

defer (html4)

异步加载脚本,加载文档的时候,文档的解析不会停止,等到文档解析完成之后,才会执行脚本

注意点:

1.只适用于外联脚本(含有src属性),不会对内联脚本有效
2.多个defer脚本按照下载的顺序执行,按序执行
3.执行时机是在文档解析完成之后,也就是DOMContentLoaded之前执行(整个 document 解析完毕且 defer-script 也加载完成之后(这两件事情的顺序无关),会执行所有由 defer-script 加载的 JavaScript 代码,然后触发 DOMContentLoaded 事件。这句话当中的document文档解析完毕后不一定会触发DOMContentLoaded 事件,得等到defer-script执行完成之后)

相比于默认脚本

1.载入 JavaScript 文件时不阻塞 HTML 的解析
2.执行阶段被放到 HTML 标签解析完成之后。

async (html5)

异步加载脚本,加载完会立即执行,执行的时候会阻塞html文档解析,执行完异步脚本之后,html文档会继续解析
注意点:
1.也是只适用于外联脚本,和defer保持一致
2.多个async脚本,下载和执行也是异步的,不能保证执行顺序
3.执行时机,一定是在window.onload之前执行,但不能确保是与DOMContentLoaded的执行先后顺序

defer和async区别

一句话概括:
async是异步加载,加载完会立即执行,执行的时候会阻塞html文档解析。
defer也是异步加载脚本,等Html文档解析完成之后再去执行脚本

js异步加载

document.createElement

使用 document.createElement 创建的 script 默认是异步的,示例如下。

1
console.log(document.createElement("script").async); // true

所以通过动态添加 script 标签引入 JavaScript 文件默认是不会阻塞页面的。如果想同步执行,需要将 async 属性人为设置为 false。
如果使用 document.createElement 创建 link 标签会怎样呢?

1
2
3
4
var link = document.createElement('link')
link.rel = 'stylesheet'
link.href= 'index.css'
document.head.appendChild(link) // 也不会阻塞页面解析

参考