javascript性能優化

加載和執行

一、 </body>閉合標籤以前,將全部的<script> 標籤放在頁面底部,確保在腳步執行以前頁面已經完成渲染。javascript

二、 合併腳本。下載單個 100KB 的文件將比下載 4 個 25KB 的文件更快,所以頁面標籤的<script>標籤越少,加載也就越快,響應也越迅速。不管外鏈文件或者內嵌腳本。html

三、 使用無阻塞下載 javascript 的方法,即在頁面加載完成後才加載 Javascript 代碼,如下有幾種無阻塞下載方法:java

  • 使用<script>defer 屬性(只有 IE4+和 FireFox3.5+支持)node

    <script src="file.js" defer></script>
    複製代碼
  • 使用動態建立的<script>元素來下載並執行代碼webpack

    // 經過監聽script元素接收完成事件來得到腳本加載完成時的狀態
    // 封裝標準和IE特有的實現方法函數
    function loadscript(url, callback) {
      let script = document.createElement('script');
      if (script.readyState) {
        //IE
        script.onreadystatechange = function() {
          if (script.readyState === 'loaded' || script.readyState === 'complete') {
            script.onreadystatechange = null;
            callback();
          }
        };
      } else {
        // 其餘瀏覽器
        scripy.onload = function() {
          callback();
        };
      }
      script.src = url;
      document.getElementsByTagName('head')[0].appendchild(script);
    }
    // 使用上述封裝函數加載文件
    loadscript('file.js', function() {
      console.log('file is loaded ~');
    });
    複製代碼
  • 使用 XHR 對象下載 javascript代碼並注入頁面中web

    let xhr = new XMLHttpRequest();
    xhr.open('get', 'file.js', true);
    xhr.onreadystatechange = function() {
      if (xhr.readyState === 4) {
        if ((xhr.status >= 200 && xhr.status < 300) || xhr.status === 304) {
          let script = document.createElement('script');
          script.text = xhr.responseText;
          document.body.appendChild(script);
        }
      }
    };
    複製代碼

總結:算法

向頁面中添加大量 JavaScript 的推薦作法:先添加動態加載所需代碼,而後加載初始化頁面所需剩下代碼跨域

數據存儲

一、訪問字面量和局部變量的速度比訪問數組元素和對象成員快,所以儘可能使用字面量和局部變量,減小數組項和對象成員的使用。數組

二、儘量使用局部變量。若是某個跨做用域的值在函數中被引用一次以上,把它存儲到局部變量中。瀏覽器

// 不推薦
function initFn() {
  // document.getElementById('myImg')屢次被引用
  document.getElementById('myImg').src = 'photo.png';
  document.getElementById('myImg').alt = 'photo';
}

// 推薦
function initFn() {
  // 改寫
  let myImg = document.getElementById('myImg');
  myImg.src = 'photo.png';
  myImg.alt = 'photo';
}
複製代碼

三、避免使用 with 語句,它會改變執行環境做用域鏈。try-catch 中的 catch 也有一樣的影響,看實際狀況中進行運用。

四、屬性和方法在原型鏈中的位置越深,訪問的速度越慢

總結:

把經常使用的對象成員、數組元素、跨域變量保存在局部變量中

DOM

一、最小化 DOM 訪問次數,若是須要屢次訪問某個 DOM 節點,使用局部變量存儲它的引用。

// 不推薦
function innerHTMLLoop(){
  for(let i = 0; i < 5; i++){
    document.getElementById('myDiv').innerHTML += 'a';
  }
}

// 推薦
function innerHTMLLoop(){
  let content = '';
  for(let i = 0; i < 5; i++){
    content += 'a';
  }
  document.getElementById('myDiv').innerHTML = content;
}
複製代碼

