优化你的DOM
来自: https://segmentfault.com/a/1190000004451493
优化DOM的本质其实就是减少DOM树的重流与重绘。对于重流和重绘的理解,详见《前端知识普及之HTML》
优化DOM的结构,无非就是引用保存,动画优化,节点保存,更新节点等基本操作。
曾记得,以前在网上翻阅HTML的时候,很多人都会不约而同(儿童)的说道,获取到DOM节点后一定要记得保存。否则,下场很难看的。
为什么~为什么~为什么~
我们都知道所谓的js其实是DOM,BOM,ECMA结合的产物。 本来我js挺快的,你非要去的DOM说说话。 那怎么办,只有敲敲门,等DOM来回应你呀~ 但是,这个等待时间灰常长。
看个demo吧.
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相关的操作。
比如. style,innerHTML等属性。
而这样做的原理就是减少重流和重绘的次数。
重流重绘发生的情况
那重流和重绘通常什么情况下会发生呢?重流发生情况:
-
添加或者删除可见的DOM元素
-
元素位置改变
-
元素尺寸改变
-
元素内容改变(例如:一个文本被另一个不同尺寸的图片替代)
-
页面渲染初始化(这个无法避免)
-
浏览器窗口尺寸改变
总的来说,就是改变页面的布局,大小,都会发生重流的情况。那重绘什么时候会发生呢? 发生重流就一定会发生重绘,但是,重绘的范围比重流稍微大了一点。比如,当你仅仅改变字体颜色,页面背景颜色等 比较"肤浅"的操作时,是不会打扰到重排的。
合并DOM的操作
当我们这样操作时:
div.style.color="red"; div.style.border="1px solid red";
浏览器会很聪明的将两次重排合并到一次发生,节省资源。 其实函数节流的思想和这个有异曲同工的妙处
var throttle = (function(){ var time; return function(fn,context){ clearTimeout(time); //进行函数的节流 time = setTimeout(fn.bind(context),200); } })()
这个技巧通常用在你调整浏览器大小的时候。但是,如果中间,你访问了offsetTop,clientTop等 立即执行属性的话。那结果你就么么哒了。
div.style.color="red"; //积累一次重排记录 var height = div.clientHeight; //触发重排 div.style.border="1px solid red"; //再次积累一次重排
这时候,浏览器已经被你玩傻了。 所以,给的一点建议就是,如果要更改DOM结构最好一次性整完,或者,要整一起整~我们上面的css修改,还可以这样
div.style.cssText="color:red;border:1px solid red"; //覆盖原来的css div.classList.add("change"); //利用class来整体改动
DOM操作的优化
DOM的操作无非就CRUD。这里简单说一下基本的API
创建节点
var div = document.createELement("div");
查找节点
var divs = document.querySelectorAll('div'); //很多个,放在数组内 var onlydiv = document.querySelector('div'); //只有一个 //以及document.getElement系列
查看节点
var html = div.innerHTML; var outer = div.outerHTML; //这两个是非常常用的 var classNames = div.classList; var className = div.className; var tagName = div.tagName; var id = div.id; var style = div.getAttribute("style"); //....
移动节点
ele.replaceChild(replace,replaced); //replace代替replaced //添加子节点 ele.appendChild(child); //删除子节点 ele.removeChild(child); //插入子节点 ele.insertBefore(newEle,referenceEle);
Ok~ 其实,上面所说的这些API只要涉及到DOM操作的都会发生重排。所以,这里是地方可以优化的.
使用fragment
当我们需要批量加入子节点的时候,就需要使用fragment这个虚拟片断,来作为一个容器.比如,我们需要在div里面添加100个p标签
var times = 100; var addP = function(){ var time = times, tag1 = document.querySelector('#tag1'); while (time--) { var p = document.createElement('p'); tag1.appendChild(p); } } var useFrag = function(){ var time = times, tag1 = document.querySelector('#tag1'), frag = document.createDocumentFragment(); while (time--) { var p = document.createElement('p'); frag.appendChild(p); } tag1.appendChild(frag); } console.time(1); addP(); console.timeEnd(1); console.time(2); useFrag(); console.timeEnd(2); //基本事件差为: 1: 1.352ms 2: 0.685ms
除了使用fragment片断,还可以使用innerHTML,outerHTML进行相关的优化操作。这里就不赘述了
UI操作的优化
这里想说的其实不多,就是学会使用absolute排版。 因为当你进行相关UI操作的时候,毫无疑问有可能不经意间,导致全屏的渲染。比如 校园二手街 的布局,当你下滑的时候,他的headerbar便会发生扩大,布局较差的情况是整版重排。(傻逼傻逼傻逼)
他这里不一样,他直接使用absolute进行布局,脱离了文档流,防止页面过度的重排,赞~
最后:
优化之路漫漫,大家好自为之~