webpack二刷之二、loader資源模塊加載器

loader資源模塊加載器

webpack 資源模塊加載

webpack內部(內部loader)默認只會處理javascript文件,也就是說它會把打包過程中所有遇到的文件當作javascript文件去解析。

可以爲其他類型的文件添加不同的加載器(loader)。

loader

loader是webpack實現整個前端模塊化的核心,藉助於loader就可以加載任何類型的資源。

安裝loader,然後在配置文件中的module屬性下配置rules數組。

rules 是針對其他資源模塊的加載規則的配置。

每個規則對象,都需要設置兩個屬性:

  • test:一個正則表達式,用於匹配打包過程中遇到的文件路徑
  • use:用於指定匹配到的文件需要使用的loader
    • 可以配置loader模塊名稱,也可以配置loader相對路徑,原理同require函數
      • use: 'css-loader'使用npm上的loader模塊
      • use: './xxx-loader'使用自定義的.js文件的相對路徑
    • 如何配置了多個loader(數組),執行順序是從後向前

一些插件介紹:

  1. css-loader
    1. 將css文件轉換稱一個js模塊,具體實現是:
      1. 將css代碼push到一個數組當中
    2. 但是整個過程中,並沒有使用到這個數組
    3. 還需要一個style-loader去使用它
  2. style-loader
    1. 將css-loader轉換後的結果,通過style標籤的形式追加到頁面上
  3. html-loader
    1. 使webpack識別.html的模塊

webpack 導入資源模塊

雖然可以通過loader將任何類型的資源作爲入口去打包。

但webpack一般還是將javascript作爲打包入口。

打包入口可以說是項目的運行入口。

webpack建議:

  1. 編寫代碼過程中,根據代碼的需要,動態導入資源
  2. 需要資源的不是應用,而是代碼

目前而言,javascript代碼負責完成整個應用的業務功能。javascript驅動前端應用的業務。

在實現業務的過程中,需要其他類型的資源(例如樣式、圖片)。

webpack建立javascript和資源的依賴關係的目的:

  1. 邏輯合理,JS確實需要這些資源文件
  2. 確保上線資源不缺失,都是必要的

webpack 文件資源加載器

大部分loader都類似css-loader,都是將資源模塊轉換爲JS代碼的實現方式。

還有一部分(例如 圖片 字體)不能通過JS的方式去表示的資源,需要用到文件資源加載器,例如file-loader

webpack默認將輸出目錄作爲網站的根目錄,所以資源的路徑默認以dist爲根目錄。

通過配置publicPath,告訴webpack打包的文件在網站中的位置,默認爲空''及網站根目錄。

例如:publicPath: 'dist/',注意/不能省略。

publicPath即打包後文件中webpack使用的變量__webpack_require__.p

使用圖片時,它拼接在圖片路徑前,即__webpack_require__.p + imgSrc,所以/不能省略。

總結

webpack 在打包時遇到圖片等文件,根據配置匹配對應的文件加載器,先將文件拷貝到輸出目錄,然後將輸出的文件的路徑,作爲返回值返回,從而可以通過import拿到訪問這個文件的路徑。

webpack URL 加載器

除了file-loader 這種通過拷貝文件的方式去處理文件資源以外,還有一種通過 Data URLs 表示文件的常見方式。

Data URLs

傳統的URL一般要求服務器有一個對應的文件,然後通過請求這個地址得到服務器上的這個文件。

Data URLs 是特殊的URL協議,它可以直接表示一個文件的內容,即url中的文本已經包含了文件的內容。

所以使用Data URLs時就不會再發送HTTP請求。

data:[<mediatype>][;base64],<data>

  • data: 協議
  • [<mediatype>][;base64], 媒體類型和編碼
  • <data> 文件內容

例如:data:text/html;charset=UTF-8,<h1>html content</h1> 表示一個編碼爲UTF-8,內容爲<h1>html content</h1>的html內容。

可以通過瀏覽器打開這個地址查看效果。

而如果是圖片或字體這種無法直接通過文本去表示的二進制類型的文件,可以通過將文件的內容進行base64編碼,然後以編碼後的字符串去表示文件的內容。

例如:data:image/png;base64,iVBORw0KGg...

表示base64編碼的png類型的文件。

通過Data URLs可以表示任意類型的文件。

url-loader

file-loader通過拷貝的方式打包文件,最終返回輸出文件的路徑。

url-loader將文件轉換爲Data URLs,最終返回一個完整的 Data URLs類型的url地址,不會輸出獨立的物理文件。

最佳實踐

  • 小文件使用Data URLs,減少請求次數
  • 大文件單獨提取存放(傳統方式),提高加載速度(Data URLs表示大文件內容過大)

每個loader加載器都有options配置選項。

通過配置url-loaderlimit(字節上限)實現最佳實踐。

  • 超出limit的文件單獨提取存放(調用file-loader加載器)
  • 小於limit的文件轉換爲Data URLs嵌入代碼

注意:如果url-loader配置了limit,大文件是使用file-loader加載器處理的,所以需要安裝它依賴的file-loader

常用加載器分類

  • 編譯轉換類
    • 把加載到的模塊,轉換爲javascript代碼
    • 例如 css-loader
  • 文件操作類
    • 把加載到的資源模塊,拷貝到輸出的目錄
    • 同時導出文件的訪問路徑
    • 例如 file-loader
  • 代碼檢查類
    • 對代碼加載的文件中的代碼進行校驗
    • 目的:統一代碼風格,從而提高代碼質量
    • 一般不會修改生產環境的代碼
    • 例如 eslint-loader

