难度级别:中级及以上 提问概率:65%
拿一个React项目举例,开发完成后,会通过webpack打出对应的CSS文件和Javascript文件,正常情况下应该是这样的
HTML代码:
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width initial-scale=1.0 minimum-scale=1.0 miximum=1.0 user-scalable=no"/>
<title>React项目</title>
<link rel="stylesheet" media="all" href="/app.css" />
<script src="/vendors.js"></script>
<script src="/app.js"></script>
</head>
<body>
<div id="app"></div>
</body>
</html>
早期的项目,Javascript文件是会放到head标签下面的,但不可避免很多代码都要在DOM元素加载完成之后才能执行。当浏览器开始加载HTML文档的时候,会有一个从上到下的过程,意思就是浏览器会先加载head标签内的所有内容,这样就需要先执行script标签代码,再从服务器下载Javascript文件,下载完成之后,要等到浏览器解析并执行完成,浏览器才能继续向下加载执行。这样就很容易因为Javascript文件过大过多,又或者是文件服务器响应时间过长,导致页面处于白屏状态。基于这一特性,开发者们逐渐发现把Javascript文件放到HTML文档底部加载,可以解决这一问题。
但不把Javascript文件放到HTML文档底部加载是否可以呢,这里就需要了解2个知识点。第一个是为script标签设置async属性,async表示当浏览器加载到script标签时进行下载,下载完成之后Javascript文件的解析和执行也会变为异步。通过以上代码其实很容易想象到,即便浏览器下载、解析、执行的速度再快,也不可能比浏览器加载div标签更快。但需要注意的是,为script标签设置async属性时,这些Javascript文件的加载执行等一系列过程都会变为异步,试想一下,如果多个Javascript文件存在变量依赖关系的话,文件的执行过程很容易变得不可控。
第二个是为script标签设置defer属性,与async不同的是,当设置了defer属性时,Javascript文件会等到HTML文档加载完成之后再执行。这种方式除了不会阻塞DOM元素的解析,还可以保证所有被设置的Javascript文件也可以按顺序执行,有效解决了多个Javascript文件存在变量依赖关系的问题。
尽管为script标签添加async或defer属性可以解决阻塞HTML文档渲染的问题,但还是建议把它放到HTML文档底部加载。因为浏览器加载HTML文档有一个从上到下的过程,尽管我们知道这个过程中在遇到script标签的时候,浏览器会去下载Javascript文件,而下载的过程也必然是异步的,但浏览器解析script标签,找到src的属性值并发送请求,这也是需要时间的。随着网络越来越发达,用户设备性能的不断提升,很多人会觉得只做一点主要的性能优化就可以了,甚至觉得性能优化已经没那么重要了,其实这种想法是不正确的。只有通过点滴的积累,在性能优化这件事上做到勿以善小而不为,我们的网站才能从量变达到质变,从而获得更好的用户体验。
刷题思考
这道题主要考察的是HTML文档加载顺序,以及为script标签设置async和defer这两个属性的区别,也就是defer的有序性以及async的无序性。
虽然现在把Javascript文件放到文档底部加载已经成为一种共识了,但还是要掌握如果不把Javascript文件放到文档底部,或者是不用使用async和defer的情况下,如何保证Javascript代码可以在HTML文档加载完成后才会执行。
类似考点
本小节看似知识点比较简单,但面试官很可能会考察比较细节的知识点,例如请你说一说document.ready和window.onload的区别是什么?例如你知道如果给script标签同时设置async和defer属性,会发生什么吗?例如请你举例说明,在项目开发中,如何保证DOM元素尽量的少?例如你知道页面意外偏移(CLS)是由什么造成的吗?该如何解决呢?