【萬字長文】前端效能優化實踐

2023-11-14 18:01:15

一、引言

從一個假死頁面引發的思考: 作為前端開發,除了要攻克頁面難點,也要有更深的自我目標,效能優化是自我提升中很重要的一環; 在前端開發中,會偶遇到頁面假死的現象, 是因為當js有大量計算時,會造成 UI 阻塞,出現介面卡頓、掉幀等情況,嚴重時會出現頁面卡死的情況;

在這裡簡單穿插概念之程序和執行緒

  • 程序:一個在記憶體中執行的應用程式。每個程序都有自己獨立的一塊記憶體空間,一個程序可以有多個執行緒,比如在Windows系統中,一個執行的demo.exe就是一個程序。
  • 執行緒:程序中的一個執行任務(控制單元),負責當前程序中程式的執行。一個程序至少有一個執行緒,一個程序可以執行多個執行緒,多個執行緒可共用資料。與程序不同的是同類的多個執行緒共用程序的堆和方法區資源,但每個執行緒有自己的程式計數器、虛擬機器器棧和本地方法棧,所以系統在產生一個執行緒,或是在各個執行緒之間作切換工作時,負擔要比程序小得多,也正因為如此,執行緒也被稱為輕量級程序。

程序與執行緒的區別

執行緒具有許多傳統程序所具有的特徵,故又稱為輕型程序(Light—Weight Process)或程序元;而把傳統的程序稱為重型程序(Heavy—Weight Process),它相當於只有一個執行緒的任務。在引入了執行緒的作業系統中,通常一個程序都有若干個執行緒,至少包含一個執行緒。

◦根本區別:程序是作業系統資源分配的基本單位,而執行緒是處理器任務排程和執行的基本單位;

◦資源開銷:每個程序都有獨立的程式碼和資料空間(程式上下文),程式之間的切換會有較大的開銷;執行緒可以看做輕量級的程序,同一類執行緒共用程式碼和資料空間,每個執行緒都有自己獨立的執行棧和程式計數器(PC),執行緒之間切換的開銷小。

◦包含關係:如果一個程序內有多個執行緒,則執行過程不是一條線的,而是多條線(執行緒)共同完成的;執行緒是程序的一部分,所以執行緒也被稱為輕權程序或者輕量級程序。

◦記憶體分配:同一程序的執行緒共用本程序的地址空間和資源,而程序之間的地址空間和資源是相互獨立的;

◦影響關係:一個程序崩潰後,在保護模式下不會對其他程序產生影響,但是一個執行緒崩潰整個程序都死掉。所以多程序要比多執行緒健壯。

◦執行過程:每個獨立的程序有程式執行的入口、順序執行序列和程式出口。但是執行緒不能獨立執行,必須依存在應用程式中,由應用程式提供多個執行緒執行控制,兩者均可並行執行;

=> 回到阻塞原因分析

瀏覽器的渲染程序的執行緒:

