Truly
写精彩代码 品暇逸人生


DHTML性能提高的其他技巧
Truly

More Performance Tips(英文)
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dndude/html/dude100499.asp

在微软开发过程的最重要的事情之一就是为产品进行调优。
多数开发人员把性能调优看作是某个功能的审查部分之一。如何为Win32程序调优进行了很多年,有大量的文献可以参考。

令许多DHTML和HTML开发者们的头痛的就是没有足够多的技术文献来了解使页面更快方法和造成了页面缓慢的原因。当然,也有一些简单的事情,比如页面上不使用2MB的图片。不过,在对DHTML页面提速的工作中,我们发现了一些有趣的技巧和窍门,可以用来提高你自己页面的性能。

这次我要调整的代码示例是一个table构建程序。它要构建一个1000多行表格,通过使用document.createElement()和element.insertBefore()方法。每行包含一个单元格cell。每个单元格内容为"Text"。实现这一代码的最差劲的代码是什么样子的?这一小段优化代码可以改善多少?非常多!

我起初写了一段自以为很快的代码,我努力避免了一些非常低级的错误,比如不进行变量声明,或者在同一个页面同时使用VBScript和JScript。代码如下:

<html>
<body>
<script>
  var tbl, tbody, tr, td, text, i, max;
  max = 1000;
  tbl = document.createElement("TABLE");
  tbl.border = "1";
  tbody = document.createElement("TBODY");
  tbl.insertBefore(tbody, null);
  document.body.insertBefore(tbl, null);
  for (i=0; i<max; i++) {
     tr = document.createElement("TR");
     td = document.createElement("TD");
     text = document.createTextNode("Text");
     td.insertBefore(text, null);
     tr.insertBefore(td, null);
     tbody.insertBefore(tr, null);
  }
</script>
</body>
</html>

 

查看示例1

所有的测试都运行在Intel Pentium II 233 ,64MB内存, Windows NT® 4.0和Internet Explorer 5,每个页面都从本地磁盘加载。所有的事件计时都是从页面的初始加载到页面“静止”(所有的队列事件都已执行,屏幕重绘完成)。基线页面(我称作‘Test1’)花费了2328毫秒。

在DHTML页面中经常用到,并且相当耗时的操作是全局对象引用。像“document”, “body”,“window”等,这些变量引用的代价比简单通过一个局部变量引用要昂贵的多。

我决定首先要改动的就是将“document.body”的引用“缓存”到一个变量“theBody”。只是给body属性加了另一个引用,因此这个改动相对小。

代码中增加了该行:

var theBody = document.body;

 

并且将行:

document.body.insertBefore(tbl, null);

 

调整为:

theBody.insertBefore(tbl, null);

 

查看示例2

上面的调整并没有影响到整个的时间,仅仅节省了3ms加载时间。然而,如果我们把这段代码引入到主循环中,结果将会变得极具意义。

接下来我打算做的是把document对象本身也缓存起来。由于document对象被引用了3002次在这个示例中。
下面是将document缓存到局部变量后的版本,所有其它的引用也指向了局部变量:

<html>
<body>
<script>
  var tbl, tbody, tr, td, text, i, max;
  max = 1000;
  var theDoc = document;
  var theBody = theDoc.body;
  tbl = theDoc.createElement("TABLE");
  tbl.border = "1";
  tbody = theDoc.createElement("TBODY");
  tbl.insertBefore(tbody, null);
  theBody.insertBefore(tbl, null);
  for (i=0; i<max; i++) {
     tr = theDoc.createElement("TR");
     td = theDoc.createElement("TD");
     text = theDoc.createTextNode("Text");
     td.insertBefore(text, null);
     tr.insertBefore(td, null);
     tbody.insertBefore(tr, null);
  }
</script>
</body>
</html>

查看示例3

这个版本的页面加载只需要2100ms。这样以来,我们将应用程序的整个运行时间减少了10%,并且很有实际意义。获取一个document变量比局部变量大约多花费4ms时间。

一个是否常用的document加载速度的优化是为<script>标签指定defer属性。设置这个属性仅适合不需要立即运行<SCRIPT>中代码的情况。(立即运行的代码指不在函数体内的--这些代码将会在脚本块加载后立即执行)当defer属性设置后,IE不会等待加载和转换这段脚本。这就也为着页面加载会快很多。通常这意味着立即运行的脚本应该封装放在一个函数内,并通过document或者body的onload的事件处理。如果你的脚本是依赖于页面加载后的用户动作--如点击按钮,或者移动鼠标到某个区域,会更加有用!不过,如果你的代码希望立即执行,或者在page加载后立即马上执行,就无法利用这个特性了。

