现代浏览器中的3f1c4e4b6b16bbbd69b2ee476dc4f83a标签分成了两种新类型:经典型和非阻塞型。接下来讨论如何运用这两种标签来尽快加载页面。
1、阻塞型脚本何去何从?
标准版本的3f1c4e4b6b16bbbd69b2ee476dc4f83a标签常常被称作阻塞型标签。这个词必须放在上下文中进行理解:现代浏览器看到阻塞型3f1c4e4b6b16bbbd69b2ee476dc4f83a标签时,会跳过阻塞点继续读取文档及下载其他资源(脚本和样式表)。但直到脚本下载完毕并运行之后,浏览器才会评估阻塞点之后的那些资源。因此,如果网页文档的93f0f5c25f18dab9d176bd4f6de5d30e标签里有5 个阻塞型3f1c4e4b6b16bbbd69b2ee476dc4f83a标签,则在所有这5 个脚本均下载完毕并运行之前,用户除了页面标题之外看不到任何东西。不仅如此,即便这些脚本运行了,它们也只能看到阻塞点之前的那部分文档。如果想看到6c04bd5ca3fcae76e30b72ad730ca86d标签中正等待加载的那些好东西,就必须给像document.onreadystatechange 这样的事件绑定一个事件处理器。
基于上述原因,现在越来越流行把脚本放在页面6c04bd5ca3fcae76e30b72ad730ca86d标签的尾部。这样,一方面用户可以更快地看到页面,另一方面脚本也可以主动亲密接触DOM 而无需等待事件来触发自己。对大多数脚本而言,这次“搬家”是个巨大的进步。
但并非所有脚本都一样。在向下搬动脚本之前,请先问自己2 个问题。
该脚本是否有可能被6c04bd5ca3fcae76e30b72ad730ca86d标签里的内联JavaScript 直接调用?答案可能一目了然,但仍值得核查一遍。
该脚本是否会影响已渲染页面的外观?Typekit 宿主字体就是一个例子。如果把Typekit 脚本放在文档末尾,那么页面文本就会渲染两次,即读取文档时即刻渲染,脚本运行时再次渲染。
上述问题只要有一个答案是肯定的,那么该脚本就应该放在93f0f5c25f18dab9d176bd4f6de5d30e标签中,否则就可以放在6c04bd5ca3fcae76e30b72ad730ca86d标签中,文档形如:
<html> <head> <!--metadata and stylesheets go here --> <script src="headScripts.js"></scripts> </head> <body> <!-- content goes here --> <script src="bodyScripts.js"></script> </body> </html>
这确实大大缩短了加载时间,但要注意一点,这可能让用户有机会在加载bodyScripts.js 之前与页面交互。
2、 脚本的提前加载与延迟运行
上面建议将大多数脚本放在6c04bd5ca3fcae76e30b72ad730ca86d中,因为这样既能让用户更快地看到网页,又能避免操控DOM之前绑定“就绪”事件的开销。但这种方式也有一个缺点,即浏览器在加载完整个文档之前无法加载这些脚本,这对那些通过慢速连接传送的大型文档来说会是一大瓶颈。
理想情况下,脚本的加载应该与文档的加载同时进行,并且不影响DOM 的渲染。这样,一旦文档就绪就可以运行脚本,因为已经按照3f1c4e4b6b16bbbd69b2ee476dc4f83a标签的次序加载了相应脚本。
如果大家已经读到这里了,那么一定会迫不及待地想写一个自定义Ajax 脚本加载器以满足这样的需求!不过,大多数浏览器都支持一个更为简单的解决方案。
<script defer src = "deferredScript.js">
添加defer(延迟)属性相当于对浏览器说:“请马上开始加载这个脚本吧,但是,请等到文档就绪且所有此前具有defer 属性的脚本都结束运行之后再运行它。”在文档93f0f5c25f18dab9d176bd4f6de5d30e标签里放入延迟脚本,既能带来脚本置于6c04bd5ca3fcae76e30b72ad730ca86d标签时的全部好处,又能让大文档的加载速度大幅提升!
不足之处就是,并非所有浏览器都支持defer属性。这意味着,如果想确保自己的延迟脚本能在文档加载后运行,就必须将所有延迟脚本的代码都封装在诸如jQuery 之$(document).ready 之类的结构中。
上一节的页面例子改进如下:
<html> <head> <!-- metadata and stylesheets go here --> <script src="headScripts.js"></scripts> <script defer src="deferredScripts.js"></script> </head> <body> <!-- content goes here --> </body> </html>
请记住deferredScripts 的封装很重要,这样即使浏览器不支持defer,deferredScripts 也会在文档就绪事件之后才运行。如果页面主体内容远远超过几千字节,那么付出这点代价是完全值得的。
3、 脚本的并行加载
如果你是斤斤计较到毫秒级页面加载时间的完美主义者,那么defer也许就像是淡而无味的薄盐酱油。你可不想一直等到此前所有的defer 脚本都运行结束,当然也肯定不想等到文档就绪之后才运行这些脚本,你就是想尽快加载并且尽快运行这些脚本。这也正是现代浏览器提供了async(异步)属性的原因。
<script async src = "speedyGonzales.js"> <script async src = "roadRunner.js">
如果说defer 让我们想到一种静静等待文档加载的有序排队场景,那么async 就会让我们想到混乱的无政府状态。前面给出的那两个脚本会以任意次序运行,而且只要JavaScript 引擎可用就会立即运行,而不论文档就绪与否。
对大多数脚本来说,async 是一块难以下咽的鸡肋。async 不像defer那样得到广泛的支持。同时,由于异步脚本会在任意时刻运行,它实在太容易引起海森堡蚁虫之灾了(脚本刚好结束加载时就会蚁虫四起)。
当我们加载一些第三方脚本,而且也不在乎它们谁先运行谁后运行。因此,对这些第三方脚本使用async 属性,相当于一分钱没花就提升了它们的运行速度。
上一个页面示例再添加两个独立的第三方小部件,得到的结果如下:
<html> <head> <!-- metadata and stylesheets go here --> <script src="headScripts.js"></scripts> <script src="deferredScripts.js" defer></script> </head> <body> <!-- content goes here --> <script async defer src="feedbackWidget.js"></script> <script async defer src="chatWidget.js"></script> </body> </html>
这个页面结构清晰展示了脚本的优先次序。对于绝大多数浏览器,DOM的渲染只会延迟至headScripts.js 结束运行时。进行DOM渲染的同时会在后台加载deferredScripts.js。接着,在DOM 渲染结束时将运行deferredScripts.js 和那两个小部件脚本。这两个小部件脚本在那些支持async 的浏览器中会做无序运行。如果不确定这是否妥当,请勿使用async!
以上是经典型和非阻塞型<script>标签用法实例详解的详细内容。更多信息请关注PHP中文网其他相关文章!