做爲一個前端,不能不考慮性能問題。對於大多數前端來講,性能優化的方法可能包括如下這些:css
等等……前端
有興趣的同窗,能夠自行搜索雅虎關於前端優化的十四條規則。但這些規則當中,有多少是須要前端工程師付諸實踐的?就我來講,CDN、緩存的設置,就是不須要我去關心的(做爲一個苦逼外包,是沒有權限去生產環境操做的)也就是說,一些優化的方法,可能只是須要前端工程師知道,說得難聽點,就是應付面試,工做中遇不到。反而是一些工做中真正遇到的難題,面試中卻不多被問到(純屬我的經驗)。有些問題,同行們(包括我)可能稀裏糊塗地用正確的方式解決了,但實際上,卻並不知道本身爲何這樣作。node
以上三本書,有幸拜讀。說不上醍醐灌頂,至少是讓我認識到了差距。web
我打算把本身的收穫記錄下來,但願對你們有所幫助。文中可能出現錯誤,但願你們能儘可能用友善的語氣提出來,謝謝!(づ ̄ 3 ̄)づ面試
不只要避免去操做DOM,還要減小去訪問DOM的次數。chrome
在瀏覽器中,DOM和JS的實現,用的並非同一個「東西」。好比說,咱們最熟悉的chrome,JS引擎是V8,而DOM和渲染,靠的是WebCore庫。也就是說,DOM和JS是兩個獨立的個體。數組
把DOM和JavaScript各自想象成一個島嶼,它們之間用收費橋樑鏈接。
--《高性能JavaScript》
1.添加頁面元素,innerHTML vs DOM方法。瀏覽器
document.getElementById('test').innerHTML='<div>test</div>'; var t=document.createElement('div'); t.appendChild(document.createTextNode('test')); document.getElementById('test').appendChild(t);
以上分別使用兩種方法,向id='test'的元素中添加一個div。以前,你們可能一直被灌輸的思想是innerHTML更快一些,真的是這樣麼?緩存
還真是,至少在IE中是這樣,但在基於webkit的新版瀏覽器中,使用DOM方法會稍快一些。因此,到底使用哪種方法,仍是應該有點爭議的。我我的是喜歡innerHTML,由於用起來更簡單。性能優化
此外,當須要添加大量相同的元素時,cloneNode比直接建立元素,稍微快一點。
2.訪問元素的正確方法。
2.1 遍歷集合vs遍歷數組
當咱們使用document.getElementsByName、document.getElementsByTagName、document.getElementsByClassName、docuemnt.images等方式來獲取DOM元素時,咱們獲得的是一個HTML集合,這個集合始終與底層文檔保持鏈接,每次去獲取集合的信息時,都會重複執行一次查詢。
var divs=document.getElementByTagName('div'); for(var i=0;i<divs.length;i++){ document.body.append(document.createElement('div')) }
若是不去運行,咱們可能覺得上面的代碼會新添加幾個div元素在頁面中,但實際上,由於每次添加完一個div後,divs.length都會被更新(加一),因此,這個循環永遠不會中止。解決辦法很是簡單
var divs=document.getElementByTagName('div'); for(var i=0,len=divs.length;i<len;i++){ document.body.append(document.createElement('div')) }
另外,HTML集合並非一個數組,若是咱們須要對這個集合進行遍歷,能夠先把它拷貝進一個數組,這樣再遍歷的時候,效率更高。
function toArray(coll){ for(var i=0,a=[],len=coll.length;i<len;i++){ a[i]=coll[i] } return a; }
2.2訪問元素屬性
當遍歷一個集合時,length屬性應被緩存在循環外部,可以避免2.1中的邏輯錯誤;集合存儲在局部變量中,也可以提升效率。此外,當對同一個DOM元素的屬性進行訪問時,把這個DOM緩存成一個局部變量,是更好的選擇。
//只是作演示,真實狀況中,固然沒有這樣的需求 //最差的方式 function fo1(){ var name=''; for(var i=0;i<document.getElementsByTagName('div');i++){ name=document.getElementsByTagName('div').nodeName; name=document.getElementsByTagName('div').nodeType; } return name; } //好一點的方式 function fo2(){ var name=''; var coll=document.getElementsByTagName('div'); for(var i=0,len=coll.length;i<len;i++){ name=coll[i].nodeName; name=coll[i].nodeType; } return name; } //更好的方式 function fo3(){ var name=''; var coll=document.getElementsByTagName('div'); var ele=null; for(var i=0,len=coll.length;i<len;i++){ el=coll[i]; name=el.nodeName; name=el.nodeType; } }
3.選擇器
前面已經提到,document.getElementsByName、document.getElementsByTagName、document.getElementsByClassName、docuemnt.images等方式,獲取到的是HTML集合,效率低下;而querySelector以及querySelectorAll與之相比,獲得的是一個NodeList,它是一個類數組對象,不會帶來HTML集合的問題。並且,這個API在獲取元素時,更加方便。惟一的問題,是要考慮目標瀏覽器是否提供支持。
4.重繪和重排
4.1什麼時候重繪、重排?
重繪並不必定致使重排,好比修改某個元素的顏色,只會致使重繪;而重排以後,瀏覽器須要從新繪製受重排影響的部分。致使重排的緣由有:
由於重排和重繪的操做十分昂貴,瀏覽器會經過隊列化修改並批量執行的方式,來進行優化(個人理解是,瀏覽器經過隊列化和批量執行的方式,減小了重繪的次數)。好比:
//這段代碼,並不會去重繪三次 var bodyStyle=document.body.style; bodyStyle.color='red'; bodyStyle.color='black'; bodyStyle.color='green';
獲取佈局的操做,會致使隊列刷新,瀏覽器的優化效果也就沒有了。要避免在佈局信息改變時,獲取下列屬性:
4.2 最小化重排、重繪的建議
建議:不要再修改佈局信息的時候,去查詢佈局信息
var computed; var tmp=''; var bodyStyle=document.body.style; if(document.body.currentStyle){ computed=document.body.currentStyle }else{ computed=document.defaultView.getComputedStyle(document.body,'') } //bad bodyStyle.color='red'; tmp=computed.backgroundColor; bodyStyle.color='green'; tmp=computed.backgroundImage; //good bodyStyle.color='red'; bodyStyle.color='green'; tmp=computed.backgroundColor; tmp=computed.backgroundImage;
修改一個元素的多個style時,一次性修改,而不是屢次(雖然屢次修改,通過現代瀏覽器的優化,也只會致使一次重排,但在老舊的瀏覽器中,仍然會致使屢次)。建議:能用css的class解決的,就儘可能不用內聯樣式。
:hover會下降響應速度,在處理很大的列表時,避免使用。
5事件委託
每綁定一個事件處理器,都是有代價的。若是有大量的元素須要綁定時間,嘗試使用事件委託。分三步
document.querySelector('#nav').onclick=function (e) { if (e.target.nodeName=='A'){ foo(); }else{ foo2() } }
總結: