高性能javascript總結

一、加載和執行

問題起源

多數瀏覽器使用單線程來處理用戶界面UI刷新和Javascript腳本的執行,所以同一時刻只能做一件事。javascript腳本霸道地讓頁面等待自己執行完畢。

解決辦法

html4中的規範指出<script> 可以放在文檔的head或者body中。理論上說將樣式文件和腳本文件放在head中有助於頁面的渲染和交互。

<html>
<head>
<title></title>
<script src="files.js"></script>
<script src="files1.js"></script>
<script src="files2.js"></script>
<link type="text/css" href="styles.css">
</head>
</html>

記住!瀏覽器在解析到<body>之前,不會渲染頁面的任何部分,把腳本放在頁面的頂部有將會導致明顯的延遲。

  • 因此,推薦將所有的<script>放在<body>標籤的底部,以減少對整個頁面下載的影響。

  • 減少頁面外鏈腳本文件的數量將會改善性能,可以將多個文件合併成一個,只需要用一個<script標籤。把部分文件和代碼合併成一個外鏈文件。

  • 無阻塞加載腳本,異步加載javascript文件。window的onload時間觸發後再下載腳本。

    • <script>添加defer或者async屬性,都是並行下載。
    • async在加載完後立即執行。
    • defer在頁面加載完後執行(onload觸發前),並且會按順序執行。
  • 動態腳本元素,通過document.createElement(“script”)來實現頁面不阻塞。

    • 其他瀏覽器可以通過scrip標籤接收完成時觸發一個load事件,通過監聽此事件來獲得腳本加載完成時的狀態。
    var script=document.createElement("script");
    script.onload=fucntion(){
    alert('script loaded')
    }
    
  • IE,它會觸發一個readystatechange事件<script>元素提供一個readystate屬性,在外鏈文件下載過程中,它的值會不斷變化,有5種取值。

    	var script=document.createElement("script");
    script.onreadystatechange=function(){
      if(script.readyState == 'loaded'||script.readyState == 'complete'){
         script.onreadystatechange=null;
         alert('Script loaded')
    }
    }
    
  • 通過XHR腳本注入實現不阻塞。

    • 你可以下載js代碼但不立即執行
    • 但是js文件必須與所請求的頁面處於相同的域。意味着不能從CDN中下載js文件,很少使用。
//1.創建一個xhr對象
var xhr=new XMLHttpRquest();
//2.下載js文件
xhr.open('get','files.js',true);
xhr.onreadystatechange=function(){
 if(xhr.readyState == 4){
  if(xhr.status >= 200 && xhr.status <300 || xhr.status == 300){
   	var script=document.createElement("script");
   	script.type='text/javascript';
   	script.text=xhr.responseText;//響應的數據
   	//3.代碼注入到頁面
   	document.body.appendchild(script);
}
}
};
xhr.send(null);

二、數據存取

問題起源

通過改變數據的存儲位置來獲得最佳的速寫性能。

解決

多使用局部變量。它在作用域鏈的起始位置。
javascript中有四種基本的數據存儲位置。
速度:1==2>3>4(火狐瀏覽器除外)

  1. 字面量(對象、數組、函數、正則、字符串、數字、布爾、null、undefined)
  2. 本地變量(var)
  3. 數組元素
  4. 對象成員(還要訪問原型,遍歷原型鏈。通過將第一次查找的值放在局部變量中改善)

閉包引起的性能問題

問題

Function同對象一樣,擁有可以編程訪問的屬性,和一系列不能通過代碼訪問而僅供javascript引擎存取的內部屬性。其中一個叫[[scope]]
閉包的[[scope]]屬性包含了與執行環境作用域鏈相同的對象引用,因此會產生副作用。

  • 函數在執行時,會有執行環境,會創建一個包含變量以及其他數據的活動對象,當執行完畢,函數的活動對象會銷燬。
  • 引入閉包後,函數的活動對象會[[scope]]屬性中,所以活動對象沒有辦法銷燬。
  • 閉包執行時,會創建一個執行環境,還有有一個閉包自身創建的活動對象。

解決

執行完後,將閉包設爲null
將常用的跨作用域的變量存儲在局部變量中。

三、DOM編程

修改DOM問題

