摘要:優化策略跟上面的大同小異,就是用局部變量緩存集合以及集合的長度,我就不進行實際測試了。例如錯誤的做法使用修改來進行優化如果需要動態修改,那么就使用批量處理操作并且讓元素脫離文檔流,等操作結束后再放回文檔流中。
上篇我介紹了Javascript標識符查找方面的優化,可以看出在這方面的優化給性能帶來的提升并不明顯,甚至可以說基本沒有影響。但是,我今天要分享的是前端Javascript優化的一個大頭。眾所周知,在瀏覽器端Javascript中DOM操作相比普通Javascript代碼來說是比較耗時的,所以在DOM優化上下功夫可以收到相當可觀的性能優化。下面我將分享幾個DOM方面的性能優化策略。
耗時的DOM操作瀏覽器中的Javascript可以分為兩個部分:ECMAScript和DOM API。而相比原生的ECMAScript來說,DOM API會耗時很多。我們可以把這兩部分想象成兩個通過橋梁連接的小島,在ECMAScript小島上進行的操作運行速度比在DOM小島上面的操作要快很多,每次在進行DOM操作的時候你都需要從ECMAScript這個小島通過這個橋梁到達DOM小島上然后在上面進行耗時的操作。所以大量的DOM操作就會降低性能。
大家先看看下面這個例子:
//優化前 var start = new Date().getTime() ; for(var i = 0 ; i < length ; i ++){ document.getElementById("test").innerHTML += "a" ; } console.log("Before:" + (new Date().getTime() - start)) ; //優化后 start = new Date().getTime() ; var content = "" ; for(var i = 0 ; i < length ; i ++){ content += "a" ; } document.getElementById("test").innerHTML += content ; console.log("After:" + (new Date().getTime() - start)) ;
從運行結果來看,可以說差距那是相當明顯啊:
優化前的代碼每一次循環都進行了DOM操作,而優化之后,只在最后一步進行了DOM操作,這就是DOM優化的力量啊。所以,我們應該在操作的時候盡量避免對DOM的操作,能少操作DOM就少操作。按照上面的比喻就好比是,我們通過橋梁從ECMAScript小島到達DOM小島,然后找出需要進行操作的元素,把它再帶回到ECMAScript小島進行操作,通過這個方式,可以加快操作的速度,我們應該盡可能多的把元素帶回到ECMAScript小島進行操作。
innerHTML還是createElement在頁面上動態添加結點一般有兩個方法:innerHTML和createElement方法。這兩個方法在性能上也有一點差別,具體差別在哪兒呢?上代碼:
var start = new Date().getTime() ; var content = "" ; for(var i = 0 ; i < 1000 ; i ++){ content += "" ; } content += "" ; document.getElementById("test").innerHTML += content ; console.log("innerHTML:" + (new Date().getTime() - start)) ; document.getElementById("test").innerHTML = "" ; start = new Date().getTime() ; //為了避免直接往test節點上面添加節點引起的頁面重畫,所以使用一個div節點來存儲添加的節點,最后把div添加到頁面中 var div = document.createElement("div") ; for(var i = 0 ; i < 1000 ; i ++){ div.appendChild(document.createElement("div")) ; } document.getElementById("test").appendChild(div) ; console.log("createElement:" + (new Date().getTime() - start)) ;
這段代碼在不同瀏覽器上的運行結果是不一樣的:
在Chrome上createElement比innerHTML快,而在Firefoxhe和IE上結果則相反,從結果上看似乎是innerHTML以2:1贏了,可是我還是建議大家使用createElement,我把上面的代碼改成下面這樣:
var start = new Date().getTime() ; var test = document.getElementById("test") ; for(var i = 0 ; i < 1000 ; i ++){ test.innerHTML += "" ; } console.log("innerHTML:" + (new Date().getTime() - start)) ; document.getElementById("test").innerHTML = "" ; start = new Date().getTime() ; for(var i = 0 ; i < 1000 ; i ++){ test.appendChild(document.createElement("div")) ; } console.log("createElement:" + (new Date().getTime() - start)) ;
上面這段代碼的運行結果
可以看出來innerHTML和createElement差很多。為了測試我用了比較大的數據1000,在實際開發中一般不會出現這種情況,所以性能上的差異也就不會那么明顯,但是除了考慮性能問題以外,我們還應該考慮代碼的可讀性以及可維護下方面的問題,而考慮到這些的話,我個人還是比較推薦使用createElement,如果大家有什么別的看法,歡迎一起討論。
HTMLCollectionHTMLCollection是若干個DOM節點的集合,它具有數組的一些特性,比如length屬性、通過下標訪問,但是它并不是數組,它沒有push和slice方法。在DOM操作中我們經常會用到HTMLCollection,下面的方法都會返回HTMLCollection:
getElementsByName
getElementsByTagName
getElementsByClassName
document.forms
document.images
document.links
還有一些別的方法和屬性會返回HTMLCollection,在這里就不一一列舉了。如何處理它們也是影響性能的一個方面。優化策略跟上面的大同小異,就是用局部變量緩存集合以及集合的長度,我就不進行實際測試了。HTMLCollection還有一個很重要的特性就是它是根據頁面的情況動態更新的,如果你更新的頁面那么它的內容也會發生變化。比如下面這段代碼:
var divs = document.getElementsByTagName("div") ; for(var i = 0 ; i < divs.length ; i ++){ document.body.appendChild(document.createElement("div")) ; }
這段代碼的原意是向body中添加多一倍的div節點,但是真正的運行會導致死循環,這就是因為divs是動態更新的,每次向body中添加div節點都會使length屬性發生變化也就是加1,所以這個循環會一直執行下去,在開發的時候應該注意這個問題。一個理想的辦法就是緩存divs的長度,這樣就不會引起死循環了。
節點篩選如果需要得到某個節點的所以孩子節點,我們可能會用到childNodes屬性;得到第一個孩子,我們可能會用到firstChild;得到下一個兄弟節點,我們可能會用到nextSibling。但是這些屬性都存在一些問題就是它們會把一些空格和空行也當作孩子節點返回給我們,而這些經常不是我們所想要的,如果使用這些屬性那么我們就需要對它們進行篩選,這樣勢必會影響效率。所以我們應該用別的屬性來替代這些,看下表:
表格左邊的是推薦的屬性,它們只會返回Element節點。不過并不是所有瀏覽器都支持,所以在使用之前我們需要先判斷一下。
使用選擇器方法替代傳統方法現代瀏覽器給我們提供了另外一種方法在獲取我們需要的節點,這個方法是querySelectorAll和querySelector。它們通過CSS選擇器作為參數,返回滿足條件的節點。querySelectorAll方法返回滿足條件的所有節點而querySelector返回滿足條件的第一個節點。使用這兩個方法來替代我們以前經常用的getElementById,getElementsByTagName等方法也是提高性能的一個途徑。不過還是老問題,并不是所有瀏覽器都支持這兩個方法,所有還是先做個判斷吧。
Reflow 和 Repaint首先,Repaint是指頁面上的元素的外觀發生了改變但是不影響布局的情況下引起的瀏覽器重新繪畫元素外觀的行為,比如修改color,background-color等屬性。Reflow是指頁面上的元素的大小布局發生的變化從而引起瀏覽器對頁面其他元素位置大小進行重新計算并且布局的行為。Reflow所導致的性能消耗遠比Repaint大,所以我們下面重點討論Reflow情況下的優化策略。
在討論Reflow之前先簡單的看一下瀏覽器加載頁面的過程。如下圖:
瀏覽器在收到HTML文檔之后對其進行解析,解析過程分為兩個部分DOM文檔的解析和CSS樣式的解析。解析DOM文檔生成一個DOM樹,DOM樹和解析出來的CSS樣式組合生成一個渲染樹,最后瀏覽器根據這個渲染樹進行頁面的排版和繪畫。而最后這一步就是會涉及到Reflow和Repaint。
以下這幾個行為會引起頁面的Reflow或Repaint:
添加,刪除,更新DOM節點
隱藏/顯示DOM節點(display:none或visibility:hidden)
修改樣式
改變窗口大小,滾動頁面
其實瀏覽器在這方面已經幫我們做了一些優化了,對于每個觸發Reflow的行為瀏覽器并不會馬上就觸發,而是把它們保存在一個隊列中,當到達一定數量的時候再進行批量的Reflow,這樣就不需要每次都進行Reflow。但是,我們的一些行為會影響到瀏覽器的優化,使得Reflow馬上觸發。當我們請求下面這些屬性的時候發生這種現象:
offsetTop, offsetLeft, offsetWidth, offsetHeight
scrollTop/Left/Width/Height
clientTop/Left/Width/Height
getComputedStyle(), or currentStyle(IE)
每當我們請求這些屬性時,瀏覽器為了返回實時的情況就必須馬上進行Reflow以計算出我們所需要的屬性。所以我們應該盡量少的使用這些屬性。
從上面可以發現,基于所有DOM操作都會引起Reflow或Repaint,所以盡可能避免頁面的Reflow或Repaint可以很好的提高DOM性能。那么該怎么做才能最好的避免或最小化Reflow呢?下面有幾個有用的建議:
1.不要逐一修改樣式,而改為通過修改className來批量改變樣式,如果樣式需要動態計算,那么也要使用cssText屬性來批量添加樣式。例如:
// 錯誤的做法 var left = 10, top = 10; el.style.left = left + "px"; el.style.top = top + "px"; // 使用修改className來進行優化 el.className += " theclassname"; // 如果需要動態修改css,那么就使用cssText el.style.cssText += "; left: " + left + "px; top: " + top + "px;";
2.批量處理DOM操作并且讓元素脫離文檔流,等操作結束后再放回文檔流中。有以下幾種辦法:
使用display:none隱藏element,然后進行操作,最后再顯示出來
使用documentFragment ,把新增的節點放在documentFragment中,最后再把documentFragment放到DOM中,因為把documentFragment放到DOM中,它只會把它的孩子節點放到DOM中,就好像documentFragment不存在。
通過cloneNode復制節點,然后離線進行操作,最后再替換DOM中的節點。
3.盡量少的訪問會引起馬上Reflow的屬性,使用局部變量來緩存這些屬性,比如:
var left = el.offsetLeft, top = el.offsetTop esty = el.style; for(big; loop; here) { left += 10; top += 10; esty.left = left + "px"; esty.top = top + "px"; }
4.對于需要動畫的元素,盡量讓它脫離文檔流,這樣就能盡量引起盡量小的Reflow
5.盡量少使用table布局
事件代理事件代理我想這個大家應該都知道了。越多的事件綁定頁面就加載越慢并且占用更多內存,同時綁定太多事件也會使得代碼的可讀性降低。使用事件代理的方法原理就是把事件綁定到元素的父節點,然后在處理函數中判斷target,根據不同的target執行不同的邏輯。這樣能很大程度的減少綁定是事件數量并且提高代碼的簡潔度。
總結看了這么多其實總結起來還是比較簡單的,在進行DOM操作的時候盡量把DOM操作轉換為本地的Javascript操作,使用時先緩存一些DOM元素或者屬性,緩存長度。在需要進行大量DOM操作的時候,先讓元素脫離文檔,等操作結束再把元素放回文檔中。優化策略還是需要在實踐中不斷嘗試,不斷摸索,找出最優的解決方案。
最近準備畢設沒什么時間更新博客,后面盡量安排好時間做到一周一篇,前端優化Javascript篇未完待續。。。
原文地址:
http://lakb248.github.io/2014/06/13/optimization_of_front-end--javascript(4optimization_of_dom)/
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/87545.html
摘要:從本篇博客開始,我會跟大家分享下我關于前端優化方面的學習,由于時間原因每篇博客只能分享一小點內容,一點點深入前端優化的細節。在前端優化這個問題上,最被大家熟知的應該就是雅虎前端優化條軍規以及雅虎前端優化條規則。 從本篇博客開始,我會跟大家分享下我關于前端優化方面的學習,由于時間原因每篇博客只能分享一小點內容,一點點深入前端優化的細節。 做過前端的人都知道,前端優化是一個永遠都不會...
摘要:特意對前端學習資源做一個匯總,方便自己學習查閱參考,和好友們共同進步。 特意對前端學習資源做一個匯總,方便自己學習查閱參考,和好友們共同進步。 本以為自己收藏的站點多,可以很快搞定,沒想到一入匯總深似海。還有很多不足&遺漏的地方,歡迎補充。有錯誤的地方,還請斧正... 托管: welcome to git,歡迎交流,感謝star 有好友反應和斧正,會及時更新,平時業務工作時也會不定期更...
摘要:前端性能優化總結資源優化緩存最好的資源優化就是不加載資源。緩存主要分為強制緩存和協商緩存。的值為服務端返回的數據到期時間。的使用教程為了保證正常的,有些渲染性能優化還是有必要的。 前端性能優化總結 資源優化 緩存 最好的資源優化就是不加載資源。緩存也是最見效的優化手段。說實話,雖然說客戶端緩存發生在瀏覽器端,但緩存主要還是服務端來控制,與我們前端關系并不是很大。但還是有必要了解一下。 ...
摘要:前端性能優化總結資源優化緩存最好的資源優化就是不加載資源。緩存主要分為強制緩存和協商緩存。的值為服務端返回的數據到期時間。的使用教程為了保證正常的,有些渲染性能優化還是有必要的。 前端性能優化總結 資源優化 緩存 最好的資源優化就是不加載資源。緩存也是最見效的優化手段。說實話,雖然說客戶端緩存發生在瀏覽器端,但緩存主要還是服務端來控制,與我們前端關系并不是很大。但還是有必要了解一下。 ...
閱讀 3258·2021-09-22 15:58
閱讀 1722·2019-08-30 14:17
閱讀 1726·2019-08-28 18:05
閱讀 1511·2019-08-26 13:33
閱讀 689·2019-08-26 12:20
閱讀 614·2019-08-26 12:18
閱讀 3195·2019-08-26 11:59
閱讀 1411·2019-08-26 10:36