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(數組),執行順序是從後向前
- 可以配置loader模塊名稱,也可以配置loader相對路徑,原理同require函數
一些插件介紹:
- css-loader
- 將css文件轉換稱一個js模塊,具體實現是:
- 將css代碼push到一個數組當中
- 但是整個過程中,並沒有使用到這個數組
- 還需要一個style-loader去使用它
- 將css文件轉換稱一個js模塊,具體實現是:
- style-loader
- 將css-loader轉換後的結果,通過style標籤的形式追加到頁面上
- html-loader
- 使webpack識別
.html
的模塊
- 使webpack識別
webpack 導入資源模塊
雖然可以通過loader將任何類型的資源作爲入口去打包。
但webpack一般還是將javascript作爲打包入口。
打包入口可以說是項目的運行入口。
webpack建議:
- 編寫代碼過程中,根據代碼的需要,動態導入資源
- 需要資源的不是應用,而是代碼
目前而言,javascript代碼負責完成整個應用的業務功能。javascript驅動前端應用的業務。
在實現業務的過程中,需要其他類型的資源(例如樣式、圖片)。
webpack建立javascript和資源的依賴關係的目的:
- 邏輯合理,JS確實需要這些資源文件
- 確保上線資源不缺失,都是必要的
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-loader
的limit
(字節上限)實現最佳實踐。
- 超出
limit
的文件單獨提取存放(調用file-loader
加載器) - 小於
limit
的文件轉換爲Data URLs嵌入代碼
注意:如果url-loader
配置了limit
,大文件是使用file-loader
加載器處理的,所以需要安裝它依賴的file-loader
。
常用加載器分類
- 編譯轉換類
- 把加載到的模塊,轉換爲javascript代碼
- 例如 css-loader
- 文件操作類
- 把加載到的資源模塊,拷貝到輸出的目錄
- 同時導出文件的訪問路徑
- 例如 file-loader
- 代碼檢查類
- 對代碼加載的文件中的代碼進行校驗
- 目的:統一代碼風格,從而提高代碼質量
- 一般不會修改生產環境的代碼
- 例如 eslint-loader
webpack 處理 ES2015
webpack默認就能處理代碼當中的 import
和export
,但這不表示webpack會自動編譯ES6的代碼。
因爲模塊打包需要,所以webpack對代碼中的import
和export
作了相應的轉換。
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文件。
然後它會順着入口文件的代碼,根據代碼中出現的import
或require
之類的語句,解析推斷出這個資源所依賴的模塊。
然後分別再解析每個模塊對應的依賴。
最後形成了整個項目中,所有用到的文件之間的依賴關係的依賴樹。
webpack會遞歸這個依賴樹,找到每個節點對應的資源文件。
最後根據配置文件中的rules
屬性,找到模塊對應的加載器去加載這個模塊。
最後會將加載到的結果,放到打包文件bundle.js
中,從而實現整個項目的打包。
Loader機制是webpack的核心。
如果沒有Loader,webpack就沒有辦法實現各種資源文件的加載,而只是打包合併JS代碼的工具。
webpack Loader的工作原理
開發一個loader
開發一個markdown文件加載器(markdown-loader
),實現在代碼中直接導入markdown文件。
原理:將md內容轉換爲html呈現到頁面中。
起步
-
創建一個loader的js文件,編寫內容
-
每個loader都需要導出一個函數,這個函數是loader對所加載到的資源的處理過程。
- 輸入:就是資源所加載到的內容,參數
source
接收 - 輸出:
return
處理後的結果
- 輸入:就是資源所加載到的內容,參數
-
在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代碼,所以纔會出現這個錯誤提示。
解決辦法:
- 直接返回javascript代碼
- 例如
return 'exports default "hello ~"'
- 例如
- 或者找一個合適的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
- 例如 上面的