不要再把 script 标签放 body 末尾了

作为一个前端工程师,你是不是经常听到,或者自己也认同一种说法,『为了加快页面的加载,script 标签得放在 body 标签的结尾处』。不知道大家是否真的有去搞明白这个说法的由来是什么,script 放在 body 结尾处就一定会加快页面的加载速度呢?

作为一个 10 年的老司机开发工程师,这句话的出处如果我没记错,应该是来自于早些年牛逼得不行的雅虎公司分享出来的前端开发『最佳实践』。不仅是雅虎,google 开发团队也应该分享过类似的结论。此结论由以下两点理由得出:

  1. script 标签会阻止同步下载。在 HTTP 1.1 协议里,浏览器对同一个域名不能有超过两个下载线程。所以为了早一步开始下载图片、样式等静态文件,通常都把这些文件放在不同的域名下,但唯独下载 JS 文件时无论是否外部资源,都会阻止下载线程,只能尽量让引入外部 JS 文件的 script 标签往后排了。
  2. JS 文件在下载时,浏览器的渲染会被阻止。因为 JS 文件可能会改变浏览器的渲染,浏览器还不如先等着 JS 加载完。网上有些说法说 JS 会阻止浏览器继续解析HTML,其实是不准确的。浏览器会一直解析 HTML 到最后,才不管 JS 是否加载完。

看起来『最佳实践』的出现合情合理,但它也不是完全就没有问题。假如有一个需求,用户点击页面里的任意链接时应该先弹出确认框,用户同意了之后,才让用户跳转。

但是,当网速不理想的时候,可能 jquery.js 还没下完,甚至还没开始下载,用户点了链接,没出现确认框就直接跳走了。

难道没有办法,可以让文档下载和渲染的同时,让 script 也下载,以尽量提早 script 的下载时间?

HTML5 的时代,如果你还不知道 script 的 async 和 defer 属性,那你就得好好补补课了,这两个属性就可以解决我们上面说的问题。

async

此属性告诉浏览器,此脚本加载的时候,不会阻止浏览器停止渲染页面,只要此脚本下载完毕,就开始执行脚本。

不过因为异步下载共有的特性,脚本下载完毕的先后顺序,可不一定是写的时候的顺序,如果两个脚本前后有依赖,使用 async 可能不是一个好主意。异步加载的方式,更适用于类似 require.js 那样的动态加载。

defer

此属性与 async 类似,只是运行的时机不是脚本下载完毕,而是等着整个文档加载完毕之后执行,类似于执行在 jQuery 的 ondomready 事件(其实也就是 DOMContentLoaded 事件)。

另外 defer 还有一个特点,它是按写得顺序来执行的。

用这张图来说明可能更直接一些:

script-async-defer

有了这两个属性,我们就可以放心把 script 标签放回 head 标签,加速 script 的下载了。

caniuse 可以看出,这两个属性的支持度还是很高的了,并且因为新属性无痛升级,你也不用担心 IE10 以下使用它们会有什么大的副作用(无非也就让页面载入慢点而已),所以已经没有什么道理不用这两个属性了。

wx pay

CC BY-NC-ND 4.0 不要再把 script 标签放 body 末尾了 by Chrisyue's Blog is licensed under a Creative Commons Attribution-NonCommercial-NoDerivatives 4.0 International License.

不要再把 script 标签放 body 末尾了》上有2条评论

发表评论

电子邮件地址不会被公开。

eighty − = seventy eight