瀏覽器有GUI渲染執行緒與JS引擎執行緒,這兩個執行緒是互斥的關係,當JS引擎執行時GUI執行緒會被掛起(相當於被凍結了),GUI更新會被儲存在一個佇列中,等到JS引擎空閒時,立即被執行。js引擎不是每次在執行更新dom語句時,都會停下來等Gui渲染引擎更新完dom再執行後面的js程式碼; 詳見GUI渲染執行緒與JS引擎執行緒

  1. GUI渲染執行緒: 負責渲染瀏覽器頁面,解析HTML、CSS,構建DOM樹、構建CSSOM樹、構建渲染樹和繪製頁面;當介面需要重繪或由於某種操作引發迴流時,該執行緒就會執行。注意:GUI渲染執行緒和JS引擎執行緒是互斥的,當JS引擎執行時GUI執行緒會被掛起,GUI更新會被儲存在一個佇列中等到JS引擎空閒時立即被執行。
  2. JS引擎執行緒: JS引擎執行緒也稱為JS核心,負責處理Javascript指令碼程式,解析Javascript指令碼,執行程式碼;JS引擎執行緒一直等待著任務佇列中任務的到來,然後加以處理,一個Tab頁中無論什麼時候都只有一個JS引擎執行緒在執行JS程式;注意:GUI渲染執行緒與JS引擎執行緒的互斥關係,所以如果JS執行的時間過長,會造成頁面的渲染不連貫,導致頁面渲染載入阻塞。
  3. 事件觸發執行緒:事件觸發執行緒屬於瀏覽器而不是JS引擎,用來控制事件迴圈;當JS引擎執行程式碼塊如setTimeOut時(也可是來自瀏覽器核心的其他執行緒,如滑鼠點選、AJAX非同步請求等),會將對應任務新增到事件觸發執行緒中;當對應的事件符合觸發條件被觸發時,該執行緒會把事件新增到待處理佇列的隊尾,等待JS引擎的處理;注意:由於JS的單執行緒關係,所以這些待處理佇列中的事件都得排隊等待JS引擎處理(當JS引擎空閒時才會去執行);
  4. 定時器觸發執行緒: 定時器觸發執行緒即setInterval與setTimeout所線上程;瀏覽器定時計數器並不是由JS引擎計數的,因為JS引擎是單執行緒的,如果處於阻塞執行緒狀態就會影響記計時的準確性;因此使用單獨執行緒來計時並觸發定時器,計時完畢後,新增到事件佇列中,等待JS引擎空閒後執行,所以定時器中的任務在設定的時間點不一定能夠準時執行,定時器只是在指定時間點將任務新增到事件佇列中;注意:W3C在HTML標準中規定,定時器的定時時間不能小於4ms,如果是小於4ms,則預設為4ms。
  5. 非同步http請求執行緒

二、案例分析

  • 背景: 測試流水線開發過程中,為了滿足使用者操作方便,從節點中提取不同的指標來作為查詢條件的指標來源和資料來源; 因此使用者的每次操作都會進行重新計算,引起迴流; 由於前端元件父子巢狀層級很多,在資料過多的時候也會形成頁面假死的狀態。
  • 基於不同的效能工具檢測;

1、performance Api

錄製步驟: 重新整理頁面載入流水線詳情頁面,然後找到某個用例技術端,進行結果更新;因此下面的圖分3個部分,第一個是詳情頁渲染; 第二段是需要大量計算的用例渲染部分; 第三段是操作結果,資料重新重新整理,重新計算和渲染的部分;

其中,FPS上方顯示紅色條的時候,意味著影格率特別低,使用者體驗特別不好。一般來說,綠色條越高,FPS越高。 分析效能圖示第一眼看FPS;效能面板底部,圖形圖表的色彩越多,意味著CPU效能已經達到極限。當我們看到CPU長時間處於最大值狀態,就需要考慮怎樣去優化

介面的監控 + 詳情頁渲染 + 用例渲染;

上面的Group面板非常有用。我們可以很清晰明瞭得分析按照活動,目錄,域,子域,URL和Frame進行分組的前端效能;

Bottom-Up: 是The Heavy (Bottom Up) view is available in the Bottom-Up tab,類似事件冒泡;

Call Tree:是And the Tree (Top Down) view is available in the Call Tree tab,類似事件捕獲;

更新結果,頁面重新繪製,計算;

黃色(Scripting):JavaScript執行

紫色(Rendering):樣式計算和佈局,即重排

藍色(Loading):網路通訊和HTML解析

綠色(Painting):重繪

灰色(System):其它事件花費的時間

白色(Idle):空閒時間: 可能是同時請求了很多介面,promise.all需要等待所有介面返回成功後才會渲染頁面,idle時間變長了很多,瀏覽器一直在等待介面全部返回;

用例列表和新增按鈕部分產生了偏移, 紅色Layout Shift;

2、lightHouse分析資料;分數很低;

3、performace monitor: 載入過程中,CPU飆80%;

主要可改善指標如下:

  • TBT: total Blocking Time總阻塞時間

  • CLS:記錄了頁面上非預期的位移波動

相關名詞解析:https://web.dev/metrics/

