用 Webpack 解決應用性能問題

1. 影響頁面加載時長的 Top3 因素

  1. 頁面初載時,加載大量 JavaScript 腳本;
  2. 頁面初載時,加載大量 CSS 文件;
  3. 頁面初載時,加載大量網絡資源;

頁面加載的越久,頁面不可交互的時間就越長,用戶的體驗就越差。javascript


2. 頁面優化的目標

  1. 頁面初載時,未壓縮的 JavaScript 腳本大小: <=200KB ;
  2. 頁面初載時,未壓縮的 CSS 資源大小: <=100KB ;
  3. HTTP 協議下,請求資源數: <=6 個 ;
  4. HTTP/2 協議下,請求資源數: <=20 個 ;
  5. 90% 的代碼覆蓋率(僅容許 10% 的未使用代碼);

只有 Chrome 可以查看你的代碼覆蓋率。java

聽從這個目標,應用將能在任何平臺(PC,Mobile Phone...)都擁有良好的性能:react

  1. 將來的網絡世界在移動端;
  2. 平均每一個移動端網站須要花費 14 秒時間達到可交互狀態;
  3. 加載的代碼越少,頁面達到可交互的時間越短;

3. 查看代碼覆蓋率

  1. 打開 Chrome Dev Tool;
  2. 按下 Cmd + Shift + P or Ctrl + Shift + P ;
  3. 輸入 Coverage ,選擇第一個出現的選項:

Xnip2019-06-18_10-48-49.jpg

  1. 點擊面板上的 reload 按鈕,查看整個應用 JavaScript 的代碼覆蓋率:

Xnip2019-06-18_10-50-46.jpg


4. 如何作到?

(1)代碼分割(code splitting)

什麼是 code splitting?
將部分代碼在構建時轉變爲異步加載的過程。webpack

(1.1)代碼分割原理

代碼分割的核心是異步加載資源,而異步加載功能使用到 stage 3 規範:whatwg/loader。
import() 容許你在瀏覽器端運行時動態獲取資源,雖然它存在如下一些問題:web

  1. 安全問題;
  2. 瀏覽器支持問題;

(1.2)代碼分割的兩種類型

  1. 靜態的;
  2. 「動態的」(在 webpack 中,並非真的動態):「動態」是指你可以在代碼運行時決定應該引入什麼 JavaScript 模塊;

(1.3)靜態代碼分割

什麼時候使用靜態的代碼分割?
  1. 你正在使用一個很是大的庫或框架 :若是在頁面初始化時你不須要使用它,就不要在頁面初載時加載它;
  2. 任何臨時的資源 :指不在頁面初始化時被使用,被使用後又會當即被銷燬的資源,例如模態框,對話框,tooltip等(任何一開始不顯示在頁面上的東西均可以有條件的加載);
  3. 路由 :既然用戶不會一會兒看到全部頁面,那麼只把當前頁面相關資源給用戶就是個明智的作法;

代碼示例
import Listener from './listeners.js'

const getModal = () => import('./src/modal.js')

Listener.on('didSomethingToWarrentModalBeingLoaded', () => {
    // Async fetching modal code from a separate chunk
  getModal().then((module) => {
    const modalTarget = document.getElementById('Modal')
    module.initModal(modalTarget)
  })
})

注意:json

  1. 在 Vue 中,能夠直接使用 import() 關鍵字,在 React 中,使用 react-loadable 完成一樣的事;
  2. 每當你使用動態代碼分割時,函數將返回一個 Promise 對象;

(1.4)「動態」代碼分割

什麼時候使用「動態」代碼分割?
  1. A/B Test :你不須要在代碼中引入不須要的 UI 代碼;
  2. 主題 :動態加載相應的主題;
  3. 爲了方便 :本質上,你能夠用靜態代碼分割代替「動態」代碼分割,可是後者比前者擁有更少的代碼量;

代碼示例
const getTheme = (themeName) => import(`./src/themes/${themeName}`)

// using `import()` 'dynamically'
if (window.feeling.stylish) {
    getTheme('stylish').then((module) => {
    module.applyTheme()
  })
} else if (window.feeling.trendy) {
    getTheme('trendy').then((module) => {
    module.applyTheme()
  })
}

這背後的原理是,webpack 將目錄中全部能夠分離的 JavaScript 文件都生成了被稱爲 contextModule 的模塊,因此本質上仍然是靜態的。api

(2)魔術註釋

魔術註釋技術是爲代碼分割技術服務的,它表現爲在 import 關鍵字前使用指定註釋來控制 webpack 生成的分割後代碼片斷。瀏覽器

代碼示例
import (
    /* webpackChunkName: "my-chunk-name" */
  './footer'
)

同時,你須要在 webapck.config.js 中添加配置:安全

{
    output: {
    filename: "bundle.js",
    chunkFilename: "[name].lazy-chunk.js"
  }
}

(2.1)webpack Modes

webpack 提供了一些註釋讓咱們可以選擇異步加載的模塊應該被怎樣組織:網絡

import (
    /* webpackChunkName: "my-chunk-name" */
  /* webpackMode: lazy */
  './someModule'
)

webpackMode 的默認值爲 lazy ,指全部異步模塊都會被單獨抽離成單一的 chunk,經過使用 lazy-once 值,能夠將全部的異步加載模塊放在同一個 chunk 中。

(2.2)Prefetch & Preload

經過開啓 prefetch ,咱們能夠經過使用 <link rel="prefetch>" 的特性,讓瀏覽器在空閒時幫咱們預先加載咱們的異步資源,這有助於提高應用性能。

import(
    /* webpackPrefetch: true */
  './someModule'
)

注意 :當你確保你的異步代碼在將來必定會用到時,再開啓該功能。


5. 參考連接

相關文章
相關標籤/搜索