js性能优化调试

客户端动态输出table数据并展示表格,是web应用中较为常见的工作。对于循环打印输出tr,td本身是一件非常僵硬和暴力的编程办法,再加上最后绑定元素innerHTML字符流输出,

系统所消耗的性能代价是非常高昂的,如果我们需要展现的数据非常庞大时,那么代价也是成倍的。然而这种动态输出表格的方法是大多数客户端程序员最常用的方法。那么基于最常用的方法,

如何才能降低性能成本,改善用户体验,快速安全的显示我们所需要的数据呢?

我认为从根本上调优需要从两个方面去考虑。

1:server的数据吐出和client的数据解析。这里涉及的知识点较多,今后再做详细的说明。但是对于较为复杂的xml的数据格式来说,client的解析应该用xpath寻址和dom内置对象相结合的方法,高速定位。

2:DHTML的优化。包括dom,css,js的优化,也就是MVC(model,view,control)的优化。

这里我们用js动态生成一个table,构建一个3000行,8列的表格,代码分多个版本,便于清晰的比较每个版本不同的性能消耗。

vision0.1【耗时14694ms】

貌似以下的写法是没有任何错误,但是确是最暴力,效率最低,性能消耗最大的写法。对于大量的数据行和列,用for循环拼接元素字符串,最后innerHTML输出是不可取的。

3000记录页面加载耗时14694毫秒,近15秒。这样的页面数据加载是近乎灾难的,应该竭力避免。

<html>

<body>

<divid="tableDiv"></div>

<script>

varmaxRow=3000;

varmaxCol=8;

varstrTbl="<tableborder='1'><tbody>";

varstrTbody='';

for(vari=0;i<maxRow;i++){

strTbody+="<tr>";

for(varj=0;j<maxCol;j++){

strTbody+="<td>test</td>";

}

strTbody+="</tr>";

}

strTbl=strTbody+"</tbody></table>";

varobj=document.getElementById("tableDiv");

obj.innerHTML=strTbl;

</script>

</body>

</html>

vision 0.2 【耗时3623ms】

这个版本的代码有非常大的改进,采用DOM技术动态添加元素,说明在需要处理展现大量数据的情况下,运用DOM快速定位并添加绑定元素的方法,效率远比拼接html元素字符串的方法要高许多。

整个页面加载完成所耗的时间为3623毫秒。3000行的记录耗时不到4秒,这个版本的代码结构和编程思路也无可挑剔,那么这样的加载速度是否可以再快些呢?

<html>

<body>

<script>

var_table,_tbody,tr,td,text,maxRow,maxCol;

maxRow=3000;

maxCol=8;

_table=document.createElement("table");

_table.border="1";

_tbody=document.createElement("tbody");

_table.insertBefore(_tbody,null);

document.body.insertBefore(_table,null);

for(vari=0;i<maxRow;i++){

tr=document.createElement("tr");

for(varj=0;j<maxCol;j++){

td=document.createElement("td");

text=document.createTextNode("Text");

td.insertBefore(text,null);

tr.insertBefore(td,null);

}

_tbody.insertBefore(tr,null);

}

</script>

</body>

</html>

vision 0.3 【耗时3320ms】

基于vision0.2中的代码,我们可以看到,整个代码段有多处用到了"body","window","document"这样的对象,在js中类似这样的对象都为全局对象,对他们的引用操作势必会消耗性能,

对这些全局变量引用要比简单通过局部变量引用的代价要昂贵的多。

这里我们可以将"document.body"的引用缓存到局部变量中,这样就完成了一个将全局对象转换成局部变量的过程。

在代码中添加:vardocBody=document.body;并且将行:document.body.insertBefore(_table,null);替换为:docBody.insertBefore(_table,null);

在代码中对"document"单个全局对象的引用就达到8×3000=24000次之多,获取一个document变量比局部变量大约多花费4ms时间,所以我们下一步把document对象也缓存起来。

在代码中添加:var_doc=document;

这样,我们重新加载页面,所耗时间为3320毫秒。只比上个版本所耗的时间减少了10%,似乎性能相差不大,但是在我们日常的开发习惯中,将全局的对象缓存到局部变量中是一个好的开始。

<html>

<body>

<script>

var_table,_tbody,tr,td,text,maxRow,maxCol;

vardocBody=document.body;

var_doc=document;

maxRow=3000;

maxCol=8;

_table=_doc.createElement("table");

_table.border="1";

_tbody=_doc.createElement("tbody");

_table.insertBefore(_tbody,null);

docBody.insertBefore(_table,null);

for(vari=0;i<maxRow;i++){

tr=_doc.createElement("tr");

for(varj=0;j<maxCol;j++){

td=_doc.createElement("td");

text=_doc.createTextNode("Text");

td.insertBefore(text,null);

tr.insertBefore(td,null);

}

_tbody.insertBefore(tr,null);

}

</script>

</body>

</html>

vision0.4【耗时2779ms】

一个document对象加载速度的优化就是在<script>标签指定defer属性。首先在这里简单介绍一下defer属性。defer作用是文档加载完毕了再执行脚本,这样回避免找不到对象的问题,

加上defer等于在页面完全在入后再执行,相当于window.onload,但应用上比window.onload更灵活。设置这个属性仅适合不需要立即运行<SCRIPT>中代码的情况。

(立即运行的代码指不在函数体内的--这些代码将会在脚本块加载后立即执行)当defer属性设置后,IE不会等待加载和转换这段脚本。这就也为着页面加载会快很多。

通常这意味着立即运行的脚本应该封装放在一个函数内,并通过document或者body的onload的事件处理。如果你的脚本是依赖于页面加载后的用户动作,如点击按钮,或者移动鼠标到某个区域,会更加有用!

最后请注意两点:

