HTML 中的 JavaScript

87 阅读4分钟

在解释行内 JavaScript 和解释外部 JavaScript 时,页面会阻塞(阻塞时间也包含下载文件的时间)。

1. <script> 包含外部域 JS 文件时注意点

<script> 元素可以包含来自外部域的 JavaScript 文件。

跟 <img> 元素很像,<script> 元素的 src 属性可以是一个完整的 URL,而且这个 URL 指向的资源可以跟包含它的 HTML 页面不在同一个域中,比如这个例子:

<script src= "http://www.somewhere.com/agile.js></script>

浏览器在解析这个资源时,会向 src 属性指定的路径发送一个 GET 请求,以取得相应资源,假定是一个 JavaScript 文件。这个初始的请求不受浏览器同源策略限制,但返回并被执行的 JavaScript 则受限制。当然,这个请求仍然受父页面 HTTP/HTTPS 协议的限制。

来自外部域的代码会被当成加载它的页面的一部分来加载和解释。这个能力可以让我们通过不同的域分发 JavaScript。

引用放在别人服务器上的 JavaScript 文件时要格外小心,因为恶意的程序员随时可能替换这个文件。

在包含外部域的 JavaScript 文件时,要确保该域是自己所有的,或者该域是一个可信的来源。

<script> 标签的 integrity 属性是防范这种问题的一个武器,但这个属性也不是所有浏览器都支持。

2. defer 推迟执行脚本

HTML4.01 为 <script> 元素定义了一个叫 defer 的属性。

HTML5 中明确规定,defer 属性只对外部脚本文件才有效。

defer 属性表示脚本在执行的时候不会改变页面的结构。也就是说,脚本会被延迟到整个页面都解析完毕后再运行。

因此,在 <script> 元素上设置 defer 属性,相当于告诉浏览器立即下载,但延迟执行。

<script defer src="example.js"></script>

设置了 defer 属性后,该脚本会在浏览器解析到结束的 </html> 标签后才会执行。

HTML5 规范要求脚本应该按照它们出现的顺序执行,而且会在 DOMContentLoaded 事件之前执行。

不过在实际当中,推迟执行的脚本不一定总会按顺序执行或者在 DOMContentLoaded 事件之前执行,因此最好只包含一个这样的脚本。

对 defer 属性的支持是从 IE4、Firefox3.5、Safari5 和 Chrome7 开始的。其他所有浏览器则会忽略这个属性,按照通常的做法来处理脚本。考虑到这一点,还是把推迟执行的脚本放在页面底部比较好。

注意:对于 XHTML 文档,指定 defer 属性时应该写成 defer="defer"。

DOMContentLoaded 事件:window 的 load 事件会在页面完全加载后触发,因为要等待很多外部资源加载完成,所以会花费较长时间。而 DOMContentLoaded 事件会在 DOM 树构建完成后立即触发,而不用等待图片、JavaScript 文件、CSS 文件或其他资源加载完成。

3. async 异步执行脚本

HTML5 为 <script> 元素定义了 async 属性。

async 只对外部脚本文件有效。

与 defer 不同的是,标记为 async 的脚本并不保证能按照它们出现的次序执行。

给脚本添加 async 属性的目的是告诉浏览器,不必等脚本下载和执行完成后再加载页面,同样也不必等到该异步脚本下载和执行后再加载其他脚本。正因为如此,异步脚本不应该在加载期间修改 DOM。

异步脚本保证会在页面的 load 事件前执行,但可能会在 DOMContentLoaded 之前或之后。

Firefox3.6、Safari5 和 Chrome7 支持异步脚本。

使用 async 也会告诉页面,你不会使用 document.write ,不过好的 Web 开发实践根本不推荐使用这个方法。

注意:对于 XHTML 文档,指定 async 属性时应该写成 async="async"。

4. 动态加载脚本

通过向 DOM 中动态添加 script 元素同样可以加载指定的脚本。只要创建一个 script 元素并将其添加到 DOM 即可。

默认情况下,以这种方式创建的 <script> 元素是以异步方式加载的,相当于添加了 async 属性。

但是不是所有浏览器都支持 async 属性,因此如果要统一动态脚本的加载行为,可以明确将其设置为同步加载。

let script = document.createElement('script')
script.src = 'gibberish.js'
script.async = false
document.head.appendChild(script)

以这种方式获取的资源对浏览器预加载器是不可见的,这会严重影响它们在资源获取队列中的优先级。根据应用程序的工作方式以及怎么使用,这种方式可能会严重影响性能。

要想让预加载器知道这些动态请求文件的存在,可以在文档头部显式声明它们:

<link rel='preload' href='gibberish.js'>

5. <noscript> 元素

通过使用 <noscript> 元素,可以指定在浏览器不支持脚本时显示的内容。

如果浏览器支持并启用脚本,则 <noscript> 元素中的任何内容都不会被渲染。

<noscript> 元素可以包含任何可以出现在 <body> 中的 HTML 元素,<script> 除外。

在下列两种情况下,浏览器将显示包含在 <noscript> 中的内容:

  • 浏览器不支持脚本;
  • 浏览器对脚本的支持被关闭。