一、页面加入脚本的缺点
HTML中无论JavaScript是内嵌还是外链在文件中,都有以下特性:
- 页面的下载和渲染都必须停下来等待脚本执行完成(浏览器必须先花时间下载外链文件中的代码,然后解析并执行它);
- 执行时间耗时越久,浏览器等待响应用户输入的时间就越长(页面渲染和用户交互完全被阻塞了);
- 脚本可能会改变页面或JavaScript的命名空间,对后面页面内容造成影响。
二、脚本位置的影响
脚本位置可以放在<head>
或<body>
中, 并允许出现多次。
- 放在
<head>
中
- 浏览器先下载脚本并执行,因此CSS样式文件和
<body>
中的内容均无法被加载,造成页面空白现象。 - 第一个 JavaScript 文件下载,阻塞了页面其他文件的下载,且每个文件必须等到前一个文件下载并执行完成才会开始下载。在这些文件逐个下载过程中,用户看到的是一片空白的页面。
- 对于有DOM操作的脚本,由于
<body>
中的内容还没有加载,因此执行脚本时会报错
从 IE 8、Firefox 3.5、Safari 4 和 Chrome 2 开始都允许并行下载 JavaScript 文件。<script>
标签在下载外部资源时不会阻塞其他<script>
标签。遗憾的是,JavaScript 下载过程仍然会阻塞其他资源的下载,比如样式文件和图片。尽管脚本的下载过程不会互相影响,但页面仍然必须等待所有 JavaScript 代码下载并执行完成才能继续。因此,尽管最新的浏览器通过允许并行下载提高了性能,但问题尚未完全解决,脚本阻塞仍然是一个问题。
- 放在
<body>
中(推荐使用)
尽管脚本下载会阻塞另一个脚本,但是页面的大部分内容都已经下载完成并显示给了用户,因此页面下载不会显得太慢。
三、减少JavaScript对性能的影响:
- 首要规则:将脚本放在底部。
- 合并脚本,减少
<script>
数量; - 无阻塞下载JavaScript脚本:
(1)在 window 对象的 onload事件触发后再下载脚本,即页面加载完成后才加载脚本;
(2)定义defer属性延迟加载脚本,带有 defer 属性的<script>
标签可以放置在文档的任何位置。对应的 JavaScript 文件将在页面解析到<script>
标签时开始下载,但不会执行,直到 DOM 加载完成,即onload事件触发前才会被执行。
(3)HTML5新属性async,它的作用和 defer 一样,能够异步地加载和执行脚本,不因为加载脚本而阻塞页面的加载。但是有一点需要注意,在有 async 的情况下,JavaScript 脚本一旦下载好了就会执行,所以很有可能不是按照原本的顺序来执行的。如果 JavaScript 脚本前后有依赖性,使用 async 就很有可能出现错误。
(4)使用 XMLHttpRequest(XHR)对象
先创建一个 XHR 对象,然后下载 JavaScript 文件,接着用一个动态<script>
元素将 JavaScript 代码注入页面:
var xhr = new XMLHttpRequest();
xhr.open("get", "script1.js", true);
xhr.onreadystatechange = function(){
if (xhr.readyState == 4){
if (xhr.status >= 200 && xhr.status < 300 || xhr.status == 304){
var script = document.createElement ("script");
script.type = "text/javascript";
script.text = xhr.responseText;
document.body.appendChild(script);
}
}
};
xhr.send(null);
- HTTP 状态码:2XX 表示有效的回应,304 表示一个缓存响应
- 可以下载不立即执行的 JavaScript 代码;由于代码返回在
<script>
标签之外,它下载后不会自动执行,直到一切都准备好了。 - 同样的代码在所有现代浏览器中都不会引发异常
- 此方法最主要的限制是:JavaScript 文件必须与页面放置在同一个域内,不能从 CDN 下载(CDN 指"内容投递网络(Content Delivery Network)",所以大型网页通常不采用 XHR 脚本注入技术。