1. 程式人生 > >前端專案優化 -Web 開發常用優化方案、Vue & React 專案優化

前端專案優化 -Web 開發常用優化方案、Vue & React 專案優化

github

github-myBlob

從輸入URL到頁面載入完成的整個過程


  1. 首先做 DNS 查詢,如果這一步做了智慧 DNS 解析的話,會提供訪問速度最快的 IP 地址回來
  2. 接下來是 TCP 握手,應用層會下發資料給傳輸層,這裡 TCP 協議會指明兩端的埠號,然後下發給網路層。網路層中的 IP 協議會確定 IP 地址,並且指示了資料傳輸中如何跳轉路由器。然後包會再被封裝到資料鏈路層的資料幀結構中,最後就是物理層面的傳輸了
  3. TCP 握手結束後會進行 TLS 握手,然後就開始正式的傳輸資料(如果使用HTTPS)
  4. 資料在進入服務端之前,可能還會先經過負責負載均衡的伺服器,它的作用就是將請求合理的分發到多臺伺服器上,這時假設服務端會響應一個 HTML 檔案
  5. 首先瀏覽器會判斷狀態碼是什麼,如果是 200 那就繼續解析,如果 400 或 500 的話就會報錯,如果 300 的話會進行重定向,這裡會有個重定向計數器,避免過多次的重定向,超過次數也會報錯
  6. 瀏覽器開始解析檔案,如果是 gzip 格式的話會先解壓一下,然後通過檔案的編碼格式知道該如何去解碼檔案
  7. 檔案解碼成功後會正式開始渲染流程,先會根據 HTML 構建 DOM 樹,有 CSS 的話會去構建 CSSOM 樹。如果遇到script標籤的話,會判斷是否存在async或者defer,前者會並行進行下載並執行 JS,後者會先下載檔案,然後等待 HTML 解析完成後順序執行,如果以上都沒有,就會阻塞住渲染流程直到 JS 執行完畢。遇到檔案下載的會去下載檔案,這裡如果使用 HTTP 2.0 協議的話會極大的提高多圖的下載效率。
  8. 初始的 HTML 被完全載入和解析後會觸發DOMContentLoaded事件
  9. CSSOM 樹和 DOM 樹構建完成後會開始生成 Render 樹,這一步就是確定頁面元素的佈局、樣式等等諸多方面的東西
  10. 在生成 Render 樹的過程中,瀏覽器就開始呼叫 GPU 繪製,合成圖層,將內容顯示在螢幕上了
  11. 沒有要傳輸的檔案了,斷開TCP連線 4 次揮手

效能優化分析


根據上面的過程可以看到,頁面的載入過程主要分為下載、解析、渲染三個步驟,整體可以從兩個角度來考慮:

  • 網頁的資源請求與載入階段
  • 網頁渲染階段

網頁的資源請求與載入階段

我們可以開啟 Chrome 的除錯工具來分析此階段的效能指標


在建立 TCP 連線的階段(HTTP 協議是建立在 TCP 協議之上的)

  • Queuing 和 Stalled 表示請求佇列以及請求等待的時間
  • DNS Lookup 表示執行 DNS 查詢所用的時間。頁面上的每一個新域都需要完整的往返才能執行DNS查詢
  • Initila connection 和 SSL 包括 TCP 握手重試和協商 SSL 以及 SSL 握手的時間。

在請求響應的階段

  • Request sent 是發出網路請求所用的時間,通常不會超過 1ms
  • Watiting(TTFB) 是等待初始響應所用的時間,也稱為等待返回首個位元組的時間,該時間將捕捉到伺服器往返的延遲時間,以及等待伺服器傳送響應所用的時間。
  • Content Download 則是從伺服器上接收資料的時間。

資源請求階段優化方案

依據上面的指標給出以下幾點優化方案(僅供參考)

1、劃分子域

條件:擁有多個域名
Chrome 瀏覽器只允許每個源擁有 6 個 TCP 連線,因此可以通過劃分子域的方式,將多個資源分佈在不同子域上用來減少請求佇列的等待時間。然而,劃分子域並不是一勞永逸的方式,多個子域意味著更多的 DNS 查詢時間。通常劃分為 3 到 5 個比較合適。
對如何拆分資源有如下建議:

  • 前端類:把專案業務本身的 html、css、js、圖示等歸為一類
  • 靜態類:CDN 資源
  • 動態類:後端 API

2、DNS 預解析