1、不要在defer型的脚本程序段中调用document.write命令,因为document.write将产生直接输出效果。

2、不要在defer型脚本程序段中包括任何立即执行脚本要使用的全局变量或者函数。

<html>

<bodyonload="init()">

<scriptdefer>

functioninit(){

var_table,_tbody,tr,td,text,maxRow,maxCol;

vardocBody=document.body;

var_doc=document;

maxRow=3000;

maxCol=8;

_table=_doc.createElement("table");

_table.border="1";

_tbody=_doc.createElement("tbody");

_table.insertBefore(_tbody,null);

docBody.insertBefore(_table,null);

for(vari=0;i<maxRow;i++){

tr=_doc.createElement("tr");

for(varj=0;j<maxCol;j++){

td=_doc.createElement("td");

text=_doc.createTextNode("Text");

td.insertBefore(text,null);

tr.insertBefore(td,null);

}

_tbody.insertBefore(tr,null);

}

}

</script>

</body>

</html>

vision0.5【耗时2650ms】

上一个版本中的页面加载速度已经缩短到了2779ms。下面我们对代码进行进一步的优化。

我们看到代码中dom操作,绑定子元素的方法是由下至上包裹,这样的元素绑定方式会相对较慢。

create<TR>

create<TD>

createTextNode

第一步insertTextNodeinto<TD>

第二步insert<TD>into<TR>

第三步insert<TR>intoTBODY

现在我们将元素的绑定顺序颠倒过来,由上至下的包裹绑定元素

create<TR>

create<TD>

createTextNode

第一步insert<TR>intoTBODY

第二步insert<TD>into<TR>

第三步insertTextNodeinto<TD>

<html>

<bodyonload="init()">

<scriptdefer>

functioninit(){

var_table,_tbody,tr,td,text,maxRow,maxCol;

vardocBody=document.body;

var_doc=document;

maxRow=3000;

maxCol=8;

_table=_doc.createElement("table");

_table.border="1";

_tbody=_doc.createElement("tbody");

_table.insertBefore(_tbody,null);

docBody.insertBefore(_table,null);

for(vari=0;i<maxRow;i++){

tr=_doc.createElement("tr");

_tbody.insertBefore(tr,null);

for(varj=0;j<maxCol;j++){

td=_doc.createElement("td");

text=_doc.createTextNode("Text");

td.insertBefore(text,null);

tr.insertBefore(td,null);

}

}

}

</script>

</body>

</html>

vision 0.6 【耗时2580ms】

这个版本中我们要调优的是修改table的css属性,使用fixed-table布局(layout)。指定了fixed-table布局后的表格的列宽使用<col>标签设置。

fixed-table布局样式将改善table的性能,因为每个单元格的内容的尺寸不需要进行计算了。这是一个非常实用的性能改善方法,特别是那些有很多列的大型表格。

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

<html>

<bodyonload="init()">

<scriptdefer>

functioninit(){

var_table,_tbody,tr,td,text,maxRow,maxCol;

vardocBody=document.body;

var_doc=document;

maxRow=3000;

maxCol=8;

_table=_doc.createElement("table");

_table.border="1";

_table.style.tableLayout="fixed";

_tbody=_doc.createElement("tbody");

_table.insertBefore(_tbody,null);

docBody.insertBefore(_table,null);

for(vari=0;i<maxRow;i++){

tr=_doc.createElement("tr");

_tbody.insertBefore(tr,null);

for(varj=0;j<maxCol;j++){

td=_doc.createElement("td");

text=_doc.createTextNode("Text");

td.insertBefore(text,null);

tr.insertBefore(td,null);

}

}

}

</script>

</body>

</html>

vision 0.7 【耗时2210ms】

最后的一个版本的调优就是给单元格赋值的方式。在所有的示例中,创建了一个TextNode,并添加给TD。而在这个版本中我们将使用innerText代替插入一个text节点,代码调整为:

td.innerText="Text";

(注意:innerText只在IE中受支持,属于IE扩展,兼容FireFox可使用innerHTML,但是innerHTML正如文章开头所说的,效率非常低下,不建议使用)

<html>

<bodyonload="init()">

<scriptdefer>

functioninit(){

var_table,_tbody,tr,td,text,maxRow,maxCol;

vardocBody=document.body;

var_doc=document;

maxRow=3000;

maxCol=8;

_table=_doc.createElement("table");

_table.border="1";

_table.style.tableLayout="fixed";

_tbody=_doc.createElement("tbody");

docBody.insertBefore(_table,null);

_table.insertBefore(_tbody,null);

for(vari=0;i<maxRow;i++){

tr=_doc.createElement("tr");

_tbody.insertBefore(tr,null);

for(varj=0;j<maxCol;j++){

td=_doc.createElement("td");

td.innerText="Text";

tr.insertBefore(td,null);

}

}

}

</script>

</body>

</html>

vision 0.8 【耗时672ms】终极优化

将字符串作为数组对象的方式是目前效率最高,性能最优的方式。

<script>

vart1=newDate();

</script>

<html>

<head>

<title></title>

<script>

functiontestTime(){

vart2=newDate();

alert(t2-t1+"ms");

}

</script>

</head>

<bodyonload="init();testTime();">

<divid="tableDiv"></div>

<script>

varmaxRow=3000;

varmaxCol=8;

varstrTbody=["<tableborder='1'><tbody>"];

for(vari=0;i<maxRow;i++){

strTbody.push("<tr>");

for(varj=0;j<maxCol;j++){

strTbody.push("<td>test</td>");

}

strTbody.push("</tr>");

}

strTbody.push("</tbody></table>");

varobj=document.getElementById("tableDiv");

obj.innerHTML=strTbody.join("");

</script>

</body>

</html>

相关推荐