優化DOM的本質其實就是減小DOM樹的重流與重繪。對於重流和重繪的理解,詳見《前端知識普及之HTML》
優化DOM的結構,無非就是引用保存,動畫優化,節點保存,更新節點等基本操做。
曾記得,之前在網上翻閱HTML的時候,不少人都會不約而同(兒童)的說道,獲取到DOM節點後必定要記得保存。不然,下場很難看的。
爲何~爲何~爲何~
咱們都知道所謂的js實際上是DOM,BOM,ECMA結合的產物。 原本我js挺快的,你非要去的DOM說說話。 那怎麼辦,只有敲敲門,等DOM來回應你呀~ 可是,這個等待時間灰常長。
看個demo吧.css
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等屬性。
而這樣作的原理就是減小重流和重繪的次數。html
那重流和重繪一般什麼狀況下會發生呢?
重流發生狀況:前端
添加或者刪除可見的DOM元素segmentfault
元素位置改變數組
元素尺寸改變瀏覽器
元素內容改變(例如:一個文本被另外一個不一樣尺寸的圖片替代)app
頁面渲染初始化(這個沒法避免)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的操做無非就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這個虛擬片段,來做爲一個容器.
好比,咱們須要在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進行相關的優化操做。這裏就不贅述了
這裏想說的其實很少,就是學會使用absolute排版。 由於當你進行相關UI操做的時候,毫無疑問有可能不經意間,致使全屏的渲染。好比校園二手街的佈局,當你下滑的時候,他的headerbar便會發生擴大,佈局較差的狀況是整版重排。(傻逼傻逼傻逼)他這裏不同,他直接使用absolute進行佈局,脫離了文檔流,防止頁面過分的重排,贊~最後:優化之路漫漫,你們好自爲之~