下面是增加了defer属性后新的版本

<html>
<body onload="init()">
<script defer>
function init() {
  var tbl, tbody, tr, td, text, i, max;
  max = 1000;
  var theDoc = document;
  var theBody = theDoc.body;
  tbl = theDoc.createElement("TABLE");
  tbl.border = "1";
  tbody = theDoc.createElement("TBODY");
  tbl.insertBefore(tbody, null);
  theBody.insertBefore(tbl, null);
  for (i=0; i<max; i++) {
     tr = theDoc.createElement("TR");
     td = theDoc.createElement("TD");
     text = theDoc.createTextNode("Text");
     td.insertBefore(text, null);
     tr.insertBefore(td, null);
     tbody.insertBefore(tr, null);
  }
}
</script>
</body>
</html>

 

查看示例4

页面加载时间降为2043ms了。相对基线页有12%的提升,相对前一版本有2.5%的提升。

接下来要改进的更有意义,但是有一点棘手。当创建element并添加到节点树,并将它们添加到主document上更有效率--不是把它们添加给一个大的子树,然后将这个子树添加到document上。例如,如果我们打算创建一个新的表格行row,并带有一个含有文本的单元格cell,我将这么做:
create <TR>
create <TD>
create TextNode
insert TextNode into <TD>
insert <TD> into <TR>
insert <TR> into TBODY

但是上面的方法会比下面的慢:
create <TR>
create <TD>
create TextNode
insert <TR> into TBODY
insert <TD> into <TR>
insert TextNode into <TD>

前面的示例都使用了第一个方法,示例5将会使用上面第二个方法,完整代码如下:

<html>
<body onload="init()">
<script defer>
function init() {
  var tbl, tbody, tr, td, text, i, max;
  max = 1000;
  var theDoc = document;
  var theBody = theDoc.body;
  tbl = theDoc.createElement("TABLE");
  tbl.border = "1";
  tbody = theDoc.createElement("TBODY");
  tbl.insertBefore(tbody, null);
  theBody.insertBefore(tbl, null);
  for (i=0; i<max; i++) {
     tr = theDoc.createElement("TR");
     td = theDoc.createElement("TD");
     text = theDoc.createTextNode("Text");
     tbody.insertBefore(tr, null);
     tr.insertBefore(td, null);
     td.insertBefore(text, null);
  }
}
</script>
</body>
</html>

 

查看示例5

示例5仅需1649ms。比上个版本要快25%,并且比基线页快了几乎30%。

下一步要调整的是修改table使用fixed-table布局(layout)。指定了fixed-table布局后的表格的列宽使用<COL>标签设置,或者,如果没有<COL>标签,每个单元格的空间从行平分。fixed-table布局样式将改善table的性能,因为每个单元格的内容的尺寸不需要进行计算了。这是一个非常实用的性能改善方法,特别是那些有很多列的大型表格。

这个操作也可以通过简单增加css样式实现:

tbl.style.tableLayout = "fixed";

 

查看示例6

然而,由于本示例只有一个单元格,所以这个改动只带来1.6%的性能提升。这个提示会随着table的单元格数量增加而增加。

最后的2个示例包括调整给单元格赋值的方式。在所有的示例中,创建了一个TextNode,并添加给TD。而示例7,将使用inerText代替插入一个text节点,代码调整为:

td.innerText = "Text";

(译者:innerText只在IE中受支持,属于IE扩展,兼容FireFox可使用innerHTML)

查看示例7

令人惊奇的是,这个改动有显著效果--比上次快了9%,总提升36%。这样以来我们的最终测试以1473ms代替了基线页的2323ms。

目前位置,几乎所有人都知道使用element.innerHTML是很慢,很慢,很慢的。想知道有多慢,我最后放了一个使用innerHTML代替innerText来为单元格添加文本的示例。这个调整击跨了性能,总时间变成3375ms,比上个版本差了80%!。这个变化比基线页还差45%。很明显,innerHTML是相当昂贵的。

你可以查看所有的测试结果,显示了演练和平均值。

调优HTML页面跟调优Win32应用程序一样,你必须知道哪里慢下来了,而哪里比较快。希望这些技巧可以帮助你加速你的页面。

posted on 2006-11-09 22:40  Truly  阅读(1448)  评论(0编辑  收藏  举报