用腳本對DOM操作代價很昂貴,DOM和javascript兩個相互獨立的功能通過接口彼此連接,就會消耗。兩個島嶼,相連接就需要收過橋費。那麼,如果修改dom代價更高,怎麼減少代價呢

解決

  1. 用局部變量存儲修改的內容。
  2. 減少dom訪問次數,把運算放在javascript這一端。
  3. innerHTML和document.createElement 相差無幾,如果更新一大段HTML推薦使用innerHTML。除谷歌等基於Webkit內核的瀏覽器外,一般是innerHTML效率高。

HTML集合問題

document.getElementsByName()
document.getElementsByClassName()
document.getElementsByTagName()

會產生一個HTML集合,類似數組的列表,只有length屬性,但是HTML集合一直與文檔保持連接,每次更新時,都會重複執行查詢過程,哪怕只是獲取元素個數。

解決

  1. 將集合拷貝到普通數組中a
function toArray(coll){
 for(var i=0;a=[];len=coll.length;i<len;i++){
 a[i]=coll[i];
 }
 return a;
}
  1. 訪問集合元素時使用局部變量,把len緩存在局部變量中。
  2. 使用選擇器API querySelectAll()/querySelect(), 使用css選擇器作爲參數並返回一個NodeList,返回的節點是靜態的,不會和文檔連接、

獲取DOM元素問題和解決

屬性名(元素節點) 被替代的屬性(包括其他空白、註釋節點 不推薦)
children childnodes
childElementCount childnodes.length
firstElementChid firstChild
lastElementChild lastChild
nextElementSibling nextSibling
previousElementSibling previousSibling

重排和重繪問題

重排:幾何屬性變化時,重新構造渲染樹

  1. 添加刪除DOM
  2. 元素位置變化
  3. 元素尺寸變化
  4. 內容改變
  5. 頁面渲染器初始化
  6. 瀏覽器窗口大小改變

重繪:重排完成後,重新繪製受影響的部分到頁面。

解決

避免使用獲取佈局信息的操作。例如scrollHeight等,因爲會刷新渲染隊列,可以將佈局信息存儲在局部變量中。
應該合併多次DOM和樣式的操作,一次性處理掉。

 /1.cssText屬性*/
