现代浏览器总是并行加载资源。脚本文件互相不会阻塞,但是会阻塞其他资源(例如图片、字体等)的下载。
后来为了用户体验,有了 async 和 defer ,脚本标记为异步,不会阻塞其他线程解析和执行。
当 HTML 解析器被 JavaScript 脚本阻塞时,解析器虽然会停止构建 DOM,但仍会识别该脚本后面的资源,并进行预加载。
存在阻塞的 CSS 资源时,浏览器会延迟 JavaScript 的执行和 DOM 构建
<script> 标记时,DOM 构建将暂停,直至脚本完成执行。<script> 标签为止很重要。实际使用时,遵循下面两个原则:
defer 的 JavaScript 脚本文件不会停止 HTML 文档解析,而是等到解析结束才执行async 只能引用外部脚本,下载完马上执行,但是不能保证加载顺序。<script src="foo.js"></script>
浏览器会做如下处理
foo.jsfoo.js 中的脚本<script src="foo.js" defer></script><script src="bar.js" defer></script>
defer 属性规定是否对异步加载的脚本延迟执行,直到页面加载为止。
foo.js、bar.jsfoo.js 和 bar.js 仍继续解析 documentDOMContentLoaded 事件前依次执行 foo.js 和 bar.js<script src="foo.js" async></script><script src="bar.js" async></script>
async 属性规定异步加载脚本并且立即执行,则会异步执行。
foo.js 和 bar.jsDOMContentLoaded 事件前或者后async:脚本相对于页面的其余部分异步地执行async 但使用 defer:脚本将在页面完成解析时执行async 也不使用 defer:在浏览器继续解析页面之前,立即读取并执行脚本如果 <script> 无 src 属性,则 defer 和 async 会被忽略
动态添加的 <script> 标签隐含 async 属性
图解脚本的异步加载

defer 会在 DOMContentLoaded 前依次执行async 则是下载完立即执行,不一定是在 DOMContentLoaded 前async 因为乱序,所以很适合像 Google Analytics 这样的无依赖脚本<link>:加载外部 CSS 样式文件 。异步加载,继续解析 HTML。<script src='url'>:加载 JavaScript 脚本文件,同步加载并阻塞解析 HTML,加载完马上执行。<script src='url' async>:加载 JavaScript 脚本文件。异步加载,继续解析 HTML,加载完马上执行。<script src='url' defer>:加载 JavaScript 脚本文件。异步加载,继续解析 HTML,加载完延迟执行。<img src='url' />:加载图片,异步加载,继续解析 HTML;但是需要等待 CSS 解析完才解码,所以 CSS 阻塞图片呈现。DOMContentLoaded 标识着程序从同步脚本执行转化为事件驱动阶段。
<link> 标签放部头而 <script> 放 <body> 尾部,是因为 JavaScript 阻塞阻塞 DOM 树的构建<script> 且没有 defer 或 async 属性的标签时,会触发页面渲染,因而如果前面 CSS 资源尚未加载完毕时,浏览器会等待它加载完毕在执行脚本。