指標 指標解析
Self Time Self Time代表函數本身執行消耗時間
Total Time Total Time則是函數本身消耗再加上在呼叫它的函數中消耗的總時間
Activity 瀏覽器活動的意思
DOM GC DOM垃圾回收
Timer Fried 銷燬計時器
XMR Load 非同步載入物件載入
Major GC 清理年老區(Tenured space)
Minor GC 每次Minor GC只會清理年輕代
Run Microtasks 執行微服務
Recalculate Style
HitTest https://www.jianshu.com/p/f6aff12fc08b
DCLDomContentloaded 當 HTML 檔案被完全載入和解析完成之後,DOMContentLoaded 事件被觸發,無需等待樣式表、影象和子框架的完成載入.
SI (Speed Index) 指標用於顯示頁面可見部分的顯示速度, 單位是時間,
FPFirst Paint 首次繪製 首次繪製(FP)這個指標用於記錄頁面第一次繪製畫素的時間,如顯示頁面背景色。FP不包含預設背景繪製,但包含非預設的背景繪製。
FCPFirst contentful paint 首次內容繪製 (FCP): LCP是指頁面開始載入到最大文字塊內容或圖片顯示在頁面中的時間。如果 FP 及 FCP 兩指標在 2 秒內完成的話我們的頁面就算體驗優秀。
LCPLargest contentful paint 最大內容繪製 (LCP): 用於記錄視窗內最大的元素繪製的時間,該時間會隨著頁面渲染變化而變化,因為頁面中的最大元素在渲染過程中可能會發生改變,另外該指標會在使用者第一次互動後停止記錄。官方推薦的時間區間,在 2.5 秒內表示體驗優秀
FID(First input delay) 首次輸入延遲,FID(First Input Delay),記錄在 FCP 和 TTI 之間使用者首次與頁面互動時響應的延遲
TTITime to Interactive 可互動時間 (TTI)首次可互動時間,TTI(Time to Interactive)。這個指標計算過程略微複雜,它需要滿足以下幾個條件:1、從 FCP 指標後開始計算2、持續 5 秒內無長任務(執行時間超過 50 ms)且無兩個以上正在進行中的 GET 請求往前回溯至 5 秒前的最後一個長任務結束的時間3、對於使用者互動(比如點選事件),推薦的響應時間是 100ms 以內。那麼為了達成這個目標,推薦在空閒時間裡執行任務不超過 50ms( W3C 也有這樣的標準規定),這樣能在使用者無感知的情況下響應使用者的互動,否則就會造成延遲感。
TBTTotal blocking Time 總阻塞時間 (TBT)阻塞總時間,TBT(Total Blocking Time),記錄在 FCP 到 TTI 之間所有長任務的阻塞時間總和。
CLSCumulative Layout Shift 記錄了頁面上非預期的位移波動。頁面渲染過程中突然插入一張巨大的圖片或者說點選了某個按鈕突然動態插入了一塊內容等等相當影響使用者體驗的網站。這個指標就是為這種情況而生的,計算方式為:位移影響的面積 * 位移距離。

三、效能優化三大核心指標:LCP FID CLS

上述的指標太多了,哪些才是核心指標呢? Google 在2020年五月提出了網站使用者體驗的三大核心指標

1、Largest Contentful Paint (LCP)

LCP:最大內容繪製 , 代表了頁面的速度指標,雖然還存在其他的一些體現速度的指標,但LCP能體現的東西更多一些。一是指標實時更新,資料更精確,二是代表著頁面最大元素的渲染時間,通常來說頁面中最大元素的快速載入能讓使用者感覺效能還挺好。

最大元素:

標籤

在svg中的image標籤

◦CSS background url()載入的圖片

◦包含內聯或文字的塊級元素

影響元素:

◦伺服器端響應時間----介面效能

◦Javascript和CSS引起的渲染卡頓 ✨✨✨---webWork.js計算問題

◦資源載入時間✨✨✨---CDN

◦使用者端渲染✨✨---SSR

2、First Input Delay (FID):

