JS知识点系列--延迟脚本

脚本的下载与执行

我们知道无论 JavaScript 代码是内嵌形式还是在外链文件形式中,页面的下载和渲染都必须下来等待脚本执行完成,即会出现页面阻塞(页面渲染和用户交互完全被阻塞),然后再继续解析和渲染页面。这是因为脚本可能会改变页面,这意味着它们对出现在脚本后面的页面的内容会造成影响,比如说JavaScript可能会向某个标签添加内容、可能会创建新的元素、可能会移除某个元素等都会对页面内容造成影响。

而在脚本的执行过程中,用户只能等待,执行过程耗时越久,浏览器等待响应用户输入的时间就越长。

脚本位置的影响

<script> 标签可以放在 HTML 文档的<head><body>中,并允许出现多次。在 <head> 中加载外链的 JavaScript会导致严重的性能问题。
上面说到当浏览器解析到 <script> 标签时,浏览器会停止解析其后的内容,而优先下载脚本文件,并执行其中的代码,这意味着,如果 styles.css 样式文件放在<script>的后面,则styles.css 样式文件和<body>标签都无法被加载,由于<body>标签无法被加载,那么页面自然就无法渲染了,因此这将导致在该 JavaScript 代码完全执行完之前,页面都是一片空白,用户体验非常不好,一般我看到长时间的空白页面,我都非常想直接关闭它

注:如果在<head>中有多个JavaScript文件,则它们是并行下载的,不需要等到一个下载执行完才开始下载下一个。但是,虽然如此,只要有脚本在下载在执行就会阻止页面渲染及其他文件的下载

还有一点要注意,CSS 本来是可以并行下载的,但是当 CSS 后面跟着 JS 的时候,该 CSS 就会出现阻塞后面资源下载的情况。因为JS 代码在执行前,浏览器必须保证在此 JS 执行之前的所有 css(无论外链还是内嵌)都已下载和解析完成。这是 CSS 阻塞后续 JS 执行的根本原因。

因此会推荐将所有<script>标签尽可能放到<body>标签的底部,以尽量减少对整个页面下载的影响,此时虽然一个脚本会阻塞另一个脚本,但是用户体验比上面的好很多,因为用户看到了大部分内容,而不是空白

通常如果JavaScript文件有多个的话,应该通过离线的打包工具或者一些实时的在线服务来实现多个文件合并成一个,这样只需要引用一个<script>标签,就可以减少性能消耗
这是因为浏览器在解析 HTML 页面的过程中每遇到一个<script>标签,都会因执行脚本而导致一定的延时,所以最小化延迟时间将会明显改善页面的总体性能。而对于外链 JavaScript 文件而言,考虑到 HTTP 请求会带来额外的性能开销,因此下载单个 100Kb 的文件将比下载 5 个 20Kb 的文件更快。所以减少页面中外链脚本的数量将会改善性能。

实现无阻塞的脚本

Web 应用的功能越丰富,所需要的 JavaScript 代码就越多,尽管下载单个较大的 JavaScript 文件只产生一次 HTTP 请求,却会锁死浏览器的一大段时间。为避免这种情况,需要通过一些特定的技术向页面中逐步加载 JavaScript 文件,这样做在某种程度上来说不会阻塞浏览器