js的异步加载

2020-08-25 loading

# js 正常顺序加载

js 会阻塞 dom 的渲染——解析过程中,浏览器发现 script 元素,就暂停解析,把网页渲染的控制权转交给 JavaScript 引擎。

  • DOMContentLoaded 事件在 load 之前执行
  • DOMContentLoaded/load 在所有 dom 渲染、js 加载之后执行
  • DOMContentLoaded/load 不等待 js 异步事件的执行
  • DOMContentLoaded/load 等待 js 同步事件的执行(如果同步事件占用的事件较长,则该 js 之后的 js 的加载执行都会延后,比如 sleepFor 循环)
  • js 按照顺序加载执行
  • var 定义的变量,在声明变量之前执行调用该变量的该函数,该变量值为 undefined
  • let/const 定义的变量,在声明变量之前执行调用该变量的该函数,报错

# DOMContentLoaded/load 的区别

  • The load event is fired when the whole page has loaded, including all dependent resources such as stylesheets and images. This is in contrast to DOMContentLoaded, which is fired as soon as the page DOM has been loaded, without waiting for resources to finish loading.
  • 在文档装载完成后会触发 load 事件。此时,在文档中的所有对象都在 DOM 中,所有图片,脚本,链接以及子框都完成了装载。
  • The DOMContentLoaded event fires when the initial HTML document has been completely loaded and parsed, without waiting for stylesheets, images, and subframes to finish loading.
  • 同时也会有 Gecko-指定 DOM 事件,如 DOMContentLoaded 和 DOMFrameContentLoaded (它们可以使用 EventTarget.addEventListener() 来处理 ) , 这些事件在页面 DOM 构建起来后就会触发,而不会等到其他的资源都装载完成。

# dom 文件加载的步骤

  • 1,解析 HTML 结构。
  • 2,DOM 树构建完成。//DOMContentLoaded
  • 3,加载外部脚本和样式表文件。
  • 4,解析并执行脚本代码。
  • 5,加载图片等外部文件。
  • 6,页面加载完毕。//load

# 重流和重绘

  • 作为开发者,应该尽量设法降低重绘的次数和成本。比如,尽量不要变动高层的 DOM 元素,而以底层 DOM 元素的变动代替;再比如,重绘 table 布局和 flex 布局,开销都会比较大。
  • 读取 DOM 或者写入 DOM,尽量写在一起,不要混杂。不要读取一个 DOM 节点,然后立刻写入,接着再读取一个 DOM 节点。
  • 缓存 DOM 信息。
  • 不要一项一项地改变样式,而是使用 CSS class 一次性改变样式。
  • 使用 documentFragment 操作 DOM
  • 动画使用 absolute 定位或 fixed 定位,这样可以减少对其他元素的影响。
  • 只在必要时才显示隐藏元素。
  • 使用 window.requestAnimationFrame(),因为它可以把代码推迟到下一次重流时执行,而不是立即要求页面重流。
  • 使用虚拟 DOM(virtual DOM)库。

位于 load 事件之后的 dom;load、DOMContentLoaded 在该 dom 渲染完成后才会执行 (同)

# async

有 async 的 script,会在渲染 dom 的时候同时下载

  • 没有 async 的 script 会优先加载并执行
  • async 按照 js 顺序进行下载,下载完成就执行;执行不分先后顺序——没有依赖的可以使用 async
  • js 的下载不会阻塞 dom 渲染
  • js 的执行会阻塞 dom 渲染
  • async 的 script 加载并执行 可以发生在 DOMContentLoaded 之后;一定在 load 之前

浏览器解析过程

  • 浏览器开始解析 HTML 网页。
  • 解析过程中,发现带有 async 属性的 script 标签。
  • 浏览器继续往下解析 HTML 网页,同时并行下载 script 标签中的外部脚本。
  • 脚本下载完成,浏览器暂停解析 HTML 网页,开始执行下载的脚本。
  • 脚本执行完毕,浏览器恢复解析 HTML 网页。

位于 load 事件之后的 dom;load、DOMContentLoaded 在该 dom 渲染完成后才会执行 (同)

# defer

有 defer 的 script,会在渲染 dom 的时候同时下载 (同 async)

  • 没有 defer 的 script 会优先加载并执行 (同 async)
  • js 按照顺序加载执行
  • js 的下载不会阻塞 dom 渲染 (同 async)
  • defer 的 script 加载并执行完之后触发 DOMContentLoaded/load
  • 对于内置而不是加载外部脚本的 script 标签,以及动态生成的 script 标签,defer 属性不起作用。另外,使用 defer 加载的外部脚本不应该使用 document.write 方法。

浏览器解析过程

  • 浏览器开始解析 HTML 网页。
  • 解析过程中,发现带有 defer 属性的 script 元素。
  • 浏览器继续往下解析 HTML 网页,同时并行下载 script 元素加载的外部脚本。 (同 async)
  • 浏览器完成解析 HTML 网页,此时再回过头执行已经下载完成的脚本。

位于 load 事件之后的 dom;load、DOMContentLoaded 在该 dom 渲染完成后才会执行 (同)

# 参考代码