FID:首次輸入延遲, 代表了頁面的互動體驗指標,就是看使用者互動事件觸發到頁面響應中間耗時多少,如果其中有長任務發生的話那麼勢必會造成響應時間變長,推薦響應使用者互動在 100ms 以內。

影響因素

◦減少第三方程式碼的影響

◦減少Javascript的執行時間

◦最小化主執行緒工作

◦減小請求數量和請求檔案大小

3、Cumulative Layout Shift (CLS)

CLS代表了頁面的穩定指標,它能衡量頁面是否排版穩定。尤其在手機上這個指標更為重要,因為手機螢幕挺小,CLS值一大的話會讓使用者覺得頁面體驗做的很差。CLS的分數在0.1或以下,則為Good。

影響因素: 通過下面的原則避免非預期佈局移動:

◦圖片或視屏元素有大小屬性,或者給他們保留一個空間大小,設定width、height,或者使用 unsized-media feature policy 。

◦不要在一個已存在的元素上面插入內容,除了相應使用者輸入。

◦使用animation或transition而不是直接觸發佈局改變。

四、如何使用效能檢測工具?

  1. Lighthouse(速度也比較快, 支援json指標產出和頁面html產出),在本地進行測量,根據報告給出的一些建議進行優化;---例如CLS,用這個就很方便, 滿足分值後或者通過評審後上線;
  2. 專案發布上線後,我們可以使用PageSpeed Insights去看下線上的效能情況;
  3. 使用PageSpeed Insights,可以使用Chrome User Experience Report API去撈取線上過去28天的資料;
  4. 資料有異常,可使用DevTools工具進行具體程式碼定位分析;---performance分析, 方案總結和優化
  5. 使用Search Console’s Core Web Vitals report檢視網站功能整體情況;
  6. 使用Web Vitals擴充套件方便的看頁面核心指標情況;

五、頁面過程詳細解析

上述講了網路請求,渲染過程,那下面我們看下一個簡單的頁面的整個過程是怎麼樣的;

  • 導航階段,該階段主要是從網路程序接收 HTML 響應頭和 HTML 響應體。
  • 解析 HTML 資料階段,該階段主要是將接收到的 HTML 資料轉換為 DOM 和 CSSOM。
  • 生成可顯示的點陣圖階段,該階段主要是利用 DOM 和 CSSOM,經過計算佈局、生成層樹 (LayerTree)、生成繪製列表 (Paint)、完成合成等操作,生成最終的圖片。

1. 導航階段,請求 HTML 資料階段

◦該任務的第一個子過程就是 Send request,該過程表示網路請求已被傳送。然後該任務進入了等待狀態。

◦接著由網路程序負責下載資源,當接收到響應頭的時候,該任務便執行 Receive Respone 過程,該過程表示接收到 HTTP 的響應頭了。

◦接著執行 DOM 事件:pagehide、visibilitychange 和 unload 等事件,如果你註冊了這些事件的回撥函數,那麼這些回撥函數會依次在該任務中被呼叫。

◦些事件被處理完成之後,那麼接下來就接收 HTML 資料了,這體現在了 Recive Data 過程,Recive Data 過程表示請求的資料已被接收,如果 HTML 資料過多,會存在多個 Receive Data 過程。

◦等到所有的資料都接收完成之後,渲染程序會觸發另外一個任務,該任務主要執行 Finish load 過程,該過程表示網路請求已經完成。

2. 解析 HTML 資料階段

其中一個主要的過程是 HTMLParser:解析的上個階段接收到的 HTML 資料。

◦在 ParserHTML 的過程中,如果解析到了 script 標籤,那麼便進入了指令碼執行過程,也就是圖中的 Evalute Script。

◦DOM 生成完成之後,會觸發相關的 DOM 事件,比如:典型的 DOMContentLoaded,還有 readyStateChanged。

◦DOM 生成之後,ParserHTML 過程繼續計算樣式表,也就是 Reculate Style,這就是生成 CSSOM 的過程

3. 生成可顯示點陣圖階段

該階段需要經歷佈局 (Layout)、分層、繪製、合成等一系列操作:

