这篇文章由 Olivier Rochard 撰写。Olivier 在法国的 Orange Labs 进行研究。
在 HTML 中,script
元素允许作者在他们的文档中包含动态脚本。defer
属性是一个布尔属性,它指示应该如何执行脚本。如果 defer
属性存在,则脚本在页面解析完成后执行。该元素被添加到文档解析完成后将执行的脚本列表的末尾。想想一个 FIFO 处理队列:添加到队列的第一个脚本元素将是第一个执行的脚本,然后处理按顺序进行,顺序相同。
使用 defer
属性有一个很好的理由:性能。如果您在 HTML 页面中包含一个 script
元素,则脚本必须在页面解析时立即进行评估。这意味着必须创建对象,必须刷新样式等。这会导致页面加载速度变慢。defer
属性意味着脚本在加载文档时对文档没有副作用,并且可以在页面加载结束时安全地评估。
defer
属性最初是在 Internet Explorer 4 中引入的,并在 HTML 4 规范 中添加。
一个简单的测试。
以下是一些简单的第一个测试,以了解属性的工作原理。以下行位于页面的 head 元素中
‹script›
var test1 = "Test 1 : fail";
‹/script›
‹script defer›
console.log(test1);
‹/script›
‹script›
test1 = "Test 1 : pass";
‹/script›
如果脚本元素的 defer
属性正确实现,浏览器将
- 渲染页面。
- 在所有其他脚本元素之后执行第二个脚本元素。
- 在 Firebug 控制台中显示“Test 1 : pass”。
如果控制台显示“Test 1 : fail”,则表示脚本按源代码中的顺序执行。
请注意,XHTML 文档的正确语法是
更高级的测试
第二个测试是查看功能如何在包含多个脚本元素的网页中工作的方式
- 在
head
和body
元素中内联 - 通过
src
属性在head
和body
元素中外部 - 使用动态 DOM 插入
以下是一个测试 defer 如何影响脚本加载和解析顺序的网页的部分源代码
‹!doctype html›
‹html›
‹head›
‹title› Test 2 ‹/title›
‹script› var test2 = "Test 2 :nn"; ‹/script›
‹script› document.addEventListener("DOMContentLoaded",
function(){
test2 += "tDOMContentLoadedn";
}, false);
‹/script›
‹script defer› test2 += "tInline HEAD deferredn"; ‹/script›
‹script› test2 += "tInline HEADn"; ‹/script›
‹script src="script1.js" defer›
// External HEAD deferred (script1.js)
‹/script›
‹script src="script2.js"›
// External HEAD (script2.js)
‹/script›
‹script›
// Dynamic DOM insertion of a script (script3.js)
head = document.getElementsByTagName('head')[0];
script3 = document.createElement('script');
script3.setAttribute('src', 'script3.js');
head.appendChild(script3);
// Dynamic DOM insertion of a deferred script (script4.js)
script4 = document.createElement('script');
script4.setAttribute('defer', 'defer');
script4.setAttribute('src', 'script4.js');
head.appendChild(script4);
‹/script›
‹script defer›
// Deferred dynamic DOM insertion of a script (script5.js)
head = document.getElementsByTagName('head')[0];
script5 = document.createElement('script');
script5.setAttribute('src', 'script5.js');
head.appendChild(script5);
// Deferred dynamic DOM insertion of a deferred script
// (script6.js)
script6 = document.createElement('script');
script6.setAttribute('defer', 'defer');
script6.setAttribute('src', 'script6.js');
head.appendChild(script6);
‹/script›
‹/head›
‹body onload="test2 += 'tBody onLoadn';"›
‹script defer› test2 += "tInline BODY deferredn"; ‹/script›
‹script› test2 += "tInline BODYn"; ‹/script›
... other body content ...
Launch test 2
... other body content ...
‹script src="script7.js" defer›
// External BODY deferred (script7.js)
‹/script›
‹script src="script8.js"›
// External BODY (script8.js)
‹/script›
‹/body›
‹/html›
当您单击文档中的“启动测试 2”链接时,将出现一个弹出窗口,其中包含一个列表。此列表显示了会话期间加载的脚本元素的顺序。
该测试还显示了 DOMContentLoaded
和 body.onload
事件何时触发。
如果浏览器中正确实现了 defer
属性,则所有延迟行都应位于列表的底部。
每个浏览器的第二个测试结果如下(延迟脚本以绿色显示)
- 该deferFirefox 3.5 浏览器的属性行为正确:
- 内联 HEAD
- 外部 HEAD(script2.js)
- 动态 DOM 插入脚本(script3.js)
- 内联 BODY
- 外部 BODY(script8.js)
- 内联 HEAD 延迟
- 外部 HEAD 延迟(script1.js)
- 动态 DOM 插入延迟脚本(script4.js)
- 内联 BODY 延迟
- 外部 BODY 延迟(script7.js)
- 延迟动态 DOM 插入脚本(script5.js)
- 延迟动态 DOM 插入延迟脚本(script6.js)
- DOMContentLoaded
- Body onLoad
- IE 8 浏览器中的 defer 属性行为不稳定:每次重新加载时顺序都不一样
- 内联 HEAD
- 外部 HEAD(script2.js)
- 内联 BODY
- 外部 BODY(script8.js)
- 动态 DOM 插入脚本(script3.js)
- 动态 DOM 插入延迟脚本(script4.js)
- 内联 HEAD 延迟
- 外部 HEAD 延迟(script1.js)
- 内联 BODY 延迟
- 外部 BODY 延迟(script7.js)
- Body onLoad
- 延迟动态 DOM 插入脚本(script5.js)
- 延迟动态 DOM 插入延迟脚本(script6.js)
- WebKit 浏览器(Safari 4.0)中的 defer 属性行为不稳定 : 每次重新加载时顺序都不一样
- 内联 HEAD 延迟
- 内联 HEAD
- 外部 HEAD 延迟(script1.js)
- 外部 HEAD(script2.js)
- 内联 BODY 延迟
- 内联 BODY
- 外部 BODY 延迟(script7.js)
- 延迟动态 DOM 插入脚本(script5.js)
- 动态 DOM 插入延迟脚本(script4.js)
- 延迟动态 DOM 插入延迟脚本(script6.js)
- 动态 DOM 插入脚本(script3.js)
- 外部 BODY(script8.js)
- DOMContentLoaded
- Body onLoad
- 该deferOpera 10.00 Beta 浏览器的属性行为:
- 内联 HEAD 延迟
- 内联 HEAD
- 外部 HEAD 延迟(script1.js)
- 外部 HEAD(script2.js)
- 动态 DOM 插入脚本(script3.js)
- 动态 DOM 插入延迟脚本(script4.js)
- 延迟动态 DOM 插入脚本(script5.js)
- 延迟动态 DOM 插入延迟脚本(script6.js)
- 内联 BODY 延迟
- 内联 BODY
- 外部 BODY 延迟(script7.js)
- 外部 BODY(script8.js)
- DOMContentLoaded
- Body onLoad
我们希望这篇文章对 defer
属性在 Firefox 3.5 中的工作原理有所帮助。上面的测试也将帮助您预测其他浏览器的行为。
资源
- 修复的错误 28293 – (defer) 带 defer 属性的脚本未延迟 : 错误 28293
- WebsiteOptimization 网站上的“JavaScript: Defer Execution”文章 : http://www.websiteoptimization.com/speed/tweak/defer/
- 该scriptMozilla 开发者中心上的文章 : https://mdn.org.cn/En/HTML/Element/Script
- 该“script元素”是 HTML 5 草案的一部分 : http://www.whatwg.org/specs/web-apps/current-work/#script
- HTML 5 草案的“解析 HTML 文档 : 结束”部分 : http://www.whatwg.org/specs/web-apps/current-work/#the-end
13条评论