javasciprt性能優化

来源:https://www.cnblogs.com/chenjg/archive/2018/04/10/8782165.html
-Advertisement-
Play Games

本文主要是在我讀《高性能Javascript》之後,想要記錄下一些有用的優化方案,並且就我本身的一些經驗,來大家一起分享下 , Javascript的載入與執行 大家都知道,瀏覽器在解析DOM樹的時候,當解析到script標簽的時候,會阻塞其他的所有任務,直到該js文件下載、解析執行完成後,才會繼 ...


本文主要是在我讀《高性能Javascript》之後,想要記錄下一些有用的優化方案,並且就我本身的一些經驗,來大家一起分享下

Javascript的載入與執行

大家都知道,瀏覽器在解析DOM樹的時候,當解析到script標簽的時候,會阻塞其他的所有任務,直到該js文件下載、解析執行完成後,才會繼續往下執行。因此,這個時候瀏覽器就會被阻塞在這裡,如果將script標簽放在head里的話,那麼在該js文件載入執行前,用戶只能看到空白的頁面,這樣的用戶體驗肯定是特別爛。對此,常用的方法有以下:

  • 將所有的script標簽都放到body最底部,這樣可以保證js文件是最後載入並執行的,可以先將頁面展現給用戶。但是,你首先得清楚,頁面的首屏渲染是否依賴於你的部分js文件,如果是的話,則需要將這一部分js文件放到head上。
  • 使用defer,比如下麵這種寫法。使用defer這種寫法時,雖然瀏覽器解析到該標簽的時候,也會下載對應的js文件,不過它並不會馬上執行,而是會等到DOM解析完後(DomContentLoader之前)才會執行這些js文件。因此,就不會阻塞到瀏覽器。
<script src="test.js" type="text/javascript" defer></script>
  • 動態載入js文件,通過這種方式,可以在頁面載入完成後,再去載入所需要的代碼,也可以通過這種方式實現js文件懶載入/按需載入,比如現在比較常見的,就是webpack結合vue-router/react-router實現按需載入,只有訪問到具體路由的時候,才載入相應的代碼。具體的方法如下:

1.動態的插入script標簽來載入腳本,比如通過以下代碼

  function loadScript(url, callback) {
    const script = document.createElement('script');
    script.type = 'text/javascript';
    // 處理IE
    if (script.readyState) {
      script.onreadystatechange = function () {
        if (script.readyState === 'loaded' || script.readyState === 'complete') {
          script.onreadystatechange = null;
          callback();
        }
      }
    } else {
      // 處理其他瀏覽器的情況
      script.onload = function () {
        callback();
      }
    }
    script.src = url;
    document.body.append(script);
  }

  // 動態載入js
  loadScript('file.js', function () {
    console.log('載入完成');
  })

2.通過xhr方式載入js文件,不過通過這種方式的話,就可能會面臨著跨域的問題。例子如下:

  const xhr = new XMLHttpRequest();
  xhr.open('get', 'file.js');
  xhr.onreadystatechange = function () {
    if (xhr.readyState === 4) {
      if (xhr.status >= 200 && xhr.status < 300 || xhr.status === 304>) {
        const script = document.createElement('script');
        script.type = 'text/javascript';
        script.text = xhr.responseText;
        document.body.append(script);
      }
    }
  }

3.將多個js文件合併為同一個,並且進行壓縮。 原因:目前瀏覽器大多已經支持並行下載js文件了,但是併發下載還是有一定的數量限制了(基於瀏覽器,一部分瀏覽器只能下載4個),並且,每一個js文件都需要建立一次額外的http連接,載入4個25KB的文件比起載入一個100KB的文件消耗的時間要大。因此,我們最好就是將多個js文件合併為同一個,並且進行代碼壓縮。

javascript作用域

