经典型和非阻塞型<script>标签用法实例详解

伊谢尔伦
伊谢尔伦 原创
2017-07-21 15:41:52 986浏览

现代浏览器中的<script>标签分成了两种新类型:经典型和非阻塞型。接下来讨论如何运用这两种标签来尽快加载页面。

1、阻塞型脚本何去何从?

标准版本的<script>标签常常被称作阻塞型标签。这个词必须放在上下文中进行理解:现代浏览器看到阻塞型<script>标签时,会跳过阻塞点继续读取文档及下载其他资源(脚本和样式表)。但直到脚本下载完毕并运行之后,浏览器才会评估阻塞点之后的那些资源。因此,如果网页文档的<head>标签里有5 个阻塞型<script>标签,则在所有这5 个脚本均下载完毕并运行之前,用户除了页面标题之外看不到任何东西。不仅如此,即便这些脚本运行了,它们也只能看到阻塞点之前的那部分文档。如果想看到<body>标签中正等待加载的那些好东西,就必须给像document.onreadystatechange 这样的事件绑定一个事件处理器。

基于上述原因,现在越来越流行把脚本放在页面<body>标签的尾部。这样,一方面用户可以更快地看到页面,另一方面脚本也可以主动亲密接触DOM 而无需等待事件来触发自己。对大多数脚本而言,这次“搬家”是个巨大的进步。

但并非所有脚本都一样。在向下搬动脚本之前,请先问自己2 个问题。

该脚本是否有可能被<body>标签里的内联JavaScript 直接调用?答案可能一目了然,但仍值得核查一遍。

该脚本是否会影响已渲染页面的外观?Typekit 宿主字体就是一个例子。如果把Typekit 脚本放在文档末尾,那么页面文本就会渲染两次,即读取文档时即刻渲染,脚本运行时再次渲染。

上述问题只要有一个答案是肯定的,那么该脚本就应该放在<head>标签中,否则就可以放在<body>标签中,文档形如:

<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、 脚本的提前加载与延迟运行

上面建议将大多数脚本放在<body>中,因为这样既能让用户更快地看到网页,又能避免操控DOM之前绑定“就绪”事件的开销。但这种方式也有一个缺点,即浏览器在加载完整个文档之前无法加载这些脚本,这对那些通过慢速连接传送的大型文档来说会是一大瓶颈。

理想情况下,脚本的加载应该与文档的加载同时进行,并且不影响DOM 的渲染。这样,一旦文档就绪就可以运行脚本,因为已经按照<script>标签的次序加载了相应脚本。

如果大家已经读到这里了,那么一定会迫不及待地想写一个自定义Ajax 脚本加载器以满足这样的需求!不过,大多数浏览器都支持一个更为简单的解决方案。

<script defer src = "deferredScript.js">

添加defer(延迟)属性相当于对浏览器说:“请马上开始加载这个脚本吧,但是,请等到文档就绪且所有此前具有defer 属性的脚本都结束运行之后再运行它。”在文档<head>标签里放入延迟脚本,既能带来脚本置于<body>标签时的全部好处,又能让大文档的加载速度大幅提升!

不足之处就是,并非所有浏览器都支持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中文网其它相关文章!

声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn核实处理。