Javascript性能优化案例

客户端动态输出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列的表格,代码分多个版本,便于清晰的比较每个版本不同的性能消耗。


vision 0.1 【耗时14694ms】

貌似以下的写法是没有任何错误,但是确是最暴力,效率最低,性能消耗最大的写法。对于大量的数据行和列,用for循环拼接元素字符串,最后innerHTML输出是不可取的。3000记录页面加载耗时14694毫秒,近15秒。这样的页面数据加载是近乎灾难的,应该竭力避免。

<html>
	<body>
	<div ></div>
	<script>
		var maxRow =3000;
		var maxCol = 8;
		var strTbl = "<table border='1'><tbody>";
		var strTbody = '';
		for(var i = 0; i < maxRow; i++){
			strTbody +="<tr>";
			for(var j = 0; j < maxCol; j++){
				strTbody += "<td>test</td>";
			}
			strTbody += "</tr>";
		}
		strTbl = strTbody + "</tbody></table>";
		var obj = 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 (var i=0; i<maxRow; i++) {
		tr = document.createElement("tr");
		for(var j = 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"的引用缓存到局部变量中,这样就完成了一个将全局对象转换成局部变量的过程。在代码中添加:

var docBody = 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;
	var docBody = 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 (var i=0; i<maxRow; i++) {
		tr = _doc.createElement("tr");
		for(var j = 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>

vision 0.4 【耗时2779ms】
一个document对象加载速度的优化就是在<script>标签指定defer属性。首先在这里简单介绍一下defer属性。defer作用是文档加载完毕了再执行脚本,这样会避免找不到对象的问题,加上defer等于在页面完全在入后再执行,相当于window.onload,但应用上比window.onload 更灵活。设置这个属性仅适合不需要立即运行<SCRIPT>中代码的情况。

HTML 4 为<script>标签定义了一个扩展属性:defer。Defer 属性指明本元素所含的脚本不会修改 DOM,因此代码能安全地延迟执行。defer 属性只被 IE 4 和 Firefox 3.5 更高版本的浏览器所支持,所以它不是一个理想的跨浏览器解决方案。在其他浏览器中,defer 属性会被直接忽略;

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

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

最后请注意两点:
1、不要在defer型的脚本程序段中调用document.write命令,因为document.write将产生直接输出效果。
2、不要在defer型脚本程序段中包括任何立即执行脚本要使用的全局变量或者函数。

<html>
<body >
<script defer>
function init() {
	var _table, _tbody, tr, td, text, maxRow, maxCol;
	var docBody = 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 (var i=0; i<maxRow; i++) {
		tr = _doc.createElement("tr");
		for(var j = 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>

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值