DNS 解析也是需要時間的,可以通過預解析的方式來預先獲得域名所對應的 IP,方法是在 head 標籤裡寫上幾個 link 標籤

<link rel="dns-prefetch" href="https://www.google.com">
<link rel="dns-prefetch" href="https://www.google-analytics.com">

對以上幾個網站提前解析,這個過程是並行的,不會阻塞頁面渲染。

3、預載入

在開發中,可能會遇到這樣的情況。有些資源不需要馬上用到,但是希望儘早獲取,這時候就可以使用預載入。
預載入其實是宣告式的 fetch,強制瀏覽器請求資源,並且不會阻塞 onload 事件,可以使用以下程式碼開啟預載入:

<link rel="preload" href="http://example.com">

預載入可以一定程度上降低首屏的載入時間,因為可以將一些不影響首屏但重要的檔案延後載入,唯一缺點就是相容性不好。

4、保持持久連線

HTTP 是一個無狀態的面向連線的協議,即每個 HTTP 請求都是獨立的。然而無狀態並不代表 HTTP 不能保持 TCP 連線,Keep-Alive 正是 HTTP 協議中保持 TCP 連線非常重要的一個屬性。 HTTP1.1 協議中,Keep-Alive 預設開啟,使得通訊雙方在完成一次通訊後仍然保持一定時長的連線,因此瀏覽器可以在一個單獨的連線上進行多個請求,有效地降低建立 TCP 請求所消耗的時間。

5、CND 加速

使用 CND 加速可以減少客戶端到伺服器的網路距離。

  • CDN 的意圖就是儘可能地減少資源在轉發、傳輸、鏈路抖動等情況下順利保障資訊的連貫性;
  • CDN 系統能夠實時地根據網路流量和各節點的連線、負載狀況以及到使用者的距離和響應時間等綜合資訊將使用者的請求重新導向離使用者最近的服務節點上
  • CDN 採用各節點快取的機制,當我們專案的靜態資源修改後,如果 CDN 快取沒有做相應更新,則看到的還是舊的網頁,解決的辦法是重新整理快取,七牛雲、騰訊雲都可單獨針對某個檔案/目錄進行重新整理;
  • CDN 快取需要合理地使用:圖片、常用 js 元件、css 重置樣式等,即不常改動的檔案可走 CDN,包括專案內的一些介紹頁;

還有一種比較流行的做法是讓一些專案依賴走 CDN,比如 vuex、vue-router 這些外掛通過外鏈的形式來引入,因為它們都有自己免費的 CDN,這樣可以減少打包後的檔案體積。

6、設定快取

快取對於前端效能優化來說是個很重要的點,良好的快取策略可以降低資源的重複載入提高網頁的整體載入速度。
通常瀏覽器快取策略分為兩種:強快取 和 協商快取。

  • 強快取:實現強快取可以通過兩種響應頭實現:ExpiresCache-Control強快取表示在快取期間不需要向伺服器傳送請求
  • 協商快取:快取過期了就是用協商快取,其通過Last-Modified/If-Modified-SinceETag/If-None-Match實現

HTTP 頭中與快取相關的屬性,主要有以下幾個:

(1) Expires: 指定快取過期的時間,是一個絕對時間,但受客戶端和服務端時鐘和時區差異的影響,是 HTTP/1.0 的產物
形如Expires: Wed, 22 Oct 2018 08:41:00 GMT

(2) Cache-Control:比 Expires 策略更詳細,max-age 優先順序比 Expires 高,其值可以是以下五種情況

  1. no-cache: 強制所有快取了該響應的快取使用者,在使用已儲存的快取資料前,傳送請求到原始伺服器(進行過期認證),通常情況下,過期認證需要配合 etag 和 Last-Modified 進行一個比較
  2. no-store: 告訴客戶端不要響應快取(禁止使用快取,每一次都重新請求資料)
  3. public: 快取響應,並可以在多使用者間共享(與中間代理伺服器相關)
  4. private: 快取響應,但不能在多使用者間共享(與中間代理伺服器相關)
  5. max-age: 快取在指定時間(單位為秒)後過期

(3) Last-Modified / If-Modified-Since: Last-Modified表示本地檔案最後修改日期,If-Modified-Since會將上次從伺服器獲取的Last-Modified的值傳送給伺服器,詢問伺服器在該日期後資源是否有更新,有更新的話就會將新的資源傳送回來。
但是如果(伺服器)在本地開啟快取檔案(或者刪了個字元 a 後又填上去),就會造成Last-Modified被修改,所以在 HTTP / 1.1 出現了ETag