當一個函數執行的時候,會生成一個執行上下文,這個執行上下文定義了函數執行時的環境。當函數執行完畢後,這個執行上下文就會被銷毀。因此,多次調用同一個函數會導致創建多個執行上下文。每隔執行上下文都有自己的作用域鏈。相信大家應該早就知道了作用域這個東西,對於一個函數而言,其第一個作用域就是它函數內部的變數。在函數執行過程中,每遇到一個變數,都會搜索函數的作用域鏈找到第一個匹配的變數,首先查找函數內部的變數,之後再沿著作用域鏈逐層尋找。因此,若我們要訪問最外層的變數(全局變數),則相比直接訪問內部的變數而言,會帶來比較大的性能損耗。因此,我們可以將經常使用的全局變數引用儲存在一個局部變數里

const a = 5;
function outter () {
  const a = 2;
  function inner () {
    const b = 2;
    console.log(b); // 2
    console.log(a); // 2
  }
  inner();
}

對象的讀取

javascript中,主要分為字面量、局部變數、數組元素和對象這四種。訪問字面量和局部變數的速度最快,而訪問數組元素和對象成員相對較慢。而訪問對象成員的時候,就和作用域鏈一樣,是在原型鏈(prototype)上進行查找。因此,若查找的成員在原型鏈位置太深,則訪問速度越慢。因此,我們應該儘可能的減少對象成員的查找次數和嵌套深度。比如以下代碼

  // 進行兩次對象成員查找
  function hasEitherClass(element, className1, className2) {
    return element.className === className1 || element.className === className2;
  }
  // 優化,如果該變數不會改變,則可以使用局部變數保存查找的內容
  function hasEitherClass(element, className1, className2) {
    const currentClassName = element.className;
    return currentClassName === className1 || currentClassName === className2;
  }

DOM操作優化

  • 最小化DOM的操作次數,儘可能的用javascript來處理,並且儘可能的使用局部變數儲存DOM節點。比如以下的代碼:
  // 優化前,在每次迴圈的時候,都要獲取id為t的節點,並且設置它的innerHTML
  function innerHTMLLoop () {
    for (let count = 0; count < 15000; count++) {
      document.getElementById('t').innerHTML += 'a';
    }
  }
  // 優化後,
  function innerHTMLLoop () {
    const tNode = document.getElemenById('t');
    const insertHtml = '';
    for (let count = 0; count < 15000; count++) {
      insertHtml += 'a';
    }
    tNode.innerHtml += insertHtml;
  }
  • 儘可能的減少重排和重繪,重排和重匯可能會代價非常昂貴,因此,為了減少重排重匯的發生次數,我們可以做以下的優化

1.當我們要對Dom的樣式進行修改的時候,我們應該儘可能的合併所有的修改並且一次處理,減少重排和重匯的次數。

  // 優化前
  const el = document.getElementById('test');
  el.style.borderLeft = '1px';
  el.style.borderRight = '2px';
  el.style.padding = '5px';

  // 優化後,一次性修改樣式,這樣可以將三次重排減少到一次重排
  const el = document.getElementById('test');
  el.style.cssText += '; border-left: 1px ;border-right: 2px; padding: 5px;'

2.當我們要批量修改DOM節點的時候,我們可以將DOM節點隱藏掉,然後進行一系列的修改操作,之後再將其設置為可見,這樣就可以最多只進行兩次重排。具體的方法如下:

  // 未優化前
  const ele = document.getElementById('test');
  // 一系列dom修改操作

  // 優化方案一,將要修改的節點設置為不顯示,之後對它進行修改,修改完成後再顯示該節點,從而只需要兩次重排
  const ele = document.getElementById('test');
  ele.style.display = 'none';
  // 一系列dom修改操作
  ele.style.display = 'block';

  // 優化方案二,首先創建一個文檔片段(documentFragment),然後對該片段進行修改,之後將文檔片段插入到文檔中,只有最後將文檔片段插入文檔的時候會引起重排,因此只會觸發一次重排。。
  const fragment = document.createDocumentFragment();
  const ele = document.getElementById('test');
  // 一系列dom修改操作
  ele.appendChild(fragment);