在生成完了 DOM 和 CSSOM 之後,渲染主執行緒首先執行了一些 DOM 事件,諸如 readyStateChange、load、pageshow。

總而言之,大致過程如下:

◦首先執行佈局,這個過程對應圖中的 Layout。

◦然後更新層樹 (LayerTree),這個過程對應圖中的 Update LayerTree。

◦有了層樹之後,就需要為層樹中的每一層準備繪製列表了,這個過程就稱為 Paint。

◦準備每層的繪製列表之後,就需要利用繪製列表來生成相應圖層的點陣圖了,這個過程對應圖中的 Composite Layers。走到了這步,主執行緒的任務就完成了。

接下來主執行緒會將合成的任務完全教給合成執行緒來執行,下面是具體的過程,也可以對照著 Composite、Raster 和 GPU 這三個指標來分析

  • 首先主執行緒執行到 Composite Layers 過程之後,便會將繪製列表等資訊提交給合成執行緒,合成執行緒的執行記錄你可以通過 Compositor 指標來檢視。合成執行緒維護了一個 Raster 執行緒池,執行緒池中的每個執行緒稱為 Rasterize,用來執行光柵化操作,對應的任務就是 Rasterize Paint。當然光柵化操作並不是在 Rasterize 執行緒中直接執行的,而是在 GPU 程序中執行的,因此 Rasterize 執行緒需要和 GPU 執行緒保持通訊。然後 GPU 生成影象,最終這些圖層會被提交給瀏覽器程序,瀏覽器程序將其合成並最終顯示在頁面上。

六、效能優化實踐方案

對於前端應用來說,網路耗時、頁面載入耗時、指令碼執行耗時、渲染耗時等耗時情況會影響使用者的等待時長,

而 CPU佔用、記憶體佔用、本地快取佔用等則可能會導致頁面卡頓甚至卡死。

因此,效能優化可以分別從耗時和資源佔用兩方面來解決,也可以理解成時間和空間兩個方面。

時間角度(耗時)

在時間角度進行優化主要是減少耗時,瀏覽器在頁面載入的過程中,主要會進行以下的步驟:

  • 網路請求相關(發起 HTTP 請求從伺服器端獲取頁面資源,包括 HTML/CSS/JS/圖片資源等)瀏覽器解析 HTML 和渲染頁面載入 Javascript 程式碼時會暫停頁面渲染(包括解析到外部資源,會發起 HTTP 請求獲取並載入)

耗時優化的著手點:

1、網路請求優化: 目標在於減少網路資源的請求和載入耗時,如果考慮 HTTP 請求過程,顯然我們可以從幾個角度來進行優化:

  • 請求鏈路:DNS 查詢、部署 CDN 節點、快取等

對於請求鏈路,核心的方案常常包括使用快取,比如 DNS 快取、CDN 快取、HTTP 快取、後臺快取等等,前端的話還可以考慮使用Service Worker、PWA等技術。使用快取並非萬能藥,很多使用由於快取的存在,我們在功能更新修復的時候還需要考慮快取的情況。除此之外,還可以考慮使用HTTP/2、HTTP/3 等提升資源請求速度,以及對多個請求進行合併,減少通訊次數;對請求進行域名拆分,提升並行請求數量。

  • 資料大小:程式碼大小、圖片資源等

資料大小則主要考對請求資源進行合理的拆分(CSS、Javascript 指令碼、圖片/音訊/視訊等)和壓縮,減少請求資源的體積,比如使用Tree-shaking、程式碼分割、移除用不上的依賴項等。在請求資源返回後,瀏覽器會進行解析和載入,這個過程會影響頁面的可見時間,通過對首屏載入的優化,可有效地提升使用者體驗。

2、首屏載入優化: 首屏載入優化核心點在於兩部分:

◦將頁面內容儘快地展示給使用者,減少頁面白屏時間。

◦將使用者可操作的時間儘量提前,避免使用者無法操作的卡頓體驗。

我們的頁面也需要在使用者端進行展示,此時可充分利用使用者端的優勢:

◦配合使用者端進行資源預請求和預載入,比如使用預熱 Web 容器配合使用者端將資源和資料進行離線,可用於下一次頁面的快速渲染使用秒看技術,通過生成預覽圖片的方式提前將頁面內容提供給使用者除了首屏渲染以外,使用者在瀏覽器頁面過程中,也會觸發頁面的二次運算和渲染,此時需要進行渲染過程的優化

3、渲染過程優化:渲染過程的優化可以理解成首屏載入完成後,使用者的操作互動觸發的二次渲染。主要思路是減少使用者的操作等待時間,以及通過將頁面渲染影格率保持在 60FPS 左右,提升頁面互動和渲染的流暢度。包括但不限於以下方案:

◦使用資源預載入,提升空閒時間的資源利用率--preload

◦減少/合併 DOM 操作,減少瀏覽器渲染過程中的計算耗時

◦使用離屏渲染,在頁面不可見的地方提前進行渲染(比如 Canvas 離屏渲染)

◦通過合理使用瀏覽器 GPU 能力,提升瀏覽器渲染效率(比如使用 css transform 代替 Canvas 縮放繪製)

以上這些,是對常見的 Web 頁面渲染優化方案。對於運算邏輯複雜、計算量較大的業務邏輯,我們還需要進行計算/邏輯執行的提速。

什麼是重繪和迴流

迴流比重繪更加消耗效能,付出的代價更高。

◦recalculate style (style):結合DOM和CSSOM,確定各元素應用的CSS規則

◦layout:重新計算各元素位置來佈局頁面,也稱reflow

◦update layer tree (layer):更新渲染樹

◦paint:繪製各個圖層

◦composite layers (composite):把各個圖層合成為完整頁面

核心: 佈局會不會變! 迴流一定會導致重繪,重繪不一定導致迴流

1.重繪:簡單來說就是重新繪畫,當給一個元素更換顏色、更換背景,雖然不會影響頁面佈局,但是顏色或背景變了,就會重新渲染頁面,這就是重繪。

2.迴流: 當增加或刪除dom節點,或者給元素修改寬高時,會改變頁面佈局,那麼就會重新構造dom樹然後再次進行渲染,這就是迴流。

哪些會引起迴流呢?

  • 改變dom元素的幾何屬性,常見的幾何屬性有 width、height、padding、margin、left、top、border 等等。
  • 改變dom樹的結構,主要指的是增加或減少dom節點,移動等操作。
  • 獲取一定特殊的屬性值,如屬性:offsetTop、offsetLeft、 offsetWidth、offsetHeight、scrollTop、scrollLeft、scrollWidth、scrollHeight、clientTop、clientLeft、clientWidth、clientHeight 時,你就要注意了!除此之外:呼叫了 getComputedStyle 方法,也會觸發迴流。

總結

重繪不會引起dom結構和頁面佈局的變化,只是樣式的變化,有重繪不一定有迴流。

迴流則是會引起dom結構和頁面佈局的變化,有迴流就一定有重繪。

怎麼進行優化或減少?

  • 多個屬性儘量使用簡寫,例如:boder可以代替boder-width、boder-color、boder-style
  • 建立多個dom節點時,使用documentfragment建立
  • 避免使用table佈局
  • 避免設定多層內聯樣式,避免節點層級過多!!!
  • 避免使用css表示式
  • 將頻繁重繪或迴流的節點設定為圖層,圖層能夠阻止該節點的渲染行為影響到別的節點(例:will-change\video\iframe等標籤),瀏覽器會自動將該節點變為圖層

1.計算/邏輯執行提速

  • 計算/邏輯執行速度優化的主要思路是「拆大為小、多路並行」,方式包括但不限於:

通過將 Javscript 大任務進行拆解,結合非同步任務的管理,避免出現長時間計算導致頁面卡頓的情況

將耗時長且非關鍵邏輯的計算拆離,比如使用 Web Worker

通過使用執行效率更高的方式,減少計算耗時,比如使用 Webassembly

通過將計算過程提前,減少計算等待時長,比如使用 AOT 技術

通過使用更優的演演算法或是儲存結構,提升計算效率,比如 VSCode 使用紅黑樹優化文字緩衝區的計算