document,getElementById('#mydiv').style.cssText='height:500px;width:200px;border-left:1px;'`
/2.*修改css的class名稱,但是需要檢查級聯樣式*/

對DOM元素進行一系列操作時

  1. 使元素脫離文檔流
  2. 對其應用多重改變
  3. 把元素帶回文檔
//demo1
//使用隱藏元素
var ul=document.getElementById('#mydiv');
ul.style.display='none';
//應用修改
修改方法
//重新顯示
ul.style.display='block';
//demo2
//在DOM之外構建並更新文檔片段,
var fragment=document.creatDocumentFragment();
應用方法
//然後再拷貝回去
document.getElementById('#mydiv').appendChild(fragment)
//demo3
//拷貝原始節點,修改副本,用新的替換舊的
var old=document.getElementById('#mydiv');
var clone=old.cloneNode(true);
應用方法
old.parentNode.replaceChild(clone,old);

使用事件委託減少事件處理器的數量。

四、循環和流程控制

循環問題

一般循環也很消耗性能,除for in外其他循環性能差不多,除非你要遍歷一個屬性未知的對象。

解決

  1. 使用局部變量保存length
  2. 減少迭代的運算量,倒序查找

流程問題

一些 if else 和switch等流程

解決

較少時 使用if else 多時使用switch
查找表,比兩者都快,將返回的結果存到數組中

遞歸問題

遞歸可以把複雜的算法變得簡單,但是終止條件不明確或者缺少終止條件會導致函數長時間運行,也會遇到瀏覽器的調用棧大小的限制。(每個瀏覽器都有調用棧的大小,例如谷歌 21837)

解決

驗證終止條件
終止條件沒有問題,那就是算法中包含太多層的遞歸。
改用迭代(優化後的循環)

五、快速響應用戶界面

瀏覽器響應頁面性能問題

再也沒有比點擊網頁毫無動靜令人沮喪了!大多數瀏覽器讓一個單線程共用於執行javascript和更新用戶頁面,每時刻只能執行一種操作。
javascript和更新用戶界面的進程通常被稱爲“瀏覽器UI線程”,任務會存在隊列中,直到進程空閒,一旦進程空閒,下一個任務就重新被放在進程中執行。

瀏覽器限制

瀏覽器限制了javascript任務的運行時間,避免某些惡意代碼能不停止的操作用戶的瀏覽器和計算機。

  • 調用棧大小限制

  • 長時間運行腳本限制:瀏覽器會記錄腳本的運行時間,並達到一定限度時終止它。火狐是10秒,IE是500萬條語句。

    • 多久算久:單個javascript操作花費總時不超過100ms

通過定時器分解解決

  • 通過定時器讓出UI線程,使得頁面得到更新。
    • setTimeout(fn,200);200表示設置200毫秒後定時器代碼加入隊列,從setTimeout調用開始算。只有當創建它的函數執行完後,定時器代碼纔可能被執行。
    • 通過定時記錄代碼的運行時間,避免將任務分解爲過於零碎的片段。
    • 同一時間只有一個定時器存在,結束後纔會創建另一個,通過這種方法使用定時器不會導致性能問題。

Web Worker解決UI線程

自javascript誕生以來,還沒有辦法在瀏覽器UI線程外運行代碼呢。web worker 引用一個接口,能使代碼運行且不佔用瀏覽器UI線程的時間。並且每個新的worker都在自己的線程中運行,不會影響瀏覽器UI和其他worker中運行的代碼。

worker的通信

通過事件接口通信。網頁代碼通過postmessage(data)方法給Worker傳數據。worker通過onmessage()接收數據。postmessage傳遞的可以是基本數據類型和object、array。

實際應用

worker適合處理純數據或者與瀏覽器UI無關長時間運行的腳本。
解析Json字符串

//頁面
var worker=new Worker('file.js');//引入在worker中運行的代碼
//5.worker傳來對象的event.data進行處理
worker.onmessage=function(event){
 var jsonData=event.data;//json字符串解析後的json對象
}
//1.頁面通過postmessage向worker中傳遞json字符串
worker.postmessage(jsonText);

/*Worker負責解析*/
//file.js內部代碼
//2.worker 用自己的onmessage處理接收的字符串,即event.data
self.onmessage=function(){
 var jsonText=event.data;
 //3.開始解析  時間長
 var jsonData=JSON.parse(jsonText);
 //4.回傳頁面
  self.postmessage(jsonData);
 

}

六、ajax的請求、發送

ajax是高性能javascript的基礎,它通過異步的方式在服務器和瀏覽器之間傳輸數據。
那麼在與服務器通信時,哪種技術最好呢,又應該用哪種數據格式呢?

請求數據技術

請求數據技術 優缺點
XHR 優:可以獲取響應頭信息和設置請求頭信息;缺點:不能跨域,服務器傳回來的數據會被當成字符串或者XML對象,處理數據很慢 。POST GET send(null)
動態腳本注入 可以跨域,不能設置請求頭,只有GET方式,腳本執行,所以響應數據要放在callback裏,不安全
Multipart XHR 允許用戶只用一個HTTP請求 就可以從服務器端向客戶端傳送多個資源,通過將js、css、圖片在服務器打包,按照雙方約定的字符串分割的長字符串傳給客戶端,用javascript處理這個字符串。缺點:獲取的資源不能被瀏覽器緩存

ajax最快的請求就是沒有請求:
在服務器端,設置HTTP頭信息以確保你的響應會被瀏覽器緩存。例如設置expires的過期時間。如果遇到版本升級,可以通過給文件增加一個版本號來解決緩存問題。
在瀏覽器端,把獲取的信息存儲到本地,從而避免再次請求。

發送數據技術

技術 優缺點
XHR GET更快 一個TCP包、post適合發送大量的數據 send(params)

數據格式

格式 優缺點
XML 及其冗長,解析耗費精力,通用性強
JSON 輕量易於解析
HTML 請求數據轉化成HTML響應在頁面,繁瑣,傳輸量大

CDN

使用內容分發網絡CDN,它負責傳遞內容給終端用戶。增強web的應用的可靠性,提升性能,通過向地理位置最近的用戶傳輸內容,減少網絡延遲。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章