(4) Etag / If-None-Match: ETag類似於檔案指紋,If-None-Match會將當前ETag傳送給伺服器,詢問該資源ETag是否變動,有變動的話就將新的資源傳送回來。並且ETag優先順序比Last-Modified高。
由於 etag 要使用少數的字元表示一個不定大小的檔案(如 etag: "58c4e2a1-f7"),所以 etag 是有重合的風險的,如果網站的資訊特別重要,連很小的概率如百萬分之一都不允許,那麼就不要使用 etag 了。使用 etag 的代價是增加了伺服器的計算負擔,特別是當檔案比較大時。

選擇合適的快取策略
對於大部分的場景都可以使用強快取配合協商快取解決,但是在一些特殊的地方可能需要選擇特殊的快取策略

  • 對於某些不需要快取的資源,可以使用Cache-control: no-store,表示該資源不需要快取
  • 對於頻繁變動的資源,可以使用Cache-Control: no-cache並配合ETag使用,表示該資源已被快取,但是每次都會發送請求詢問資源是否更新。
  • 對於程式碼檔案來說,通常使用Cache-Control: max-age=31536000並配合策略快取使用,然後對檔案進行指紋處理,一旦檔名變動就會立刻下載新的檔案。

7、使用 HTTP / 2.0

因為瀏覽器會有併發請求限制,在 HTTP / 1.1 時代,每個請求都需要建立和斷開,消耗了好幾個 RTT 時間,並且由於 TCP 慢啟動的原因,載入體積大的檔案會需要更多的時間。
在 HTTP / 2.0 中引入了多路複用,能夠讓多個請求使用同一個 TCP 連結,極大的加快了網頁的載入速度。並且還支援 Header 壓縮,進一步的減少了請求的資料大小。

8、圖片和檔案壓縮

這又涉及到很多知識點了,簡單來說,我們要儘可能地在保證我們的 App 能正常執行、圖片儘可能保證高質量的前提下去壓縮所有用到的檔案的體積。比如圖片格式的選擇、去掉我們程式碼中的註釋、空行、無關程式碼等。
圖片相關優化

  • 不用圖片。很多時候會使用到很多修飾類圖片,其實這類修飾圖片完全可以用 CSS 去代替。
  • 對於移動端來說,螢幕寬度就那麼點,完全沒有必要去載入原圖浪費頻寬。一般圖片都用 CDN 載入,可以計算出適配螢幕的寬度,然後去請求相應裁剪好的圖片。
  • 小圖使用 base64 格式
  • 選擇正確的圖片格式:
    • 對於能夠顯示 WebP 格式的瀏覽器儘量使用 WebP 格式。因為 WebP 格式具有更好的影象資料壓縮演算法,能帶來更小的圖片體積,而且擁有肉眼識別無差異的影象質量,缺點就是相容性並不好
    • 小圖使用 PNG,其實對於大部分圖示這類圖片,完全可以使用 SVG 代替
    • 照片使用 JPEG

構建工具的使用

  • 對於 Webpack4,打包專案使用 production 模式,這樣會自動開啟程式碼壓縮
  • 使用 ES6 模組來開啟 tree shaking,這個技術可以移除沒有使用的程式碼
  • 優化圖片,對於小圖可以使用 base64 的方式寫入檔案中
  • 按照路由拆分程式碼,實現按需載入
  • 給打包出來的檔名新增雜湊,實現瀏覽器快取檔案(能及時更新)
  • 啟用 gzip 壓縮(需要前後端支援)
  • 各種 loader/plugin 的使用

壓縮 HTML 檔案
可以把 HTML 的註釋去掉,把行前縮排刪掉,這樣處理的檔案可以明顯減少 HTML 的體積;這樣做幾乎是沒有風險的,除了 pre 標籤不能夠去掉行首縮排之外,其他的都正常。

網頁渲染階段優化方案

1、<script>標籤位置

渲染執行緒和 JS 引擎執行緒是互斥的,如果你想首屏渲染的越快,就越不應該在首屏就載入 JS 檔案,因此建議將 script 標籤放在 body 標籤底部的原因。或者使用給 script 標籤新增 defer 或者 async 屬性 。

  • defer 表示該檔案會並行下載,但是會放到 HTML 解析完成後再順序執行;
  • 對於沒有任何依賴的 JS 檔案可以加上 async 屬性,表示載入和渲染後續文件元素的過程將和 JS 檔案的載入與執行並行無序進行

