详细讲解JS脚本的加载流程

237 阅读2分钟

脚本加载流程

With <script>, parsing is interrupted by fetching and execution. With <script defer>, fetching is parallel to parsing and execution takes place after all parsing has finished. And with <script async>, fetching is parallel to parsing but once it finishes parsing is interrupted to execute the script. The story for <script type="module"> is similar to <script defer>, but the dependencies will be fetched as well, and the story for <script type="module" async> is similar to <script async> with the extra dependency fetching.

图片来自文档

同步加载

浏览器正常是同步加载 JavaScript 脚本,当浏览器加载 HTML 并遇到<script>标签时,它会立即执行脚本,阻塞HTML解析。外部脚本<script src="...">也是,浏览器会等待脚本下载,执行下载的脚本后才继续解析。

问题

  1. 当页面顶部有一个庞大的脚本,则会阻塞页面解析,在下载并运行之前,用户无法看到页面内容。

解决

  1. 把脚本放到最底部,但对于长HTML文档,这可能会有明显的延迟。
  2. 采用异步加载脚本

异步加载

异步加载有两种方式:deferasync

  1. async:下载完就执行

    • async脚本异步加载,加载完成后就立刻执行。
    • 多个async脚本不会按顺序执行。
    • 其他脚本也不会等待async脚本,async脚本也不会等待其他脚本。
    • DOMContentLoaded:和async脚本不会互相等待

      • DOMContentLoaded可能在async脚本执行之前触发(如果async脚本在页面解析完成后完成加载)
      • 或在async脚本执行之后触发(如果async脚本很快加载完成或在 HTTP 缓存中)
  2. defer:渲染完再执行

    • defer脚本异步加载,加载完成后,在 DOM 完全构建完成后,在DOMContentLoaded前执行。
    • 多个defer脚本会按顺序执行
    • 导致DOMContentLoad延后,即在执行完defer资源的代码之后才会触发DOMContentLoad事件

浏览器加载 ES6 模块 type="module"

script标签带上type="module"属性表示这是 ES6 模块。 浏览器加载模块脚本都是异步加载,不会造成堵塞浏览器。 即等到整个页面渲染完,再执行模块脚本 ,也就是说 type="module" 等价于 defer 。如果网页有多个模块脚本,它们会按照在页面出现的顺序依次执行

补充

  • DOMContentLoaded: 当初始的 HTML 文档被完全加载和解析完成之后,DOMContentLoaded 事件被触发,而无需等待样式表、图像和子框架的完全加载。
  • Load:load 事件在整个页面及所有依赖资源如样式表和图片都已完成加载时触发。它与 DOMContentLoaded 不同,后者只要页面 DOM 加载完成就触发,无需等待依赖资源的加载。