JS对DOM的优化小结

1 篇文章 0 订阅
所有的HTML解析成DOM节点才能操作
1. 什么是dom?



2.优化DOM
优化DOM的本质其实就是减少DOM树的重绘与重流。 
什么是 重流||重绘?

通常页面在加载你的js,css,img等文件时,引擎会对文件加以解析,最终生成两棵树,渲染树和DOM树。DOM树中的 需要显示节点 在渲染数中都会存在(即display:none的不存在)。DOM是页面显示的HTML结构,渲染树是DOM显示的真实结点 ,也可以理解为渲染树其实就是css文件指定节点的样式表。
  • 当渲染树和DOM树都已经完成的时候,则开始将页面显示到桌面上。
  • 如果改变页面的DOM结构,渲染树和DOM树都发生改变
  • 浏览器会重新计算出渲染树的这一过程叫重流(重排)
  • 将更新后的结构重新渲染到页面的这一过程叫重绘
整个流程就是这个图
这里写图片描述
重流重绘发生的情况

重流发生情况:
  • 添加或删除可见DOM元素
  • 元素位置改变
  • 元素尺寸改变
  • 元素内容改变
  • 页面渲染初始化
  • 浏览器窗口尺寸改变
总的来说,改变页面布局、大小,都会发生重流的情况。那重绘什么时候发生呢?发生重流就一定会发生重绘,但是重绘的范围大一点。比如,当你仅仅改变字体颜色,页面背景等比较“肤浅”的操作时,是不会打扰到重排的。
优化DOM的结构,无非就是引用保存,动画优化,节点保存,更新节点等基本操 作。 
优化DOM的效果
var times=10000;
var time1 = function(){
var time = times;
while(time--){
//DOM的两个操作放在循环内
var dom = document.querySelector("#Div1");
dom.innerHTML+="a";
}
};
var time2=function(){
var time = times,
dom = document.querySelector("#Div1");
while(time--){
//DOM的一个操作放在循环内
dom.innerHTML+="a";
}
};
var time3=function(){
var time = times,
dom = document.querySelector("#Div1"),
str = "";
while(time--){
//循环内不放置DOM的操作
str +="a";
}
dom.innerHTML=str;
}
console.time(1);
//设置时间起点
time1();
console.timeEnd(1);
console.time(2);
//设置时间起点
time2();
console.timeEnd(2);
console.time(3);
//设置时间起点
time3();
console.timeEnd(3);
//测试结果为:
1: 101.868ms
2: 101.560ms
3: 13.615ms
  • 过多的DOM操作时,一定要保存获取到的DOM节点及所有涉及DOM相关的操作。
  • 合并DOM的操作
div .style.color = "red" ; div .style.border = "1px solid red" ;
浏览器会聪明的将两次重排合并到一次发生,节省资源。 
但是如果中间访问了其他属性等,那么就会再次重排。
div.style.color= "red" ; //积累一次重排记录 var height = div.clientHeight; //触发重排 div.style.border= "1px solid red" ; //再次积累一次重排
所以如果更改DOM结构最好一次性写完