2、Webworker 的使用

執行 JS 程式碼過長會卡住渲染,對於需要很多時間計算的程式碼可以考慮使用 Webworker。Webworker 可以讓我們另開一個執行緒執行指令碼(這並沒有改變 JS 單執行緒的本質,因為新開的執行緒受控於主執行緒且不得操作 DOM)而不影響渲染。

3、懶載入

懶載入就是將不關鍵的資源延後載入。
懶載入的原理就是隻載入自定義區域(通常是可視區域,但也可以是即將進入可視區域)內需要載入的東西。對於圖片來說,先設定圖片標籤的src屬性為一張佔位圖,將真實的圖片資源放入一個自定義屬性中,當進入自定義區域時,就將自定義屬性替換為src屬性,這樣圖片就會去下載資源,實現了圖片懶載入。
懶載入不僅可以用於圖片,也可以使用在別的資源上。比如進入可視區域才開始播放視訊等等。

4、預載入

  • 圖片等靜態資源在使用之前的提前請求
  • 資源使用到時能從快取中載入,提升使用者體驗
  • 頁面展示的依賴關係維護

使用場景比如抽獎動畫展示過程中預先載入其他內容,或者電子書閱讀章節的預載入可以使切換下一章節時更為流暢。

5、減少迴流與重繪

執行 JavaScript 的解析和 UI 渲染的兩個瀏覽器執行緒是互斥的,UI 渲染時 JS 程式碼解析終止,反之亦然。
當 頁面佈局和幾何屬性 改變時,就會觸發 迴流。
當 需要更新的只是元素的某些外觀 時,就會觸發 重繪。

  • 用 translate 替代 top 屬性:top 會觸發 reflow,但 translate 不會
  • 不要一條一條地修改 DOM 的樣式,預先定義好 class,然後修改 DOM 的 className
  • 把 DOM 離線後修改,比如:先把 DOM 給 display:none(有一次 reflow),然後你修改 100 次,然後再把它顯示出來
  • 不要把 DOM 節點的屬性值放在一個迴圈裡當成迴圈的變數
  • offsetHeight、offsetWidth 每次都要重新整理緩衝區,緩衝機制被破壞,先用變數儲存下來
  • 不要使用 table 佈局,可能很小的一個小改動會造成整個 table 的重新佈局
  • 動畫實現的速度的選擇:選擇合適的動畫速度
  • 啟用 gpu 硬體加速(並行運算),gpu 加速意味著資料需要從 cpu 走匯流排到 gpu 傳輸,需要考慮傳輸損耗.
    • transform:translateZ(0)
    • transform:translate3D(0)
    • 似乎現在瀏覽器能智慧地分析 gpu 加速了?

6、編寫高效率的 CSS

使用 CSS 前處理器時注意不要有過多的巢狀,巢狀層次過深會影響瀏覽器查詢選擇器的速度,且一定程度上會產生出很多冗餘的位元組。

7、減少 DOM 元素數量、減少 DOM 的操作

減少 DOM 元素數量,合理利用 :after、:before 等偽類,避免頁面過深的層級巢狀;
優化 JavaScript 效能,減少 DOM 操作次數(或集中操作),能有效規避頁面重繪/重排;
只能說盡可能去做優化,如資料分頁、首屏直出、按需載入等

8、函式節流

為觸發頻率較高的函式使用函式節流

其他

SPA SEO SSR

SPA:單頁面富應用
動態地重寫頁面的部分與使用者互動而不是載入新的頁面。
優點:① 前後端分離 ② 頁面之間切換快 ③ 後端只需提供 API
缺點:① 首屏速度慢,因為使用者首次載入 SPA 框架及應用程式的程式碼然後才渲染頁面 ② 不利於 SEO
SEO(Search Engine Optimization):搜尋引擎優化
常用技術:利用 <title> 標籤和 <meta> 標籤的 description

<html>
<head>
<title>標題內容</title>
  <meta name="description" content="描述內容">
  <meta name="keyword" content="關鍵字1,關鍵字2,—">
  </head>
</html>

SPA 應用中,通常通過 AJAX 獲取資料,而這裡就難以保證我們的頁面能被搜尋引擎正常收錄到。並且有一些搜尋引擎不支援執行 JS 和通過 AJAX 獲取資料,那就更不用提 SEO 了。
對於有些網站而言,SEO 顯得至關重要,例如主要以內容輸出為主的 Quora、stackoverflow、知乎和豆瓣等等,那如何才能正常使用 SPA 而又不影響 SEO 呢 ?所以有了 SSR
SSR(Server-Side Rendering):服務端渲染