3.使用事件委托:事件委托就是將目標節點的事件移到父節點來處理,由於瀏覽器冒泡的特點,當目標節點觸發了該事件的時候,父節點也會觸發該事件。因此,由父節點來負責監聽和處理該事件。那麼,它的優點在哪裡呢?假設你有一個列表,裡面每一個列表項都需要綁定相同的事件,而這個列表可能會頻繁的插入和刪除。如果按照平常的方法,你只能給每一個列表項都綁定一個事件處理器,並且,每當插入新的列表項的時候,你也需要為新的列表項註冊新的事件處理器。這樣的話,如果列表項很大的話,就會導致有特別多的事件處理器,造成極大的性能問題。而通過事件委托,我們只需要在列表項的父節點監聽這個事件,由它來統一處理就可以了。這樣,對於新增的列表項也不需要做額外的處理。而且事件委托的用法其實也很簡單:

function handleClick(target) {
  // 點擊列表項的處理事件
}
function delegate (e) {
  // 判斷目標對象是否為列表項
  if (e.target.nodeName === 'LI') {
    handleClick(e.target);
  }
}
const parent = document.getElementById('parent');
parent.addEventListener('click', delegate);

您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 一個簡單的TextView實現了打字機的效果讓文字一個個顯示出來, 方法介紹: startShow 開始打字 使用: startShow(int typeStartTime,int typeTime) 使用: typeAndhide(View v,int duration) 打字結束後隱藏TextV ...
  • 前言: 看到大神們的寫的第三方控制項,比較好用,我們使用的時候直接是在gradle上加上代碼就可以使用了,現在到我們寫了一個第三方控制項,想要別人使用的時候也是直接在gradle加上相關的代碼就可以用了,我們該如何操作呢?答案很簡單,就是在JitPack網站上發佈我們的庫就可以 正文: 1.創建一個mo ...
  • 廢話少說,看東西 幀動畫的創建方式主要以下2種: * 用xml創建動畫; * 純Java代碼創建動畫; 本文內容主要關註 純java代碼創建幀動畫 的方式; * 用xml創建幀動畫:http://www.cnblogs.com/geaosu/p/8745151.html 純Java代碼創建動畫過程: ...
  • 現在web從伺服器請求數據,很多用到Ajax,不過都是用的JQuery封裝好的,之前做項目,由於無法引用JQuery,所以就只能用原生了,話不多說,請看代碼。 自己封裝Ajax,主要分三步: 第一步:創建需要的對象,這裡主要用到的是XMLHttpRequest,註意需要考慮早期的IE; 第二步:連接 ...
  • n 超級鏈接 l 語法格式:<a 屬性 = “值”>………</a> l 常用屬性: n Href:目標文件的地址URL,該URL可以是相對地址,也可以是絕對地址。 n Target:目標文件的顯示視窗。 -blank:在新的視窗中打開目標文件。 -self:在當前視窗中打開目標文件(預設方式),相當 ...
  • 今天是把添加好友的功能實現了,第三次課漸入尾聲,這幾天的進度可能要稍微差一點,但是後期,特別是考完試以後,我想進度應該能趕上來。 加是能加上,就是因為老師偷懶,直接reload()的了,整個頁面都要刷,用戶體驗極差。 應該是能做到局部刷新的,就像刪除好友一樣,不過不會寫,這個坑就占時空在這兒。 其實 ...
  • 一、遞歸組件 組件在它的模板內可以遞歸地調用自己, 只要給組件設置name 的選項就可以了。 示例如下: 渲染結果為: 設置name 後,在組件模板內就可以遞歸使用了,不過需要註意的是,必須給一個條件來限制遞歸數量,否則會拋出錯誤: max stack size exceeded 。 組件遞歸使用可 ...
  • 一:聲明文檔類型 <!DOCTYPE html> 二:註釋方式 <!--註釋內容--> 快捷鍵:ctrl+/ 三:文檔結構 四:<head></head>標簽內的六大標簽 link: 鏈接外部的css和js文件 script: 定義頁面腳本 base: 鏈接定位,少用 title: 定義頁面標題 s ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...