1. 批量增加 Dom
尽量使用修改 innerHTML 的方式而不是用 appendChild 的方式 ; 因为使用 innerHTML 开销更小 , 速度更快 , 同时也更加内存安全 .
有一点需要注意的是 , 用 innerHTML 方式添加时 , 一定不要在循环中使用 innerHTML += 的方式添加 , 这样反而会使速度减慢 ; 而是应该中间用 array 缓存起来 , 循环结束后调用 xx.innerHTML = array.join(‘’); 的方式 , 或者至少保存到 string 中再插到 innerHTML 中 .
针对用户列表一块采用这种方式优化后 , 加载速度提升一倍 .
2. 单个增加 Dom
这里是指要将新节点加载到一个内容不断变化的节点的情形 , 对于内容稳定的节点来说 , 随便怎么加都没有问题 .但是对于有动态内容的节点来说 , 为其添加子节点尽量使用 dom append 的方式 .
这是因为 ,dom append 不会影响到其他的节点 ; 而如果修改 innerHTML 属性的话 , 该父节点的所有子节点都会从 dom 树中剥离 , 再根据新的 innerHTML 值来重绘子节点 dom 树 ; 所有注册到原来子节点的事件也会失效 .
综上 , 如果在一个有动态内容的节点上 出现了 innerHTML += 的代码 , 就该考虑是否有问题了 .
3. 创建 Dom 节点
用 createElement 方式创建一个 dom 节点 , 有一个很重要的细节 : 在执行完 createElement 代码之后 , 应该马上append 到 dom 树中 ; 否则 , 如果在将这个孤立节点加载到 dom 树之前所做的赋值它的属性和 innerHTML 的操作都会引发该 dom 片段内存无法回收的问题 . 这个不起眼细节 , 一旦遇到大量 dom 增删操作 , 就会引发内存的灾难 .
4. 删除 Dom 节点
删除 dom 节点之前 , 一定要删除注册在该节点上的事件 , 不管是用 observe 方式还是用 attachEvent 方式注册的事件 , 否则将会产生无法回收的内存 .
另 , 在 removeChild 和 innerHTML=’’ 二者之间 , 尽量选择后者 . 因为在 sIEve( 内存泄露监测工具 ) 中监测的结果是用 removeChild 无法有效地释放 dom 节点 .
5. 创建事件监听
现有的 js 库都采用 observe 方式来创建事件监听 , 其实现上隔离了 dom 对象和事件处理函数之间的循环引用 ,所以应该尽量采用这种方式来创建事件监听 .
6. 监听动态元素
Dom 事件默认是向上冒泡的 , 发生在子节点中的事件 , 可以由父节点来处理 . Event 的 target/srcElement 仍是产生事件的最深层子节点 . 这样 , 对于内容动态增加并且子节点都需要相同的事件处理函数的情况 , 可以把事件注册上提到父节点上 , 这样就不需要为每个子节点注册事件监听了 .
同时 , 这样做也避免了产生无法回收的内存 . 即使是用 Prototype 的 observe 方式注册事件并在删除节点前调用stopObserving, 也会产生出少量无法回收的内存 , 所以应该尽量少的为 dom 节点注册事件监听 .
所以 , 当代码中出现在循环里注册事件时 , 也是我们该考虑事件上提机制的时候了 .
****************************************************************************************************************************
7. HTML 提纯
HTML 提纯体现的是一种各负其责的思想 . HTML 只用来显示 , 尽量不出现和显示无关的属性 . 比如 onclick 事件 , 比如自定义的对象属性 .
事件可以用前面的方法避免 , 对象属性指的是这样的一种情景 : 通常情况下 , 动态增加的内容都是有个对象和它对应 ,比如聊天室的用户列表 , 每个显示用户的 dom 节点都有一个 user 对象和它对应 , 这样在 html 中 , 应该仅保留一个 id属性和 user 对象对应 , 而其他的信息 , 则应通过 user 对象去获取 .
    基于 PrototypeJS ,写了一个 Dom 生成器,以提供简单高效的 HTML 操作接口的语法和语义优化 :
1. 使用类似 JSONML 的格式 (HTML in JSON) 描述 DOM 结构,以补充 HTML 转义字符串的表达形式,规范格式中可支持以下 HTML 语义:
l 标签名
l 属性 ( 标识符 , 类名 , 事件名 , 内联样式等 )
注:事件名上注册的侦听函数将自动通过 Event.observe 方式添加
l 嵌套标签
格式规范可简单描述为:
{
tag:string,    // 元素的标记名,如果没有,默认为   div
children|cn: string|Array|json,    // 子结点对应的   json   数组或字节点的   html   或单个   json
html:string,    // 对应的   html   ,如果有   cn     children   属性就忽略
style:function|string|json,    // 元素的样式,可以是函数,字符串,   json   对象
cls:string,    // 元素的   class   属性的值
x:y    // x   表示其他名字,   y   表示变量值、非空字符串
onXXX: function // 以   on   为首的属性是事件侦听器
}
一个具体的例子为:
var   list = DomBuilder.append(   'my-div'   , {
tag :  'ul'   ,
cls :  'my-list'   ,
children : [
{
tag :  'li'   ,
id :  'item1'   ,
html :  'List Item 1'   ,
onclick :  function   () {
alert(   'List Item1 Clicked'   )
}
},
{
tag :  'li'   ,
id :  'item2'   ,
html :  'List Item 2'   ,
customattr :  'customValue'
}]
});
2. 在实际修改 DOM 结构时,提供两种方式可供调用者选择,以便应需使用:
l W3C 标准 DOM 操作 (appendChild, removeChild) 方式
l 使用 innerHTML,insertAdjacentHTML 等的实效模式
一个具体例子为:
DomBuilder.useDom =  true   ; //   显示申明使用   W3C Dom   操作方式
3. 另外提供 DOM 节点的有效回收方法
var   abandoned = DomBuilder.destroy(list); //     DOM   节点从文档中移除
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值