在HTML中,我们可以使用<script>
标签和它的src
属性来引入一个外部脚本,例如:
<script src="scripts/main.js"></script>
其中src
属性指向的是需要加载的JavaScript文件路径。
这样做的好处是不用将JS脚本代码直接写入到HTML文件中,可以区别管理HTML代码和JS代码,可读性更高,也更利于维护。
更重要的是:如果一个JavaScript文件被多个页面共享,那它只会被第一个使用它的页面下载一次,后续页面可以从浏览器缓存中获取该文件,这样也能够提升加载效率。
这种方式虽好,但仍有些不足。
比如:页面上的HTML和JS代码都是按照从上至下的顺序执行的,如果遇到某个JS文件下载和执行的时间过长,就会阻塞剩余的页面加载,导致页面加载时间过长。
再比如:如果想用JS代码操作页面上的元素,但JS代码的加载早于要操作的HTML元素,代码将会出错。
那么,对于一些不会影响页面渲染的功能性JS代码,我们是不是可以让它晚点加载,以提高执行效率呢?
对执行顺序有要求的JS代码,我们是不是可以让它延后加载呢?
答案是肯定的!
旧办法是把需要延后加载的脚本元素放在文档底端的</body>
标签之前,这样脚本就可以在HTML解析完毕后加载了。
还有个比较新的方法是在<script>
标签内加入defer
,例如:
<script src="scripts/main.js" defer></script>
这种写法和把脚本放在文档底端的</body>
标签之前具有同样的效果,可以让代码在文档完成解析后,触发DOMContentLoaded
事件前执行。
注意:如果存在多个包含defer
的JS脚本,它们会在触发DOMContentLoaded
事件前,按照出现的先后顺序依次执行。
这种方法适合页面较小,JS脚本较少,或者需要预加载其他页面脚本的情况。
但是对于有大量JS代码的大型网站,让JS代码在文档完成解析后才执行会带来显著的性能损耗。
比如页面看起来已经渲染完毕,但是点击页面上的功能按钮并没有反应,就有可能是实现按钮功能的JS代码执行的太晚,而用户在代码还未执行完毕时就产生了操作。
对于这种需要在减少阻塞的基础上尽快执行的代码,我们可以选择另一个折中的方法:
<script src="scripts/main.js" async></script>
通过在<script>
标签内加入async
,可以使脚本在下载阶段不阻塞其余代码的解析,并在下载完成后尽快执行。
也正是因为如此,如果存在多个包含async
的JS脚本,它们会按照下载完成的顺序执行(哪个先下载完就先执行哪个),这样脚本的运行次序就无法控制。
一般来说:当页面的脚本之间彼此独立,且不依赖于本页面的其它任何脚本时,async
是最理想的选择。
最后做个总结:
defer
和async
都能消除脚本下载期间造成的阻塞,只是执行的时机不同。async
会在脚本下载完成后尽快执行,如果脚本无依赖,不用考虑执行顺序,那么应该使用async
。defer
会在脚本下载完成且页面渲染完毕后执行,如果脚本依赖于其它脚本,需要考虑执行顺序,则应使用defer
,将关联的脚本按顺序置于HTML中。