Webpack 4教程 - 第八部分 使用prefetch和preload進行動態載入

葡萄城技術團隊發表於2019-05-16

轉載請註明出處:葡萄城官網,葡萄城為開發者提供專業的開發工具、解決方案和服務,賦能開發者。
原文出處:https://wanago.io/2018/08/13/webpack-4-course-part-seven-decreasing-the-bundle-size-with-tree-shaking/

 

本系列的第一篇文章中,我們討論了匯入(import)和匯出(export)。這次我們深入介紹動態匯入(dynamic import),它值得專門用一篇文章來介紹。我們會介紹動態匯入是什麼以及如何使用它們。開始吧!

在過去,ECMAScript模組是完全靜態的。你必須在執行程式碼之前指明想要匯入和匯出的東西。隨著動態匯入提案的出現,我們有了額外的選擇,即動態地匯入模組。現在它進行到了TC39流程的第三個階段。有了它,你就可以新增動態匯入模組了。使用它時,你可能會根據使用者及其操作行為的做相應處理。比如,你有一個單頁應用,只有當使用者決定開啟它的子頁面時才載入特定程式碼。這樣可以大幅節省應用的初始載入時間。

使用動態匯入

動態匯入操作符是作為函式使用的。它接受一個字串引數,返回一個Promise。當模組載入好後,這個Promise被resolve。

如果你想了解更多關於Promise的內容,可檢視以實現一個排序演算法為例解釋Promise和回撥函式

document.addEventListener("DOMContentLoaded", () => {
  const button = document.querySelector('#divideButton');
  button.addEventListener('click', () => {
    import('./utilities/divide')
      .then(divideModule => {
          console.log(divideModule.divide(6, 3)); // 2
      })
  });
});

在瀏覽器的開發者工具,如果開啟Network標籤,你可以看到,模組開始下載的發生在點選按鈕之後,而不是在此之前。值得注意的是,如果再次點選按鈕,包含了拆分後的模組檔案不會再次被下載。

在Webpack中使用動態匯入,會新增一個chunk,我們視作非同步chunk

非同步chunk在教程的第四部分-使用SplitChunksPlugin分離程式碼中有介紹。

像這樣的chunk會被打包進單獨的檔案。當使用表示式建立指向其檔案的路徑時,你需要小心。考慮如下例子:

let fileName = '';

document.addEventListener("DOMContentLoaded", () => {
  const button = document.querySelector('#divideButton');
  fileName = 'divide';
  button.addEventListener('click', () => {
    import(`./utilities/${fileName}`)
      .then(divideModule => {
        console.log(divideModule.divide(6, 3)); // 2
      })
  });
});

以上程式碼在你的專案中被打包過後,你會發現Webpack在utilities資料夾下為每個模組單獨建立了非同步chunk。這是因為Webpack不能在編譯時知道哪些模組需要被匯入。

你還需要知道像import(pathToFile)這樣的完全的動態宣告是不起作用的,因為Webpack至少需要一部分檔案路徑資訊。這是因為pathToFile可以是你工程中任何檔案的路徑,而Webpack會為每個模組在給定的資料夾中建立非同步chunk。你可以自定義此行為,我們下面就會這麼做。

使用在Webpack中使用魔法註釋

匯入模組的規範不允許你在匯入時使用除了檔名以外的引數。幸運的是,有了Webpack,你可以利用所謂的**魔法註釋(magic comments)**來使用附加引數。

webpackInclude 和 webpackExclude

在之前的小節,我們提到Webpack會為每個模組在我們給定的資料夾中建立非同步chunk。雖然這是預設行為,但它可以修改。

其中一種方法是使用webpackExclude,它是一個正規表示式,用以匹配潛在的可被匯入的檔案。任何匹配到的檔案都不會被打包進來。  

import(
  `./utilities/${fileName}`
  /* webpackExclude: /subtract.js$/ */
)

以上程式碼表示,檔案 subtract.js 檔案不會被打包進來,即使它在 utilities 目錄下。

與之相反的一個引數叫做webpackInclude。使用它時,只有匹配了正規表示式的模組會被打包。

webpackMode

webpackMode屬性定義了resolve動態模組時的模式。支援以下模式:

lazy

這是預設模式。它為每個動態匯入的模組建立非同步chunk

lazy-once

使用它,會為滿足匯入條件的所有模組建立單一的非同步chunk

import(
  `./utilities/${fileName}`
  /* webpackMode: "lazy-once" */
)
  .then(divideModule => {
    console.log(divideModule.divide(6, 3)); // 2
  })

以上程式碼表示,Webpack會為所有 utilities 目錄下的所有模組共同建立一個非同步chunk。它會導致使用者以一個檔案下載所有的模組。

eager

此模式會阻止Webpack生成額外的chunk。所有匯入的模組被包含在當前chunk,所以不需要再發額外的網路請求。它仍然返回一個Promise,但它被自動resolve。使用eager模式的動態匯入與靜態匯入的區別在於,整個模組只有當**import()**掉用之後才執行。

weak

徹底阻止額外的網路請求。只有當該模組已在其他地方被載入過了之後,Promise才被resolve,否則直接被reject。

webpackChunkName

它是新chunk的名字,可以和[index]、[request]變數一起使用。

[index]在當前動態匯入宣告中表示檔案的索引。另一方面,[request]表示匯入檔案的動態部分。

import(
  `./utilities/${fileName}`
  /* webpackChunkName: "utilities-[index]-[request]" */
)

以上程式碼可能生成例如 utilities-0-divide.js 這樣的檔名。

請注意,如果在某些情況下,確定只有一個非同步chunk(比如本來就沒有動態生成路徑,或者使用了lazy-once模式),[index]和[request]就不會被使用了。

使用預先拉取和預先載入提升效能

Webpack 4.6.0為我們提供了預先拉取(prefetching)和預先載入(preloading)的功能。使用這些宣告可以修改瀏覽器處理非同步chunk的方式。

預先拉取

使用預先拉取,你表示該模組可能以後會用到。瀏覽器會在空閒時間下載該模組,且下載是發生在父級chunk載入完成之後。  

import(
  `./utilities/divide`
  /* webpackPrefetch: true */
  /* webpackChunkName: "utilities" */
)

以上的匯入會讓<link rel="prefetch" as="script" href="utilities.js">被新增至頁面的頭部。因此瀏覽器會在空閒時間預先拉取該檔案。

預先載入

在資源上新增預先載入的註釋,你指明該模組需要立即被使用。非同步chunk會和父級chunk並行載入。如果父級chunk先下載好,頁面就已可顯示了,同時等待非同步chunk的下載。這能大幅提升效能。  

import(
  `./utilities/divide`
  /* webpackPreload: true */
  /* webpackChunkName: "utilities" */
)

以上程式碼的效果是讓<link rel="preload" as="script" href="utilities.js">起作用。不當地使用wepbackPreload會損害效能,所以使用的時候要小心。

總結

這次我們學習瞭如何使用動態匯入提升應用的效能。它們能顯著減少頁面的初次載入時間。使用可傳入Webpack的額外引數,你可以更進一步地定製它,並且新增上對預先拉取和預先載入的支援。所有這些,都會優化你的使用者體驗,讓你的網站更加靈動。

 

相關文章