以下內容部分參考《深入淺出 React 與 Redux》- 程墨

為了量化網頁效能,我們定義兩個指標:

  • TTFP(Time To First Paint):指的是從網頁 HTTP 請求發出,到使用者可以看到第一個有意義的內容渲染出來的時間差
  • TTI(Time To Interactive):指的是從網頁 HTTP 請求發出,到使用者可以對網頁內容進行互動的時間

在一個 完全靠瀏覽器端渲染 的應用中,當用戶在瀏覽器中開啟一個頁面的時候,最壞情況下沒有任何快取,需要等待三個 HTTP 請求才能到達 TTFP 的時間點:

  • 向伺服器獲取 HTML,這個 HTML 只是一個無內容的空架子,但是皮之不存毛將焉附,這個 HTML 就是皮,在其中執行的 JavaScript 就是毛,所以這個請求時不可省略的
  • 獲取 JavaScript 檔案,大部分情況下,如果這是瀏覽器第二次訪問這個網站,就可以直接讀取快取,不會發出真正的 HTTP 請求
  • 訪問 API 伺服器獲取資料,得到的資料將由 JavaScript 加工之後用來填充 DOM 樹,如果應用的是 React,那就是通過修改元件的狀態或者屬性來驅動渲染

而對於伺服器端渲染,因為獲取 HTTP 請求之後就會返回所有有內容的 HTML,所以在一個 HTTP 的週期之後就會提供給瀏覽器有意義的內容,所以首次渲染時間 TTFP 會優於完全依賴於瀏覽器端渲染的頁面。

除了更短的 TTFP,伺服器端渲染還有一個好處就是利於搜尋引擎優化,雖然某些搜尋引擎已經能夠索引瀏覽器端渲染的網頁,但是畢竟不是所有搜尋引擎都能做到這一點,讓搜尋引擎能夠索引到應用頁面的最直接方法就是提供完整 HTML

上面的效能對比只是理論上的分析,實際上,採用伺服器端渲染是否能獲得更好的 TTFP 有多方面因素。
1、伺服器端產生的 HTML 過大是否會影響效能?
因為伺服器端渲染返回的是完整的 HTML,那麼下載這個 HTML 的時間也會增長。
2、伺服器端渲染的運算消耗是否是伺服器能夠承擔得起的?
瀏覽器端渲染的方案下,伺服器只提供靜態資源,壓力被分攤到了訪問使用者的瀏覽器中;如果使用伺服器端渲染,每個頁面請求都要產生 HTML 頁面,這樣伺服器的壓力就會很大。

React 並不是給伺服器端渲染設計的,如果應用對 TTFP 要求不高,也不希望對 React 頁面進行搜尋引擎優化,那麼沒有必要使用“同構”來增加開發難度;如果希望應用的效能能更進一步,而且伺服器運算資源充足,那麼可以嘗試。對 Vue 而言應該也是同樣的道理。


最後我們來總結下服務端渲染理論上的優缺點:
優點:

  • 更快的響應時間、首屏載入時間,可以將 SEO 的關鍵資訊直接在後臺渲染成 HTML,從而保證搜尋引擎的爬蟲都能爬到關鍵資料
  • 更快的內容到達時間,特別是對於緩慢的網路情況或執行緩慢的裝置
  • 無需等待所有的 JavaScript 都完成下載並執行,才顯示伺服器渲染的標記,所以使用者將會更快速地看到完整渲染的頁面,通常可以產生更好的使用者體驗
  • 資原始檔從本地請求(各種 bundle 什麼的),更快的下載速度

缺點:

  • 佔用伺服器更多的 CPU 和記憶體資源
  • 一些常用的瀏覽器 API 可能無法使用,如 window、document、alert 等,如果需要使用的話需要對執行的環境加以判斷
  • 開發難度加大

Vue 專案優化點

1、第三方庫走 cdn
例如:

<script src="//cdn.bootcss.com/vue/2.2.5/vue.min.js"></script>
<script src="//cdn.bootcss.com/vue-router/2.3.0/vue-router.min.js"></script>
<script src="//cdn.bootcss.com/vuex/2.2.1/vuex.min.js"></script>
<script src="//cdn.bootcss.com/axios/0.15.3/axios.min.js"></script>

