引言

每当浏览器在加载html的过程中遇到这样的标签时,浏览器会暂停继续构建html,而是优先执行当前的js脚本,等执行完毕后再继续加载后面的html。

怎样加快博客的加载速度呢?可以通过给<script></script>添加deferansyc属性来实现异步加载,调整js的加载时间和顺序,确保浏览器构建HTML的过程一切顺利。

defer解释说明

defer特性告诉浏览器不要等待脚本。相反,浏览器将继续处理HTML,构建DOM。脚本会在后台下载,然后等DOM构建完成后,脚本才会执行。

简单来说:

  1. 具有defer特性的脚本不会阻塞页面。
  2. 具有defer特性的脚本总是要等到DOM解析完毕,但在DOMContentLoaded事件之前执行。

举例说明:

1
2
3
4
5
6
7
8
9
<p>...content before scripts...</p>

<script>
document.addEventListener('DOMContentLoaded', () => alert("DOM ready after defer!"));
</script>

<script defer src="https://javascript.info/article/script-async-defer/long.js?speed=1"></script>

<p>...content after scripts...</p>
  1. 页面内容立即显示。
  2. DOMContentLoaded事件处理程序等待具有defer特性的脚本执行完成。它仅在脚本下载且执行结束后才会被触发。

    具有defer特性的脚本保持其相对顺序,就像常规脚本一样。

我们有两个具有defer特性的脚本:long.js在前,small.js在后。

1
2
<script defer src="https://javascript.info/article/script-async-defer/long.js"></script>
<script defer src="https://javascript.info/article/script-async-defer/small.js"></script>

浏览器扫描页面寻找脚本,然后并行下载它们,以提高性能。因此,在上面的示例中,两个脚本是并行下载的。small.js可能会先下载完成。
但是,defer特性除了告诉浏览器不要阻塞页面之外,还可以确保脚本执行的相对顺序。因此,即使 small.js先加载完成,它也需要等到long.js执行结束才会被执行。

当我们需要先加载JavaScript库,然后再加载依赖于它的脚本时,这可能会很有用。

defer特性仅适用于外部脚本
如果<script>脚本没有src,则会忽略defer特性。

async解释说明

async特性与defer有些类似。它也能够让脚本不阻塞页面。但是,在行为上二者有着重要的区别。

async特性意味着脚本是完全独立的:

  1. 浏览器不会因async脚本而阻塞(与defer类似)。
  2. 其他脚本不会等待async脚本加载完成,同样,async脚本也不会等待其他脚本。
  3. DOMContentLoaded和异步脚本不会彼此等待:
  • DOMContentLoaded可能会发生在异步脚本之前(如果异步脚本在页面完成后才加载完成)
  • DOMContentLoaded也可能发生在异步脚本之后(如果异步脚本很短,或者是从HTTP缓存中加载的)

换句话说,async脚本会在后台加载,并在加载就绪时运行。DOM和其他脚本不会等待它们,它们也不会等待其它的东西。async脚本就是一个会在加载完成时执行的完全独立的脚本。

举例说明:类似于我们在讲defer时所看到的例子:long.js和small.js两个脚本,只是现在defer变成了async

它们不会等待对方。先加载完成的(可能是small.js)—— 先执行:

1
2
3
4
5
6
7
8
9
10
<p>...content before scripts...</p>

<script>
document.addEventListener('DOMContentLoaded', () => alert("DOM ready!"));
</script>

<script async src="https://javascript.info/article/script-async-defer/long.js"></script>
<script async src="https://javascript.info/article/script-async-defer/small.js"></script>

<p>...content after scripts...</p>
  1. 页面内容立刻显示出来:加载写有async的脚本不会阻塞页面渲染。
  2. DOMContentLoaded可能在async之前或之后触发,不能保证谁先谁后。
  3. 较小的脚本small.js排在第二位,但可能会比long.js这个长脚本先加载完成,所以small.js会先执行。虽然,可能是long.js先加载完成,如果它被缓存了的话,那么它就会先执行。换句话说,异步脚本以加载优先的顺序执行。

当我们将独立的第三方脚本集成到页面时,此时采用异步加载方式是非常棒的:计数器,广告等,因为它们不依赖于我们的脚本,我们的脚本也不应该等待它们:

1
2
<!-- Google Analytics 脚本通常是这样嵌入页面的 -->
<script async src="https://google-analytics.com/analytics.js"></script>

总结

deferasync<script>标签的两个属性,用来控制js脚本的加载。

总的HTML加载时间,下载脚本的时间,执行脚本的时间是固定的。不同之处在于HTML阻塞的时间以及执行脚本的次序。

  1. 不加任何asyncdefer的情况,页面总加载时间最长:HTML加载时间+下载脚本时间+执行脚本时间
  2. 加了asyncdefer的时间,在加载HTML时间足够长的情况下,所有静态资源总的加载时间为:HTML加载时间+执行脚本时间
  3. 总的来说,async加在那些非必要的,起装饰或者优化效果的js上,defer加在那些确保页面完整性的必要js上。

适用说明

  1. defer特性除了告诉浏览器不要阻塞页面之外,还可以确保脚本执行的相对顺序
  2. 其他脚本不会等待async脚本加载完成,同样async脚本也不会等待其他脚本。

参考文章:Hexo异步加载方案