首页 > 代码库 > 高性能javascript(第三章 DOM编程)
高性能javascript(第三章 DOM编程)
1、浏览器之DOM
浏览器通常要求 DOM 实现和 JavaScript 实现保持相互独立
这对性能意味着什么呢?简单说来,两个独立的部分以功能接口连接就会带来性能损耗。一个很形象的 比喻是把 DOM 看成一个岛屿,把 JavaScript(ECMAScript)看成另一个岛屿,两者之间以一座收费桥连接
每次 ECMAScript 需要访问DOM 时,你需要过桥,交一次“过桥费”。你操作 DOM 次数越多,费用就越高。一般的建议是尽量减少过桥次数,努力停留在 ECMAScript 岛上。本章将对此问题给出详细解答,告诉你应该关注什么地方, 以提高用户交互速度。
2、DOM访问和修改
访问一个 DOM 元素的代价就是交一次“过桥费”。修改元素的费用可能更贵,因为它经常导致浏览器重新计算页面的几何变化。
function innerHTMLLoop() {for (var count = 0; count < 15000; count++) {document.getElementById(‘here‘).innerHTML += ‘a‘;
上面这段代码对dom访问了两次一次读取它,另外一次写入它
改进:
function innerHTMLLoop2() { var content = ‘‘; for (var count = 0; count < 15000; count++) { content += ‘a‘; } document.getElementById(‘here‘).innerHTML += content;}
一般经验法则是:轻轻地触摸 DOM,并尽量保持在 ECMAScript 范围内。
3、innerHTML 与 DOM 方法比较
使用innerHTML创建1000*5的表:
使用 DOM 方法创建同样的表:
其实这两种方式在新版的浏览器上差别不是很大
4、使用节点克隆,一般会快2%~10%;
5、HTML集合:
死循序:
var alldivs = document.getElementsByTagName_r(‘div‘); for (var i = 0; i < alldivs.length; i++) { document.body.appendChild(document.createElement(‘div‘)) }
看下面三种不同的访问方式:
function toArray(coll) { for (var i = 0, a = [], len = coll.length; i < len; i++) { a[i] = coll[i]; } return a; } var coll = document.getElementsByTagName_r(‘div‘); var arr = toArray(coll);//slower function loopCollection() { for (var count = 0; count < coll.length; count++) { } }// faster function loopCopiedArray() { for (var count = 0; count < arr.length; count++) { } }//缓存 跟loopCopiedArray差不多一样快 function loopCacheLengthCollection() { var coll = document.getElementsByTagName_r(‘div‘), len = coll.length; for (var count = 0; count < len; count++) { } }
6、访问集合元素时使用局部变量(缓存):
三种缓存方式的比较如下代码:
// slowfunction collectionGlobal() { var coll = document.getElementsByTagName_r(‘div‘), len = coll.length, name = ‘‘; for (var count = 0; count < len; count++) { name = document.getElementsByTagName_r(‘div‘)[count].nodeName; name = document.getElementsByTagName_r(‘div‘)[count].nodeType; name = document.getElementsByTagName_r(‘div‘)[count].tagName; } return name;};// fasterfunction collectionLocal() { var coll = document.getElementsByTagName_r(‘div‘), len = coll.length, name = ‘‘; for (var count = 0; count < len; count++) { name = coll[count].nodeName; name = coll[count].nodeType; name = coll[count].tagName; } return name;};// fastestfunction collectionNodesLocal() { var coll = document.getElementsByTagName_r(‘div‘), len = coll.length, name = ‘‘, el = null; for (var count = 0; count < len; count++) { el = coll[count]; name = el.nodeName; name = el.nodeType; name = el.tagName; } return name;};
//nextSibling方式function testNextSibling() { var el = document.getElementById(‘mydiv‘), ch = el.firstChild, name = ‘‘; do { name = ch.nodeName; } while (ch = ch.nextSibling); return name;};// nodeName方式function testChildNodes() { var el = document.getElementById(‘mydiv‘), ch = el.childNodes, len = ch.length, name = ‘‘; for (var count = 0; count < len; count++) { name = ch[count].nodeName; } return name;};
8、只表示元素节点的DOM属性(HTML标签)和表示所有节点的属性如下表:一般情况下只表示元素节点的dom要快些
9、选择器API
var elements = document.querySelectorAll(‘#menu a‘);
elements 的值将包含一个引用列表,指向那些具有 id="menu"属性的元素。函数 querySelectorAll()接收 一个 CSS 选择器字符串参数并返回一个 NodeList——由符合条件的节点构成的类数组对象。此函数不返回 HTML 集合,所以返回的节点不呈现文档的“存在性结构”。
这就避免了本章前面提到的 HTML 集合所固有 的性能问题(以及潜在的逻辑问题)。
var elements = document.getElementById(‘menu‘).getElementsByTagName_r(‘a‘);
这种情况下 elements 将是一个 HTML 集合,所以你还需要将它拷贝到一个数组中,如果你想得到与 querySelectorAll()同样的返回值类型的话。
对于下面两段代码,使用选择器API比对手快2~6倍
//使用选择器APIvar errs = document.querySelectorAll(‘div.warning, div.notice‘);//使用一般方法var errs = [], divs = document.getElementsByTagName_r(‘div‘), classname = ‘‘;for (var i = 0, len = divs.length; i < len; i++) { classname = divs[i].className; if (classname === ‘notice‘ || classname === ‘warning‘) { errs.push(divs[i]); }}
10、重绘和重排版
当浏览器下载完所有页面 HTML 标记,JavaScript,CSS,图片之后,它解析文件并创建两个内部数据结构:
一棵DOM树:表示页面结构
一棵渲染树:表示 DOM 节点如何显示
渲染树中为每个需要显示的 DOM 树节点存放至少一个节点(隐藏 DOM 元素在渲染树中没有对应节点)。渲染树上的节点称为“框”或者“盒”,符合 CSS 模型的定义,将页面元素看作一个具有填充、边距、 边框和位置的盒。
一旦 DOM 树和渲染树构造完毕,浏览器就可以显示(绘制)页面上的元素了。
当 DOM 改变影响到元素的几何属性(宽和高)——例如改变了边框宽度或在段落中添加文字,将发生一系列后续动作——浏览器需要重新计算元素的几何属性,而且其他元素的几何属性和位置也会因此改变受到影响。
浏览器使渲染树上受到影响的部分失效,然后重构渲染树。这个过程被称作重排版。重排版完成时,浏览器在一个重绘进程中重新绘制屏幕上受影响的部分。
不是所有的 DOM 改变都会影响几何属性。例如,改变一个元素的背景颜色不会影响它的宽度或高度。 在这种情况下,只需要重绘(不需要重排版),因为元素的布局没有改变。
高性能javascript(第三章 DOM编程)