webpack 處理 ES2015

webpack默認就能處理代碼當中的 importexport,但這不表示webpack會自動編譯ES6的代碼。

因爲模塊打包需要,所以webpack對代碼中的importexport作了相應的轉換。

webpack並不能轉換代碼中其他的ES6特性。

如果需要webpack處理代碼中其他ES6特性的轉換,就需要爲JS文件配置一個額外的編譯類型的loader,例如常見的babel-loader

babel-loader依賴額外的babel的核心模塊@babel/core

另外可以安裝一個babel的插件集合(預設)@babel/preset-env

babel只是轉換JS代碼的一個平臺,我們需要基於這個平臺,通過不同的插件去轉換代碼中具體的特性。

所以需要配置babel要使用的插件。

  • webpack只是打包工具,不會處理代碼中的ES6的新特性
  • 通過配置加載器實現編譯轉換代碼

webpack 模塊加載方式

除了代碼中的import可以觸發模塊的加載,webpack還提供了其他幾種方式:

  • 遵循 ES Modules 標準的import聲明
  • 遵循 CommonJS 標準的require函數
    • 注:如果require一個 ESM 的模塊,需要通過require(<path>).default 獲取 ESM 模塊的默認屬性
  • 遵循 AMD 標準的define函數和require函數

建議:除非必要情況,否則不要再項目中混合使用這幾種標準。

webpack除了以上3個方式,loader加載的非javascript也會觸發資源加載。(一些獨立的加載器,在工作時也會處理所加載到的資源當中導入的模塊)

例如

  • css-loader加載的css文件(樣式代碼中@import指令和url函數)
  • html代碼的圖片標籤的src屬性,a標籤的href屬性
    • 使用html-loader

html-loader默認只會處理html中的src屬性,如果要實現其他標籤的屬性也能觸發webpack打包,需要爲加載器添加一些相應配置。

代碼中所有引用到的資源(有引用資源可能性的地方)都會被webpack找到,然後根據配置交給對應的loader去處理,最後將處理的結果整體打包到輸出目錄。

webpack就是依據這樣的特點,去實現整個項目的模塊化。

webpack 核心工作原理

在項目中一般都會散落着各種各樣的代碼及資源文件(.js .html .css .png .json .scss…)。

webpack會根據配置找到其中的一個文件作爲打包入口(entry),一般是一個javascript文件。

然後它會順着入口文件的代碼,根據代碼中出現的importrequire之類的語句,解析推斷出這個資源所依賴的模塊。

然後分別再解析每個模塊對應的依賴。

最後形成了整個項目中,所有用到的文件之間的依賴關係的依賴樹。

webpack會遞歸這個依賴樹,找到每個節點對應的資源文件。

最後根據配置文件中的rules屬性,找到模塊對應的加載器去加載這個模塊。

最後會將加載到的結果,放到打包文件bundle.js中,從而實現整個項目的打包。

Loader機制是webpack的核心。
如果沒有Loader,webpack就沒有辦法實現各種資源文件的加載,而只是打包合併JS代碼的工具。

webpack Loader的工作原理

開發一個loader

開發一個markdown文件加載器(markdown-loader),實現在代碼中直接導入markdown文件。

原理:將md內容轉換爲html呈現到頁面中。

起步

  1. 創建一個loader的js文件,編寫內容

  2. 每個loader都需要導出一個函數,這個函數是loader對所加載到的資源的處理過程。

    1. 輸入:就是資源所加載到的內容,參數source接收
    2. 輸出:return處理後的結果
  3. 在webpack配置文件中配置rules,使用這個loader

// markdown-loader.js
module.exports = source => {
  console.log(source)
  return 'hello ~'
}

// webpack.config.js
module.exports = source => {
	module: {
    rules: [
      {
        test: /.md$/,
        use: './markdown-loader', // 同require一樣可以指定相對路徑
      }
    ]
    }
}

此時執行打包命令,會報錯You may need an additional loader to handle the result of these loaders.

“你可能需要一個額外的loader去處理這個自定義加載器的結果”。

webpack加載資源的過程,類似於一個工作管道,可以在這個過程中依次使用多個loader。

Source => loader1 -> loader2 -> loaderx => Result

但是它要求最終這個管道工作過後的結果,必須是一段javascript代碼

而上面的loader返回的是hello ~它不是javascript代碼,所以纔會出現這個錯誤提示。

解決辦法:

  1. 直接返回javascript代碼
    1. 例如 return 'exports default "hello ~"'
  2. 或者找一個合適的loader繼續處理markdown-loader處理的結果

使用合適的loader處理

繼續完善功能,安裝markdown解析模塊marked,yarn add marked --dev

該模塊可以解析md內容,並返回一個字符串。

markdown-loader仍然返回解析後的html字符串,將結果交給下一個loader處理。

const market = require('market')
module.exports = source => {
  const html = market(source)
  return html
}

安裝用於加載html的loader(html-loader)。

將這個加載器,配置在markdown-loader的後面執行(代碼中位置靠前)。

總結

  • Loader負責資源文件從輸入到輸出的轉換。
  • Loader實際上是一種管道的概念,對於同一個資源可以依次使用多個Loader,將此次loader返回的結果交給下一個loader處理。
    • 例如 上面的 markdown-loader -> html-loader
    • 以及處理樣式的 css-loader -> style-loader
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章