在 webpack 裡有個 externals 選項,可以忽略不需要打包的庫
https://webpack.js.org/configuration/externals/#root

const path = require('path')

module.exports = {
  mode: 'production',
  entry: './src/index.js',
  externals: {
    'vue': 'Vue',
    'vue-router': 'VueRouter',
    'vuex': 'Vuex',
    'axios': 'axios'
  },
  output: {
    ...
  }
}

2、路由懶載入

import Vue from 'vue'
import Router from 'vue-router'

Vue.use(Router)

export default new Router({
  routes: [
    {
      path: '/ebook',
      component: () => import('./views/ebook/index.vue'), // 路由懶載入,這裡用的是ES6的語法  import()函式是動態載入 import 是靜態載入
      children: [ // 動態路由, 可以傳遞路徑引數
        {
          path: ':fileName',
          component: () => import('./components/ebook/EbookReader.vue')
        }
      ]
    },
    {
      path: '/store',
      component: () => import('./views/store/index.vue'),
      redirect: '/store/shelf', // #/store -> #/store/home
      ...
    }
  ]
})

3、使用懶載入外掛 Vue-Loader
具體的使用可以參考 這篇文章 或者去看官方文件
step1:cnpm install vue-lazyload --save
step2:main.js匯入

import VueLazyLoad from 'vue-lazyload'
Vue.use(VueLazyload)

step3:<img class="item-pic" v-lazy="newItem.picUrl"/>vue 檔案中將需要懶載入的圖片繫結v-bind:src修改為v-lazy
這只是圖片懶載入,還有很多其他可選配置

4、v-ifv-show的選擇
一般來說,v-if有更高的切換開銷,而v-show有更高的初始渲染開銷。因此,如果需要非常頻繁地切換,則使用v-show較好;如果在執行時條件很少改變,則使用v-if較好。

React 專案優化點

1、單個元件的優化:更改 shouldComponentUpdate 函式的預設實現,根據每個 React 元件的內在邏輯定製其行為,減少不必要的重新渲染

shouldComponentUpdate(nextProps, nextState) {
  // 假設影響渲染內容的 prop 只有 completed 和 text,只需要確保
  // 這兩個 prop 沒有變化,函式就可以返回 false
  return (nextProps.completed !== this.props.completed) ||
    (nextProps.text !== this.props.text)
}

2、使用 immutable.js 解決複雜資料 diff、clone 等問題。
immutable.js 實現原理:持久化資料結構,也就是使用舊資料建立新資料時,要保證舊資料同時可用且不變。同時為了避免 deepCopy 把所有節點都複製一遍帶來的效能損耗,Immutable 使用了結構共享,即如果物件樹中一個節點發生變化,只修改這個節點和受它影響的父節點,其它節點則進行共享。

3、在 constructor() 裡做 this 繫結
當在 render() 裡使用事件處理方法時,提前在建構函式裡把 this 繫結上去(如果需要的話),因為在每次 render 過程中, 再呼叫 bind 都會新建一個新的函式,浪費資源.

// bad
class App extends React.Component {
  onClickDiv() {
    // do stuff
  }

  render() {
    return <div onClick={this.onClickDiv.bind(this)} />;
  }
}

// good
class App extends React.Component {
  constructor(props) {
    super(props);

    this.onClickDiv = this.onClickDiv.bind(this);
  }

  onClickDiv() {
    // do stuff
  }

  render() {
    return <div onClick={this.onClickDiv} />;
  }
}

4、基於路由的程式碼分割
使用React.lazyReact Router來配置基於路由的程式碼分割

import { BrowserRouter as Router, Route, Switch } from 'react-router-dom';
import React, { Suspense, lazy } from 'react';

const Home = lazy(() => import('./routes/Home'));
const About = lazy(() => import('./routes/About'));

const App = () => (
  <Router>
    <Suspense fallback={<div>Loading...</div>}>
      <Switch>
        <Route exact path="/" component={Home}/>
        <Route path="/about" component={About}/>
      </Switch>
    </Suspense>
  </Router>
);

參考資料

https://juejin.im/post/5c4a6fcd518825469414e062#heading-29
https://juejin.im/post/5cab64ce5188251b19486041#heading-6
https://juejin.im/post/5d548b83f265da03ab42471d
https://www.jianshu.com/p/333f390f2e84
https://yuchengkai.cn/docs/fronte