二、若是須要在迭代中使用 HTML 集合,把它的長度緩存到一個變量中;若是須要屢次操做集合,把它拷貝到一個數組中。

  • HTML 集合有document.getElementsByName(),document.getElementsClassName(),document.getElementsByTagName(),document.images,document.links,document.forms等等,返回的是相似數組的列表,可是它以一種「假定實時態」實時存在,即當底層文檔對象更新時,它也會自動更新。

    // 死循環
    let divs = document.getElementsByTagName('div');
    for (let i = 0; i < divs.length; i++) {
      document.body.appendChild(document.createElement('div'));
    }
    複製代碼
  • 當遍歷一個集合時,把集合存儲在局部變量中,並把 length 緩存在循環外部

    // 推薦
    function collectNodes() {
      let coll = document.getElementsByTagName('div');
      let len = coll.length;
      let nodeName = '';
      let tagName = '';
      let el = null;
      for (let i = 0; i < len; i++) {
        el = coll[i];
        nodeName = el.nodeName;
        tagName = el.tagName;
      }
      return { nodeName, tagName };
    }
    複製代碼

三、減小重繪和重排,批量修改樣式時,「離線」操做 DOM 樹,使用緩存,並減小訪問佈局信息的次數。

  • 當頁面佈局和幾何屬性改變時須要「重排」。下列狀況均會發生「重排」:

    一、添加或刪除可見的 DOM 元素

    二、元素位置發生改變

    三、元素尺寸改變(包括:外邊距、內邊距、邊框厚度、寬度、高度等屬性改變)

    四、內容改變,如文本改變或圖片被另外一個不一樣尺寸的圖片代替

    五、頁面渲染器初始化

    六、瀏覽器窗口改變

  • 完成「重排」後,瀏覽器會從新繪製受影響的部分到屏幕中,過程爲「重繪」。

  • 減小「重繪」和「重排」

    一、使元素脫離文檔流

    二、對其應用屢次改變

    三、把元素帶回文檔中

    // 第一種方式:隱藏元素,應用修改,從新顯示
    let ul = document.getElementById('nyList');
    ul.style.display = 'none';
    // 向ul中添加附加數據
    appendDataToElement(ul, data);
    ul.style.display = 'block';
    
    // 第二種方式:使用文檔片斷(document fragment)在當前DOM以外構建一個子樹,再把它拷貝到文檔(推薦)
    let fragment = document.createDocumentFragment();
    // 向fragment中添加附加數據
    appendDataToElement(ul, data);
    document.getElementById('myList').appendChild(fragment);
    
    // 第三種方式:將原始元素拷貝到一個脫離文檔的節點中,修改副本,完成後再替換原始元素
    let old = document.getElementById('myList');
    let clone = old.cloneNode(true);
    // 向clone中添加附加數據
    appendDataToElement(ul, data);
    old.parentNode.replaceChild(clone, old);
    複製代碼

四、使用事件委託減小事件處理器的數量

// Event對象提供了一個屬性叫target,能夠返回事件的目標節點,咱們成爲事件源,也就是說,target就能夠表示爲當前的事件操做的dom,可是不是真正操做dom,固然,這個是有兼容性的,標準瀏覽器用ev.target,IE瀏覽器用event.srcElement。
myLi.onclick = function(e) {
  let e = e || window.event;
  let target = ev.target || ev.srcElement;
  if (target.nodeName.toLowerCase() == 'li') {
    alert(target.innerHTML);
  }
};
複製代碼

算法和流程控制

  • 選擇時間複雜度低的算法
  • 避免使用 for-in 循環,除非須要遍歷一個屬性數量未知的對象
  • 使用 if-else 要確保最可能出現的條件放在首位,條件數量大時,優先使用 switch
  • 使用查找表。查找表相對於 if-else 和 switch,不近速度快,並且可讀性好
    // 將返回值集合存入數組
    let results = [result0,result1,result2,result3,result4,result5,result6,result7,result8,result9,result10,];
    // 返回當前結果
    return results[value];
    複製代碼

構建和部署

  • 合併 JavaScript 文件以減小 HTTP 請求數

  • 使用 webpack 壓縮 JavaScript 文件

  • 使用 CDN 提供 JavaScript 文件;CDN 不只能夠提高性能,也會爲你管理文件的壓縮和緩存

參考:圖靈圖書《高性能JavaScript》

相關文章
相關標籤/搜索