寫在前面:javascript
你們都知道DOM的操做很昂貴。 css
而後貴在什麼地方呢? html
1、訪問DOM元素前端
2、修改DOM引發的重繪重排java
1、訪問DOM 數組
像書上的比喻:把DOM和JavaScript(這裏指ECMScript)各自想象爲一個島嶼,它們之間用收費橋樑鏈接,ECMAScript每次訪問DOM,都要途徑這座橋,並交納「過橋費」,訪問DOM的次數越多,費用也就越高。所以,推薦的作法是儘可能減小過橋的次數,努力待在ECMAScript島上。咱們不可能不用DOM的接口,那麼,怎樣才能提升程序的效率?瀏覽器
// code1錯誤 console.time(1); for(var i = 0; i < times; i++) { document.getElementById('div1').innerHTML += 'a'; } console.timeEnd(1); // code2正確 console.time(2); var str = ''; for(var i = 0; i < times; i++) { str += 'a'; } document.getElementById('div2').innerHTML = str; console.timeEnd(2);
////////////////////////
html集合相似數組,可是跟數組仍是不同的。如: document.getElementsByTagName('a') 返回的html集合。這個集合是實時更新的,即後面代碼修改了DOM,會反映在這個html集合裏面。可嘗試代碼。緩存
<body> <ul id='fruit'> <li> apple </li> <li> orange </li> <li> banana </li> </ul> </body> <script type="text/javascript"> var lis = document.getElementsByTagName('li'); var peach = document.createElement('li'); peach.innerHTML = 'peach'; document.getElementById('fruit').appendChild(peach); console.log(lis.length); // 4 </script>
正由於這個緣由:html集合,讀取 length 屬性比數組消耗大多了。數據結構
要解決這個問題並不難,在遍歷DOM集合的時候,緩存length就行了。不要每次使用就獲取,主要體如今for循環中(你應該知道,for循環中,每一次都會執行判讀語句,讀取length)app
console.time(0); var lis0 = document.getElementsByTagName('li'); var str0 = ''; for(var i = 0; i < lis0.length; i++) { str0 += lis0[i].innerHTML; } console.timeEnd(0); console.time(1); var lis1 = document.getElementsByTagName('li'); var str1 = ''; for(var i = 0, len = lis1.length; i < len; i++) { str1 += lis1[i].innerHTML; } console.timeEnd(1);
2、重繪重排
1.什麼是重繪重排?
瀏覽器下載完頁面中的全部組件——HTML標記、JavaScript、CSS、圖片以後會解析生成兩個內部數據結構——DOM樹
和渲染樹
。
在文檔初次加載時,瀏覽器引擎經過解析 html文檔 構建一棵DOM樹,以後根據DOM元素的幾何屬性構建一棵用於展現渲染的渲染樹。渲染樹中的節點被稱爲「幀」或「盒",符合CSS模型的定義,可理解爲(包括理解頁面元素爲一個具備大小,填充,邊距,邊框和位置的盒子)。因爲隱藏元素不須要顯示,渲染樹中並不包含DOM樹中隱藏的元素(知道這點有用)。 當渲染樹構建完成,瀏覽器把每個元素放到正確的位置上,而後再根據每個元素的其餘樣式,繪製頁面。
因爲瀏覽器的流佈局,對渲染樹的計算一般只須要遍歷一次就能夠完成。但table及其內部元素除外,它可能須要屢次計算才能肯定好其在渲染樹中節點的屬性,一般要花3倍於同等元素的時間。這也是爲何咱們要避免使用table作佈局的一個緣由。
重繪:是一個元素外觀的改變所觸發的瀏覽器行爲,例如改變visibility、outline、背景色等屬性(上面說到的其餘屬性)。瀏覽器會根據元素的新屬性從新繪製,使元素呈現新的外觀。重繪不會帶來從新佈局,並不必定伴隨重排。
重排:當DOM的變化影響了元素的幾何屬性(寬或高),瀏覽器須要從新計算元素的幾何屬性,一樣其餘元素的幾何屬性和位置也會所以受到影響。瀏覽器會使渲染樹中受到影響的部分失效,並從新構造渲染樹。這個過程稱爲重排。重排必定伴隨着重繪。
2. 觸發重排的操做:
2.1 修改DOM元素幾何屬性:
修改元素大小,位置,內容(通常只有重繪,可是內容可能致使元素大小變化)
2.2 DOM樹結構發生變化
當DOM樹的結構變化時,例如節點的增減、移動等,也會觸發重排。瀏覽器引擎佈局的過程,相似於樹的前序遍歷,是一個從上到下從左到右的過程。 一般在這個過程當中,當前元素不會再影響其前面已經遍歷過的元素。因此,若是在body最前面插入一個元素,會致使整個文檔的從新渲染,而在其後插入一個元 素,則不會影響到前面的元素。
2.4 改變瀏覽器大小
3.渲染樹變化的排隊和刷新
思考下面代碼:
1 var ele = document.getElementById('myDiv'); 2 ele.style.borderLeft = '1px'; 3 ele.style.borderRight = '2px'; 4 // var _top = ele.offsetTop; //刷新隊列 5 ele.style.padding = '5px';
三行代碼,三次修改元素的幾何屬性,瀏覽器應該發生三次重排重繪。
可是瀏覽器並不會這麼笨,它也是有作優化的。它會把三次修改「保存」起來(大多數瀏覽器經過隊列化修改並批量執行來優化重排過程,也有設置時間片斷的),一次完成!
然而,若是你在三行代碼中,如下獲取DOM佈局信息。(爲了返回最新的佈局信息,將當即執行渲染樹變化隊列的更新)
如上面被註釋的第4行,若是取消註釋會致使(2+3)、(5)兩次重排;
獲取關於DOM佈局信息的屬性:
4 應對方法:儘可能減小重繪次數、減小重排次數、縮小重排的影響範圍。
4.1 合併屢次操做,如上面的操做
ele.style.cssText = 'border-left: 1px; border-right: 2px; padding: 5px;';
4.2 將須要屢次重排的元素,position屬性設爲absolute或fixed,這樣此元素就脫離了文檔流,它的變化不會影響到其餘元素。例若有動畫效果的元素就最好設置爲絕對定位。
4.3 因爲display屬性爲none的元素不在渲染樹中,對隱藏的元素操做不會引起其餘元素的重排。若是要對一個元素進行復雜的操做時,能夠先隱藏它,操做完成後再顯示。這樣只在隱藏和顯示時觸發2次重排。可是這可能致使瀏覽器的閃爍。
4.4 在內存中屢次操做節點,完成後再添加到文檔中去(可以使用fragment元素)。例如要異步獲取表格數據,渲染到頁面。能夠先取得數據後在內存中構建整個表格的html片斷,再一次性添加到文檔中去,而不是循環添加每一行。
var fragment = document.createDocumentFragment(); // 未使用的虛擬節點,appendChild(fragment) //append的是裏面的子元素 var li = document.createElement('li'); li.innerHTML = 'apple'; fragment.appendChild(li); var li = document.createElement('li'); li.innerHTML = 'watermelon'; fragment.appendChild(li); document.getElementById('fruit').appendChild(fragment);
參考文檔: