html5页面怎么js延迟加载html元素js文件

HTML5树组件延迟加载技术实现_HTML5教程_前端技术
您的位置: &
& 详细内容
HT for Web的HTML5树组件有延迟加载的功能,这个功能对于那些需要从服务器读取具有层级依赖关系数据时非常有用,需要获取数据的时候再向服务器发起请求,这样可减轻服务器压力,同时也减少了浏览器的等待时间,让页面的加载更加流畅,增强用户体验。
进入正题,今天用来做演示的Demo是,客户端请求服务器读取系统文件目录结构,通过HT for Web的HTML5树组件显示系统文件目录结构。
首先,我们先来设计下服务器,这次Demo的服务器采用Node.js,用到了Node.js的express、socket.io、fs和http这四个模块,Node.js的相关知识,我在这里就不阐述了,网上的教材一堆,这里推荐下socket.io的相关入门http://socket.io/get-started/chat/。
服务端代码代码:
fs = require("fs"),
&&&&express = require("express"),
&&&&app = express(),
&&&&server = require("http").createServer(app),
&&&&io = require("socket.io")(server),
&&&&root = &/Users/admin/Projects/ht-for-web/guide&;
io.on("connection", function(socket){
&&&&socket.on("explore", function(url){
&&&&&&&&socket.emit("file", walk(url
app.use(express.static("/Users/admin/Projects/ht-for-web"));
server.listen(5000, function(){
&&&&console.log("server is listening at port 5000");
io监听了connection事件,并获得一个socket;socket再监听一个 叫explore的自定义事件,通过url参数获取到数据后,派发一个叫file的自定义事件,供客户端监听并做相应处理;通过app.use结合 express.static设置项目路径;最后让server监听5000端口。
到此,一个简单的服务器就搭建好了,现在可以通过http://localhost:5000来访问服务器了。等等,好像缺了点什么。对了,获取系统文件目录结构的方法忘记给了,OK,那么我们就先来看看获取整站文件的代码是怎么写的:
walk(pa) {
dirList = fs.readdirSync(pa),
&&&&&&&&key = pa.substring(pa.lastIndexOf("/") + 1),
&&&&&&&&obj = {
&&&&&&&&&&&&name: key,
&&&&&&&&&&&&path: pa,
&&&&&&&&&&&&children: [],
&&&&&&&&&&&&files: []
&&&&&&&&};
&&&&dirList.forEach(function(item) {
&&&&&&&&var
stats = fs.statSync(pa + "/"
&&&&&&&&if
(stats.isDirectory()) {
&&&&&&&&&&&&obj.children.push(walk(pa + "/"
&&&&&&&&else
&&&&&&&&&&&&obj.files.push({name: item, dir: pa + "/"
&&&&return
如大家所见,采用递归的方式,逐层遍历子目录,代码也没什么高深的地方,相信大家都看得懂。那我们来看看运行效果吧:
duang~文件目录结构出来了,是不是感觉酷酷的,这代码量不小吧。其实,代码并不多,贴出来大家瞅瞅:
lang=&en&&
charset=&UTF-8&&
&&&&&title&tree-loader&/title&
&&&&&script
src=&/socket.io/socket.io.js&&&/script&
&&&&&script
src=&/lib/core/ht.js&&&/script&
&&&&&script&
&&&&&&&&var socket = io(), idMap = {};
&&&&&&&&function init() {
&&&&&&&&&&&&var dm = window.dm = new ht.DataModel(),
&&&&&&&&&&&&&&&&&&&&tree = new ht.widget.TreeView(dm);
&&&&&&&&&&&&&
&&&&&&&&&&&&tree.addToDOM();
&&&&&&&&&&&&socket.on("file", function(data) {
&&&&&&&&&&&&&&&&var root = dm.getDataById(idMap[data.path]);
&&&&&&&&&&&&&&&&createChildren(data.children
[], root, dm);
&&&&&&&&&&&&&&&&createFiles(data.files
[], root, dm);
&&&&&&&&&&&&});
&&&&&&&&&&&&socket.emit("explore");
&&&&&&&&function createChildren(children, parent, dm) {
&&&&&&&&&&&&children.forEach(function(child) {
&&&&&&&&&&&&&&&&var n = createData(child, parent);
&&&&&&&&&&&&&&&&dm.add(n);
&&&&&&&&&&&&&&&&createChildren(child.children
[], n, dm);
&&&&&&&&&&&&&&&&createFiles(child.files
[], n, dm);
&&&&&&&&&&&&});
&&&&&&&&function createFiles(files, parent, dm){
&&&&&&&&&&&&files.forEach(function(file){
&&&&&&&&&&&&&&&&var n = createData(file, parent);
&&&&&&&&&&&&&&&&dm.add(n);
&&&&&&&&&&&&});
&&&&&&&&function createData(data, parent){
&&&&&&&&&&&&var n = new ht.Data();
&&&&&&&&&&&&n.setName(data.name);
&&&&&&&&&&&&n.setParent(parent);
&&&&&&&&&&&&n.a("path", data.path);
&&&&&&&&&&&&idMap[data.path] = n.getId();
&&&&&&&&&&&&
&&&&&/script&
onload=&init();&&
这就是全部的HTML代码,加上空行总共也就50几行,怎么样,有没有感觉HT for Web很强大。废话不多说,来看看这些代码都干了些什么:
&&& 要用到socket.io就需要在页面引入&script src=&/socket.io/socket.io.js&&&/script&,其实在我的项目中并不存在/socket.io/socket.io.js文件,但是却能正常使用,具体什么原因,我就不多说,大家自己研究去吧;
&&& 最重要的是要引入HT for Web的核心包&script src=&/lib/core/ht.js&&&/script&,这个包不引入的话,下面的HT for Web组件就无法使用;
&&& 接下来就是代码了,首先创建一个数据容器DataModel,用来存放文件目录的节点数据,再创建一个TreeView对象并引用刚创建到数据容器,接下来通过socket监听file事件,获取服务器返回的数据,在回调函数中通过调用createChildren和createFiles函数,创建文件目录节点对象,并添加到数据容器中,最后是向服务器发起数据请求,即通过socket派发explore事件。
整体的思路是这样子的,当然这离我们要实现的树组件的延迟加载技术还有些差距,那么,HT for Web的HTML5树组件的延迟加载技术是怎么实现的呢?不要着急,马上开始探讨。
首先我们需要改造下获取文件目录的方法walk,因为前面介绍的方法中,使用的是加载整站文件目录,所以我们要将walk方法改造成只获取一级目录结构,改造起来很简单,就是将递归部分改造成获取当前节点就可以了,具体代码如下:
obj.children.push(walk(pa + "/"
obj.children.push({name: item, path: pa + "/"
这样子服务器就只请求当前请求路径下的第一级文件目录结构。接下来就是要调整下客户端代码了,首先需要给tree设置上loader:
tree.setLoader({
&&&&load: function(data) {
&&&&&&&&socket.emit("explore", data.a("path"));
&&&&&&&&data.a("loaded", true);
&&&&isLoaded: function(data) {
&&&&&&&&return
data.a("loaded");
loader包含了两个方法,load和isLoaded,这两个方法的功能分别是加载 数据和判断数据是否已经加载,在load方法中,对socket派发explore事件,当前节点的path为参数,向服务器请求数据,之后将当前节点的 loaded属性设置为true;在isLoaded方法中,返回当前节点的loaded属性,如果返回为true,那么tree将不会在执行load方 法向服务器请求数据。
接下来需要移除createChildren的两个回调方法,并且在 createFiles方法中为创建出来的节点的loaded属性设置成true,这样在不是目录的节点前就不会有展开的图标。 createChildren和createFiles两个方法修改后的代码如下:
createChildren(children, parent, dm) {
&&&&children.forEach(function(child) {
&&&&&&&&var
n = createData(child, parent);
&&&&&&&&dm.add(n);
createFiles(files, parent, dm){
&&&&files.forEach(function(file){
&&&&&&&&var
n = createData(file, parent);
&&&&&&&&n.a("loaded", true);
&&&&&&&&dm.add(n);
如此,延迟加载技术就设计完成了,我在服务器的控制台打印出请求路径,看看这个延迟加载是不是真的,如下图:
看吧,控制台打印的是4条记录,第一条是请求跟目录时打印的,我在浏览器中展开里三个目录,在控制台打印了其对应的目录路径。
等等,现在这个目录看起来好烦,只有文字,除了位子前的展开图标可以用来区别文件和目录外,没有其他什么区别,所以我决定对其进行一番改造,让每一级目录都有图标,而且不同文件对应不同的图标,来看看效果吧:
怎么样,是不是一眼就能看出是什么文件,这个都是样式上面的问题,我就不再一一阐述了,直接上代码:
lang=&en&&
charset=&UTF-8&&
&&&&&title&&/title&
&&&&&script
src=&/socket.io/socket.io.js&&&/script&
&&&&&script
src=&/build/ht-debug.js&&&/script&
&&&&&script&
&&&&&&&&var socket = io(), idMap = {};
&&&&&&&&function init() {
&&&&&&&&&&&&var icons = ["css", "dir-open", "dir", "file", "flash", "gif", "html", "jar",
&&&&&&&&&&&&&&&&"java", "mp3", "pdf", "png", "script", "txt", "video", "xml", "zip"];
&&&&&&&&&&&&icons.forEach(function(c){
&&&&&&&&&&&&&&&&ht.Default.setImage(c, 16, 16, "/test/wyl/images/" + c + ".png");
&&&&&&&&&&&&});
&&&&&&&&&&&&var dm = window.dm = new ht.DataModel(),
&&&&&&&&&&&&&&&&&&&&tree = new ht.widget.TreeView(dm);
&&&&&&&&&&&&tree.setLoader({
&&&&&&&&&&&&&&&&load: function(data) {
&&&&&&&&&&&&&&&&&&&&socket.emit("explore", data.a("path"));
&&&&&&&&&&&&&&&&&&&&data.a("loaded", true);
&&&&&&&&&&&&&&&&},
&&&&&&&&&&&&&&&&isLoaded: function(data) {
&&&&&&&&&&&&&&&&&&&&return data.a("loaded");
&&&&&&&&&&&&&&&&}
&&&&&&&&&&&&});
&&&&&&&&&&&&tree.getLabelFont = function(data){
&&&&&&&&&&&&&&&&return "13px Helvetica, Arial, sans-serif";
&&&&&&&&&&&&};
&&&&&&&&&&&&tree.getLabelColor = function (data) {
&&&&&&&&&&&&&&&&return this.isSelected(data) ? "white" : "black";
&&&&&&&&&&&&};
&&&&&&&&&&&&tree.getSelectBackground = function (data) {
&&&&&&&&&&&&&&&&return "#408EDB";
&&&&&&&&&&&&};
&&&&&&&&&&&&tree.getIcon = function (data) {
&&&&&&&&&&&&&&&&var icon = data.getIcon()
&&&&&&&&&&&&&&&&if (data.a("isdir")) {
&&&&&&&&&&&&&&&&&&&&if (this.isExpanded(data)) {
&&&&&&&&&&&&&&&&&&&&&&&&icon = "dir-open";
&&&&&&&&&&&&&&&&&&&&} else {
&&&&&&&&&&&&&&&&&&&&&&&&icon = "dir";
&&&&&&&&&&&&&&&&&&&&}
&&&&&&&&&&&&&&&&}
&&&&&&&&&&&&&&&&
&&&&&&&&&&&&};
&&&&&&&&&&&&tree.addToDOM();
&&&&&&&&&&&&socket.on("file", function(data) {
&&&&&&&&&&&&&&&&var root = dm.getDataById(idMap[data.path]);
&&&&&&&&&&&&&&&&createChildren(data.children
[], root, dm);
&&&&&&&&&&&&&&&&createFiles(data.files
[], root, dm);
&&&&&&&&&&&&});
&&&&&&&&&&&&socket.emit("explore");
&&&&&&&&function createChildren(children, parent, dm) {
&&&&&&&&&&&&children.forEach(function(child) {
&&&&&&&&&&&&&&&&var n = createData(child, parent);
&&&&&&&&&&&&&&&&n.a("isdir", true);
&&&&&&&&&&&&&&&&dm.add(n);
&&&&&&&&&&&&});
&&&&&&&&function createFiles(files, parent, dm){
&&&&&&&&&&&&files.forEach(function(file){
&&&&&&&&&&&&&&&&var n = createData(file, parent);
&&&&&&&&&&&&&&&&n.a("loaded", true);
&&&&&&&&&&&&&&&&dm.add(n);
&&&&&&&&&&&&});
&&&&&&&&function createData(data, parent){
&&&&&&&&&&&&var name = data.name,
&&&&&&&&&&&&&&&&&&&&icon = "file";
&&&&&&&&&&&&if (/.jar$/.test(name)) icon = "jar";
&&&&&&&&&&&&else if (/.css$/.test(name)) icon = "css";
&&&&&&&&&&&&else if (/.gif$/.test(name)) icon = "gif";
&&&&&&&&&&&&else if (/.png$/.test(name)) icon = "png";
&&&&&&&&&&&&else if (/.js$/.test(name)) icon = "script";
&&&&&&&&&&&&else if (/.html$/.test(name)) icon = "html";
&&&&&&&&&&&&else if (/.zip$/.test(name)) icon = "zip";
&&&&&&&&&&&&var n = new ht.Data();
&&&&&&&&&&&&n.setName(data.name);
&&&&&&&&&&&&n.setParent(parent);
&&&&&&&&&&&&n.setIcon(icon);
&&&&&&&&&&&&n.a("path", data.path);
&&&&&&&&&&&&idMap[data.path] = n.getId();
&&&&&&&&&&&&
&&&&&/script&
onload=&init();&&
在最后,附上完整的服务器代码:
fs = require("fs"),
&&&&express = require("express"),
&&&&app = express(),
&&&&server = require("http").createServer(app),
&&&&io = require("socket.io")(server),
&&&&root = "/Users/admin/Projects/ht-for-web/guide";
io.on("connection", function(socket){
&&&&socket.on("explore", function(url){
&&&&&&&&socket.emit("file", walk(url
app.use(express.static("/Users/admin/Projects/ht-for-web"));
server.listen(5000, function(){
&&&&console.log("server is listening at port 5000");
walk(pa) {
dirList = fs.readdirSync(pa),
&&&&&&&&key = pa.substring(pa.lastIndexOf("/") + 1),
&&&&&&&&obj = {
&&&&&&&&&&&&name: key,
&&&&&&&&&&&&path: pa,
&&&&&&&&&&&&children: [],
&&&&&&&&&&&&files: []
&&&&&&&&};
&&&&dirList.forEach(function(item) {
&&&&&&&&var
stats = fs.statSync(pa + "/"
&&&&&&&&if
(stats.isDirectory()) {
&&&&&&&&&&&&obj.children.push({name: item, path: pa + "/"
&&&&&&&&else
&&&&&&&&&&&&obj.files.push({name: item, dir: pa + "/"
&&&&return
( 01:03:56)
( 01:03:55)
( 01:03:53)
( 01:03:42)
( 01:03:37)
( 01:03:34)
( 01:03:16)
( 23:06:33)
相关排行总榜Html5 页面中 JavaScript 启动调用的三种方法比较
Html5 页面中 Script 启动调用的三种方法比较太阳火神的美丽人生 (http://blog.csdn.net/opengl_es)本文遵循“署名-非商业用途-保持一致”创作公用协议转载请保留此句:太阳火神的美丽人生 -
本博客专注于 敏捷开发及移动和物联设备研究:iOS、Android、Html5、Arduino、pcDuino,否则,出自本博客的文章拒绝转载或再转载,谢谢合作。首先,来看一下 Html5 页面引用 JavaScript 代码的几种方式:1、Html5 页面中使用 &script&
标签容纳 JavaScript 代码;
&/script&这里不仅可以调用任意位置的函数,而且可以声明变量,构建函数和对象等等。2、Html5 页面中使用 &script src="xxx.js"&&/script& 引入同路径下的 xxx.js 文件中的 JavaScript 代码; &script src="js/main.js"&&/script&注意这里的 main.js 是与当前 Html 页面文件同目录下的 js 子目录中的文件,使用时,确保相对路径正确,当然了,也可以使用绝对路径。3、Html5 界面元素事件直接赋与一段 JavaScript 代码;
此处可参考 javascript设置onclick以下资源可供参考,进一步理解 JavaScritp:JavaScript的5种调用函数的方法解读ECMAScript[1]――执行环境、作用域及闭包解读ECMAScript[2]――函数、构造器及原型百度百科 javascript要想真正掌握 JavaScript 语言,那么闭包是必须要深刻理解和领悟的核心。顺便,记录一下引用
的几种方式:1、外部样式表css 样式存于一个 .css 扩展名的文件中,引用方式:
2、内部样式表
3、内联样式更详细的 CSS 运用,可参考 CSS 选择器及各样式引用方式介绍 。 最后,切入主题,页面加载初始调用
代码的三种方式:1、body 的 onload 事件触发回调 JavaScript 函数; 该事件在 document 文档中的所有内容都加载完成后才会被调用,这种方式显得有点笨拙,但比较把握,确保一切安好,才去调用。如想避勉过多加载,那么页面设计上可以相对讲究些方法,人为达到滞后加载的效果,就可以给这个事件进行减压,达到尽快执行的目标。2、document 的 onready 事件触发回调 JavaScript 函数;
document.onready = init();该事件在 document 文档中的 dom 模型加载完成就会产生回调,而不考虑引用的图片、脚本等资源。3、在引入 Html5 页面的 JavaScript 代码中,直接调用 JavaScript 函数或执行 JavaScript 命令;
&/script&或在外部 js 脚本中,直接调用 init(); init() 可以是 Html5 页面中声明的函数,也可以是外部引用的脚本中的函数。不过发现一个有趣的问题,当在 init() 函数定义中,通过 document.getElementById('container');获取 container 元素的引用时,如果该 Html5 元素在该段页面内 JavaScript 代码之后时,会获取不到该元素。这说明一个问题,第三种启动执行方式,是边加载边执行,所以对顺序有要求;每时每刻,都有老人变新人,老手涉猎新事物,变潮人,接触陌生的领域,变新手。凡事都有这个过程,新领域的成熟与否,其实你与无太大关系,你还是一样的新;但这个领域的成熟,标志着,有这么多别人分享的东西,可供参考,内在的 bug 也会少很多;但无论怎样,在你的头脑里,还是需要一点点去塞满,并不代表,这个领域有多少,你就有多少,也不代表,你一下子就能把这个领域成熟的东西就全能掌握,那是需要不断理解、实践,总结、升华,以便再进一步获取更深层次理解的过程,唐僧取到的经书,如果不被水泡了,可能我们真的就能去掉这个理解的过程了,悟性确实是可以很高的,这个高真能达到不需要理解的过程?!。。。。。。IBM Bluemix
点击按钮,开始云上的开发!
developerWorks 社区
随着 Web2.0 技术的不断推广,越来越多的应用使用 JavaScript 技术在客户端进行处理,从而使 JavaScript 在浏览器中的性能成为开发者所面临的最重要的可用性问题。而这个问题又因 JavaScript 的阻塞特性变的复杂,也就是说当浏览器在执行 JavaScript 代码时,不能同时做其他任何事情。本文详细介绍了如何正确的加载和执行 JavaScript 代码,从而提高其在浏览器中的性能。
, 软件工程师,
蔡愉晟 是 IBM 中国软件开发实验室 Smarter Commerce 旗下产品 DemandTec 的一名软件工程师,之前曾在 UT/LTD 部门工作,目前主要从事 DemandTec: End-2-End Promotion 产品的开发工作。对 HTML5、CSS3 相关技术有浓厚的兴趣。
概览无论当前 JavaScript 代码是内嵌还是在外链文件中,页面的下载和渲染都必须停下来等待脚本执行完成。JavaScript 执行过程耗时越久,浏览器等待响应用户输入的时间就越长。浏览器在下载和执行脚本时出现阻塞的原因在于,脚本可能会改变页面或 JavaScript 的命名空间,它们对后面页面内容造成影响。一个典型的例子就是在页面中使用document.write()。例如清单 1清单 1 JavaScript 代码内嵌示例&html&
&title&Source Example&/title&
&script type="text/javascript"&
document.write("Today is " + (new Date()).toDateString());
&/html&当浏览器遇到&script&标签时,当前 HTML 页面无从获知 JavaScript 是否会向&p& 标签添加内容,或引入其他元素,或甚至移除该标签。因此,这时浏览器会停止处理页面,先执行 JavaScript代码,然后再继续解析和渲染页面。同样的情况也发生在使用 src 属性加载 JavaScript的过程中,浏览器必须先花时间下载外链文件中的代码,然后解析并执行它。在这个过程中,页面渲染和用户交互完全被阻塞了。脚本位置HTML 4 规范指出 &script& 标签可以放在 HTML 文档的&head&或&body&中,并允许出现多次。Web 开发人员一般习惯在 &head& 中加载外链的 JavaScript,接着用 &link& 标签用来加载外链的 CSS 文件或者其他页面信息。例如清单 2清单 2 低效率脚本位置示例&html&
&title&Source Example&/title&
&script type="text/javascript" src="script1.js"&&/script&
&script type="text/javascript" src="script2.js"&&/script&
&script type="text/javascript" src="script3.js"&&/script&
&link rel="stylesheet" type="text/css" href="styles.css"&
&p&Hello world!&/p&
&/html&然而这种常规的做法却隐藏着严重的性能问题。在清单 2 的示例中,当浏览器解析到 &script& 标签(第 4 行)时,浏览器会停止解析其后的内容,而优先下载脚本文件,并执行其中的代码,这意味着,其后的 styles.css 样式文件和&body&标签都无法被加载,由于&body&标签无法被加载,那么页面自然就无法渲染了。因此在该 JavaScript 代码完全执行完之前,页面都是一片空白。图 1 描述了页面加载过程中脚本和样式文件的下载过程。图 1 JavaScript 文件的加载和执行阻塞其他文件的下载我们可以发现一个有趣的现象:第一个 JavaScript 文件开始下载,与此同时阻塞了页面其他文件的下载。此外,从 script1.js 下载完成到 script2.js 开始下载前存在一个延时,这段时间正好是 script1.js 文件的执行过程。每个文件必须等到前一个文件下载并执行完成才会开始下载。在这些文件逐个下载过程中,用户看到的是一片空白的页面。从 IE 8、Firefox 3.5、Safari 4 和 Chrome 2 开始都允许并行下载 JavaScript 文件。这是个好消息,因为&script&标签在下载外部资源时不会阻塞其他&script&标签。遗憾的是,JavaScript 下载过程仍然会阻塞其他资源的下载,比如样式文件和图片。尽管脚本的下载过程不会互相影响,但页面仍然必须等待所有 JavaScript 代码下载并执行完成才能继续。因此,尽管最新的浏览器通过允许并行下载提高了性能,但问题尚未完全解决,脚本阻塞仍然是一个问题。由于脚本会阻塞页面其他资源的下载,因此推荐将所有&script&标签尽可能放到&body&标签的底部,以尽量减少对整个页面下载的影响。例如清单 3清单 3 推荐的代码放置位置示例&html&
&title&Source Example&/title&
&link rel="stylesheet" type="text/css" href="styles.css"&
&p&Hello world!&/p&
&!-- Example of efficient script positioning --&
&script type="text/javascript" src="script1.js"&&/script&
&script type="text/javascript" src="script2.js"&&/script&
&script type="text/javascript" src="script3.js"&&/script&
&/html&这段代码展示了在 HTML 文档中放置&script&标签的推荐位置。尽管脚本下载会阻塞另一个脚本,但是页面的大部分内容都已经下载完成并显示给了用户,因此页面下载不会显得太慢。这是优化 JavaScript 的首要规则:将脚本放在底部。组织脚本由于每个&script&标签初始下载时都会阻塞页面渲染,所以减少页面包含的&script&标签数量有助于改善这一情况。这不仅针对外链脚本,内嵌脚本的数量同样也要限制。浏览器在解析 HTML 页面的过程中每遇到一个&script&标签,都会因执行脚本而导致一定的延时,因此最小化延迟时间将会明显改善页面的总体性能。这个问题在处理外链 JavaScript 文件时略有不同。考虑到 HTTP 请求会带来额外的性能开销,因此下载单个 100Kb 的文件将比下载 5 个 20Kb 的文件更快。也就是说,减少页面中外链脚本的数量将会改善性能。通常一个大型网站或应用需要依赖数个 JavaScript 文件。您可以把多个文件合并成一个,这样只需要引用一个&script&标签,就可以减少性能消耗。文件合并的工作可通过离线的打包工具或者一些实时的在线服务来实现。需要特别提醒的是,把一段内嵌脚本放在引用外链样式表的&link&之后会导致页面阻塞去等待样式表的下载。这样做是为了确保内嵌脚本在执行时能获得最精确的样式信息。因此,建议不要把内嵌脚本紧跟在&link&标签后面。无阻塞的脚本减少 JavaScript 文件大小并限制 HTTP 请求数在功能丰富的 Web 应用或大型网站上并不总是可行。Web 应用的功能越丰富,所需要的 JavaScript 代码就越多,尽管下载单个较大的 JavaScript 文件只产生一次 HTTP 请求,却会锁死浏览器的一大段时间。为避免这种情况,需要通过一些特定的技术向页面中逐步加载 JavaScript 文件,这样做在某种程度上来说不会阻塞浏览器。无阻塞脚本的秘诀在于,在页面加载完成后才加载 JavaScript 代码。这就意味着在 window 对象的 onload事件触发后再下载脚本。有多种方式可以实现这一效果。延迟加载脚本HTML 4 为&script&标签定义了一个扩展属性:defer。Defer 属性指明本元素所含的脚本不会修改 DOM,因此代码能安全地延迟执行。defer 属性只被 IE 4 和 Firefox 3.5 更高版本的浏览器所支持,所以它不是一个理想的跨浏览器解决方案。在其他浏览器中,defer 属性会被直接忽略,因此&script&标签会以默认的方式处理,也就是说会造成阻塞。然而,如果您的目标浏览器支持的话,这仍然是个有用的解决方案。清单 4 是一个例子清单 4 defer 属性使用方法示例&script type="text/javascript" src="script1.js" defer&&/script&带有 defer 属性的&script&标签可以放置在文档的任何位置。对应的 JavaScript 文件将在页面解析到&script&标签时开始下载,但不会执行,直到 DOM 加载完成,即onload事件触发前才会被执行。当一个带有 defer 属性的 JavaScript 文件下载时,它不会阻塞浏览器的其他进程,因此这类文件可以与其他资源文件一起并行下载。任何带有 defer 属性的&script&元素在 DOM 完成加载之前都不会被执行,无论内嵌或者是外链脚本都是如此。清单 5 的例子展示了defer属性如何影响脚本行为:清单 5 defer 属性对脚本行为的影响&html&
&title&Script Defer Example&/title&
&script type="text/javascript" defer&
alert("defer");
&script type="text/javascript"&
alert("script");
&script type="text/javascript"&
window.onload = function(){
alert("load");
&/html&这段代码在页面处理过程中弹出三次对话框。不支持 defer 属性的浏览器的弹出顺序是:“defer”、“script”、“load”。而在支持 defer 属性的浏览器上,弹出的顺序则是:“script”、“defer”、“load”。请注意,带有 defer 属性的&script&元素不是跟在第二个后面执行,而是在 onload 事件被触发前被调用。如果您的目标浏览器只包括 Internet Explorer 和 Firefox 3.5,那么 defer 脚本确实有用。如果您需要支持跨领域的多种浏览器,那么还有更一致的实现方式。HTML 5 为&script&标签定义了一个新的扩展属性:async。它的作用和 defer 一样,能够异步地加载和执行脚本,不因为加载脚本而阻塞页面的加载。但是有一点需要注意,在有 async 的情况下,JavaScript 脚本一旦下载好了就会执行,所以很有可能不是按照原本的顺序来执行的。如果 JavaScript 脚本前后有依赖性,使用 async 就很有可能出现错误。动态脚本元素文档对象模型(DOM)允许您使用 JavaScript 动态创建 HTML 的几乎全部文档内容。&script&元素与页面其他元素一样,可以非常容易地通过标准 DOM 函数创建:清单 6 通过标准 DOM 函数创建&script&元素var script = document.createElement ("script");
script.type = "text/javascript";
script.src = "script1.js";
document.getElementsByTagName("head")[0].appendChild(script);新的&script&元素加载 script1.js 源文件。此文件当元素添加到页面之后立刻开始下载。此技术的重点在于:无论在何处启动下载,文件的下载和运行都不会阻塞其他页面处理过程。您甚至可以将这些代码放在&head&部分而不会对其余部分的页面代码造成影响(除了用于下载文件的 HTTP 连接)。当文件使用动态脚本节点下载时,返回的代码通常立即执行(除了 Firefox 和 Opera,他们将等待此前的所有动态脚本节点执行完毕)。当脚本是“自运行”类型时,这一机制运行正常,但是如果脚本只包含供页面其他脚本调用调用的接口,则会带来问题。这种情况下,您需要跟踪脚本下载完成并是否准备妥善。可以使用动态 &script& 节点发出事件得到相关信息。Firefox、Opera, Chorme 和 Safari 3+会在&script&节点接收完成之后发出一个 onload 事件。您可以监听这一事件,以得到脚本准备好的通知:清单 7 通过监听 onload 事件加载 JavaScript 脚本var script = document.createElement ("script")
script.type = "text/javascript";
//Firefox, Opera, Chrome, Safari 3+
script.onload = function(){
alert("Script loaded!");
script.src = "script1.js";
document.getElementsByTagName("head")[0].appendChild(script);Internet Explorer 支持另一种实现方式,它发出一个 readystatechange 事件。&script&元素有一个 readyState 属性,它的值随着下载外部文件的过程而改变。readyState 有五种取值:
“uninitialized”:默认状态
“loading”:下载开始
“loaded”:下载完成
“interactive”:下载完成但尚不可用
“complete”:所有数据已经准备好微软文档上说,在&script&元素的生命周期中,readyState
的这些取值不一定全部出现,但并没有指出哪些取值总会被用到。实践中,我们最感兴趣的是“loaded”和“complete”状态。Internet Explorer 对这两个 readyState
值所表示的最终状态并不一致,有时&script&元素会得到“loader”却从不出现“complete”,但另外一些情况下出现“complete”而用不到“loaded”。最安全的办法就是在 readystatechange 事件中检查这两种状态,并且当其中一种状态出现时,删除 readystatechange 事件句柄(保证事件不会被处理两次):清单 8 通过检查 readyState 状态加载 JavaScript 脚本var script = document.createElement("script")
script.type = "text/javascript";
//Internet Explorer
script.onreadystatechange = function(){
if (script.readyState == "loaded" || script.readyState == "complete"){
script.onreadystatechange =
alert("Script loaded.");
script.src = "script1.js";
document.getElementsByTagName("head")[0].appendChild(script);大多数情况下,您希望调用一个函数就可以实现 JavaScript 文件的动态加载。下面的函数封装了标准实现和 IE 实现所需的功能:清单 9 通过函数进行封装function loadScript(url, callback){
var script = document.createElement ("script")
script.type = "text/javascript";
if (script.readyState){ //IE
script.onreadystatechange = function(){
if (script.readyState == "loaded" || script.readyState == "complete"){
script.onreadystatechange =
callback();
} else { //Others
script.onload = function(){
callback();
script.src =
document.getElementsByTagName("head")[0].appendChild(script);
}此函数接收两个参数:JavaScript 文件的 URL,和一个当 JavaScript 接收完成时触发的回调函数。属性检查用于决定监视哪种事件。最后一步,设置 src 属性,并将&script&元素添加至页面。此 loadScript() 函数使用方法如下:清单 10 loadScript()函数使用方法loadScript("script1.js", function(){
alert("File is loaded!");
});您可以在页面中动态加载很多 JavaScript 文件,但要注意,浏览器不保证文件加载的顺序。所有主流浏览器之中,只有 Firefox 和 Opera 保证脚本按照您指定的顺序执行。其他浏览器将按照服务器返回它们的次序下载并运行不同的代码文件。您可以将下载操作串联在一起以保证他们的次序,如下:清单 11 通过 loadScript()函数加载多个 JavaScript 脚本loadScript("script1.js", function(){
loadScript("script2.js", function(){
loadScript("script3.js", function(){
alert("All files are loaded!");
});此代码等待 script1.js 可用之后才开始加载 script2.js,等 script2.js 可用之后才开始加载 script3.js。虽然此方法可行,但如果要下载和执行的文件很多,还是有些麻烦。如果多个文件的次序十分重要,更好的办法是将这些文件按照正确的次序连接成一个文件。独立文件可以一次性下载所有代码(由于这是异步进行的,使用一个大文件并没有什么损失)。动态脚本加载是非阻塞 JavaScript 下载中最常用的模式,因为它可以跨浏览器,而且简单易用。使用 XMLHttpRequest(XHR)对象此技术首先创建一个 XHR 对象,然后下载 JavaScript 文件,接着用一个动态 &script& 元素将 JavaScript 代码注入页面。清单 12 是一个简单的例子:清单 12 通过 XHR 对象加载 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.responseT
document.body.appendChild(script);
xhr.send(null);此代码向服务器发送一个获取 script1.js 文件的 GET 请求。onreadystatechange 事件处理函数检查 readyState 是不是 4,然后检查 HTTP 状态码是不是有效(2XX 表示有效的回应,304 表示一个缓存响应)。如果收到了一个有效的响应,那么就创建一个新的&script&元素,将它的文本属性设置为从服务器接收到的 responseText 字符串。这样做实际上会创建一个带有内联代码的&script&元素。一旦新&script&元素被添加到文档,代码将被执行,并准备使用。这种方法的主要优点是,您可以下载不立即执行的 JavaScript 代码。由于代码返回在&script&标签之外(换句话说不受&script&标签约束),它下载后不会自动执行,这使得您可以推迟执行,直到一切都准备好了。另一个优点是,同样的代码在所有现代浏览器中都不会引发异常。此方法最主要的限制是:JavaScript 文件必须与页面放置在同一个域内,不能从 CDN 下载(CDN 指"内容投递网络(Content Delivery Network)",所以大型网页通常不采用 XHR 脚本注入技术。总结减少 JavaScript 对性能的影响有以下几种方法:
将所有的&script&标签放到页面底部,也就是&/body&闭合标签之前,这能确保在脚本执行前页面已经完成了渲染。
尽可能地合并脚本。页面中的&script&标签越少,加载也就越快,响应也越迅速。无论是外链脚本还是内嵌脚本都是如此。
采用无阻塞下载 JavaScript 脚本的方法:
使用&script&标签的 defer 属性(仅适用于 IE 和 Firefox 3.5 以上版本);
使用动态创建的&script&元素来下载并执行代码;
使用 XHR 对象下载 JavaScript 代码并注入页面中。通过以上策略,可以在很大程度上提高那些需要使用大量 JavaScript 的 Web 网站和应用的实际性能。
Steve Sounders 撰写的介绍了网站性能问题的现状、产生的原因,以及改善或解决性能问题的原则、技术技巧和最佳实践。
Steve Sounders 撰写的提供了提升网站性能的最佳实践和实用建议。
参考了在 JavaScript 影响页面下载性能方面的研究成果。
Steve Sounders 的个人博客 (developerWorks,2011 年 8 月):找出瓶颈,加快客户端内容的速度。 (developerWorks,2010 年 2 月):浏览器端页面渲染时间的性能分析。:通过专门关于 Web 技术的文章和教程,扩展您在网站开发方面的技能。:这是有关 Ajax 编程模型信息的一站式中心,包括很多文档、教程、论坛、blog、wiki 和新闻。任何 Ajax 的新信息都能在这里找到。,这是有关 Web 2.0 相关信息的一站式中心,包括大量 Web 2.0 技术文章、教程、下载和相关技术资源。您还可以通过
栏目,迅速了解 Web 2.0 的相关概念。
查看 ,了解更多和 HTML5 相关的知识和动向。加入 。查看开发人员推动的博客、论坛、组和维基,并与其他 developerWorks 用户交流。
developerWorks: 登录
标有星(*)号的字段是必填字段。
保持登录。
单击提交则表示您同意developerWorks 的条款和条件。 查看条款和条件。
在您首次登录 developerWorks 时,会为您创建一份个人概要。您的个人概要中的信息(您的姓名、国家/地区,以及公司名称)是公开显示的,而且会随着您发布的任何内容一起显示,除非您选择隐藏您的公司名称。您可以随时更新您的 IBM 帐户。
所有提交的信息确保安全。
选择您的昵称
当您初次登录到 developerWorks 时,将会为您创建一份概要信息,您需要指定一个昵称。您的昵称将和您在 developerWorks 发布的内容显示在一起。昵称长度在 3 至 31 个字符之间。
您的昵称在 developerWorks 社区中必须是唯一的,并且出于隐私保护的原因,不能是您的电子邮件地址。
标有星(*)号的字段是必填字段。
(昵称长度在 3 至 31 个字符之间)
单击提交则表示您同意developerWorks 的条款和条件。 .
所有提交的信息确保安全。
文章、教程、演示,帮助您构建、部署和管理云应用。
立即加入来自 IBM 的专业 IT 社交网络。
为灾难恢复构建应用,赢取现金大奖。
static.content.url=/developerworks/js/artrating/SITE_ID=10Zone=Web developmentArticleID=942924ArticleTitle=JavaScript 的性能优化:加载和执行publish-date=

我要回帖

更多关于 js延迟加载html元素 的文章

 

随机推荐