通過將計算結果快取的方式,減少運算次數

空間角度(資源)

在做效能優化的時候,其實很多情況下會依賴時間換空間、空間換時間等方式,只能根據自己專案的實際情況做出取捨,選擇相對合適的一種方案去進行優化。

資源佔用常見的優化方式包括:

1.合理使用快取,不濫用使用者的快取資源(比如瀏覽器快取、IndexDB),及時進行快取清理;

2.避免存在記憶體洩露,比如儘量避免全域性變數的使用、及時解除參照等

3.避免複雜/異常的遞迴呼叫,導致呼叫棧的溢位

4.通過使用資料結構享元的方式,減少物件的建立,從而減少記憶體佔用

七、效能提升解決方案實踐:

實踐1: 單執行緒到多執行緒的實踐

注意:new Worker(xxx.js)裡的xxx.js必須和HTML檔案同源必須在http/https協定下存取HTML檔案,不能用檔案協定(類似file:///E:/wamp64/www/t.html 這種

因此我用mamp搭建的一個環境指向測試的資料夾;本地設定http://www.performance.com/進行測試;

主程序程式碼
<!DOCTYPE html>
<html lang="en">


<head>
  <meta charset="UTF-8">
  <title>web-worker小測試</title>
</head>
<body>
  <!-- <script type="text/javascript" src="worker.js"></script> -->
  <script>
    // const work = new Worker('worker.js');
    // work.postMessage('hello worker')
    // work.onmessage = (e) => {
    //   console.log(`主程序收到了子程序發出的資訊:${e.data}`);
    //   // 主程序收到了子程序發出的資訊:你好,我是子程序!
    //   work.terminate();
    // };
    let cnt = 0;
    for (let i = 0; i < 900000000; i += 1) {
      cnt += 1;
    }
    console.log(cnt);
  </script>
</body>


</html

worker.js

onmessage = (e) => {
	console.log(`收到了主程序發出的資訊:${e.data}`); 
  let cnt = 0;
  for (let i = 0; i < 900000000; i += 1) {
    cnt += 1;
  }
  console.log(cnt);
	//收到了主程序發出的資訊:hello worker
	postMessage(`你好,我是子程序!${cnt}`);
}
  

在vue.js中的使用過程, 除了設定,其他同上;


1、npm install vue-worker
2、chainWebpack中進行設定:
    config.module
      .rule('worker')
      .test(/\.worker\.js$/)
      .use('worker-loader')
      .loader('worker-loader')
      .options({
        inline: 'fallback',
      });
    config.module.rule('js').exclude.add(/\.worker\.js$/);

更新後,當前頁面程式碼段中,計算300ms+ 提升,效果一般,主要還是dom節點過多引起的;

Web Worker的限制

1.在 Worker 執行緒的執行環境中沒有 window 全域性物件,也無法存取 DOM 物件

2.Worker中只能獲取到部分瀏覽器提供的 API,如定時器、navigator、location、XMLHttpRequest等

3.由於可以獲取XMLHttpRequest 物件,可以在 Worker 執行緒中執行ajax請求

4.每個執行緒執行在完全獨立的環境中,需要通過postMessage、 message事件機制來實現的執行緒之間的通訊

計算的運算時長 - 通訊時長 > 50ms,推薦使用Web Worker

實踐2, webpack設定方向

1.生產環境關閉productionSourceMap、css sourceMap

SourceMap就是當頁面出現某些錯誤,能夠定位到具體的某一行程式碼,SourceMap就是幫你建立這個對映關係的,方便程式碼偵錯;

關閉css sourceMap以後,js檔案從55M降低到12M, 檔案從650個減少至325個;

實踐3、網路請求優化-CDN資源引入

分析大檔案, 目前專案的情況: js總計12.1M (325個專案); css 總計11.1M (249個專案),

◦element-ui年1.85M--涉及改造的有點多,暫時不更新;

◦Ecahrts 2.55M --cdn引入

◦handsontable 3.34M---線上引入後,由於handsontable-vue還會安裝handsontable, 因此需要把handsontable-vue也使用線上的方式,cdh資源可在https://www.jsdelivr.com/package/npm/@handsontable/vue中找到;

◦vue-json-editor 1.24M--沒找到線上js資源,建議元件引入的時候,直接使用json-editor;

◦將資源進行cdn方式引入;使用CDN引入以後,js總計9.3M(326個專案), css總計10.9(249個專案)

  • 安裝webpack-bundle-analyzer
  • vue.config.js
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;


// 正式環境不打包公共js
let externals = {};
// 儲存cdn的檔案
const cdn = {
  css: [
    'https://cdn.jsdelivr.net/npm/handsontable/dist/handsontable.full.min.css',
  ],
  js: [],
};


// 正式環境才需要
// if (isProduction) {
externals = { // 排除打包的js
  vue: 'Vue',
  echarts: 'echarts',
  vueHandsontable: 'vue-handsontable',
};
cdn.js = [
  'https://cdn.bootcdn.net/ajax/libs/vue/2.6.11/vue.min.js', // vuejs
  'https://cdn.jsdelivr.net/npm/[email protected]/dist/echarts.min.js',
  'https://cdn.jsdelivr.net/npm/handsontable/dist/handsontable.full.min.js',
  'https://cdn.jsdelivr.net/npm/@handsontable/[email protected]/dist/vue-handsontable.min.js',
];
// }
**************
  configureWebpack: {
    // 常用的公共js 排除掉,不打包 而是在index新增cdn,
    externals,
    plugins: [
      new BundleAnalyzerPlugin(), // 分析打包大小使用預設設定
    ],
  },
*************
  chainWebpack: (config) => {
    // 注入cdn變數 (打包時會執行)
    config.plugin('html').tap((args) => {
      args[0].cdn = cdn; // 設定cdn給外掛
      return args;
    });
  }

實踐4、通過 compression-webpack-plugin 外掛把程式碼壓縮為gzip。但是!需要伺服器支援webpack端 vue.config.js設定如下:

使用CDN引入以後,js總計9.3M(326個專案)=> 3.1M, css總計10.9(249個專案)--沒有發生變化

nginx的設定, 這篇文章很不錯https://www.cnblogs.com/wwjj4811/p/15847916.html, 這塊我沒線上測試;不過應該沒啥問題;

1、安裝 compression-webpack-plugin(vue2--npm install --save-dev [email protected])
2、const CompressionPlugin = require('compression-webpack-plugin');
3、chainWebpack中設定
    if (isProduction) {
      config.plugin('compressionPlugin').use(new CompressionPlugin({
        test: /\.(js)$/, // 匹配檔名
        threshold: 10240, // 對超過10k的資料壓縮
        minRatio: 0.8,
        deleteOriginalAssets: true, // 刪除原始檔
      }));
    }
4、nginx中增加設定後重啟
gzip on;  			  #開啟gzip功能
gzip_types *;		  #壓縮原始檔型別,根據具體的存取資源型別設定
gzip_comp_level 6;	  #gzip壓縮級別
gzip_min_length 1024; #進行壓縮響應頁面的最小長度,content-length
gzip_buffers 4 16K;	  #快取空間大小
gzip_http_version 1.1; #指定壓縮響應所需要的最低HTTP請求版本
gzip_vary  on;		  #往頭資訊中新增壓縮標識
gzip_disable "MSIE [1-6]\."; #對IE6以下的版本都不進行壓縮
gzip_proxied  off; #nginx作為反向代理壓縮伺服器端返回資料的條件




實踐5、tree-shaking

tree shaking:通常用於描述移除 JavaScript 上下文中的未參照程式碼(dead-code)。它依賴於 ES2015 模組系統中的靜態結構特性,例如import和export,是 webpack 4 版本,擴充套件的這個檢測能力;

使用tree-shaking以後,js總計3.1M(326個專案)=> 2.9M(315個專案), css總計10.9(249個專案)--384K(15個專案)

作者:京東零售 蘇文靜

來源:京東雲開發者社群 轉載請註明來源