??個(gè)人主頁(yè):愛吃炫邁
??系列專欄:前端工程化
?????座右銘:道阻且長(zhǎng),行則將至??
loader
一個(gè)loader就是一個(gè)Node.js模塊,這個(gè)模塊需要導(dǎo)出一個(gè)函數(shù),這個(gè)導(dǎo)出函數(shù)的工作就是獲得處理前的源內(nèi)容,對(duì)源內(nèi)容進(jìn)行處理后,返回處理后的內(nèi)容
實(shí)現(xiàn)一個(gè)替換文件中姓名的loader。例如將“小張”替換成“小李”
name.js
export const name = "小張"
index.js
import { name } from "./name.js"
function showInfo() {
console.log("name")
}
showInfo()
運(yùn)行代碼輸出:
小張
現(xiàn)在我們希望將小張?zhí)鎿Q成小李:
- 創(chuàng)建replace-loader
myLoaders/replace-loader.js
module.exports = function(source) {
// source為compiler傳遞給loader的一個(gè)文件的源內(nèi)容
const cotent = source.replace("小張", "小李")
// 該處理函數(shù)需要返回處理后的內(nèi)容
return cotent
}
- 使用loader
webpack.config.js
const path = require("path")
module.export = {
mode: "production",
entry: "./src/index.js",
output: {
path: path.resolve(__dirname, "dist"),
filename: "[name].js",
},
module: {
rules: [
{
test: /\.js$/,
use:"./myLoaders/replace-loader.js"
// 本地loader, 要填寫本地的路徑
}
]
},
}
打包后我們就會(huì)發(fā)現(xiàn)實(shí)現(xiàn)我們的想要的功能啦~~~
因?yàn)槲覀兪褂玫氖潜镜氐膌oader,因此需要填寫本地的路徑。loader的引入主要有以下幾種方式:
loader引入方式
- 如果是npm包安裝的loader,那么直接寫loader名稱即可
{
test: /\.js$/,
use:'babel-loader'
}
- 如果是本地自定義的loader,那么需要寫本地loader的地址
{
test: /\.js$/,
use: path.resolve(__dirname, "./myLoaders/replace-loader")
}
如上面的代碼,我們每使用一個(gè)自定義的loader,都必須使用 path 模塊來(lái)解析自定義loader的路徑問(wèn)題,這就會(huì)導(dǎo)致代碼變得難以維護(hù)。那可不可以像引用第三方的loader一樣,只寫loader 名呢?我們可以使用 resolveLoader 來(lái)解決這個(gè)問(wèn)題。
- 如果是本地定義的loader,然后也想直接使用loader名稱,那么可以取個(gè)別名
module.exports = {
resolveLoader: {
// 取個(gè)別名
alias: {
"replace-loader": path.resolve(__dirname,"myLoaders/replace-loader")
}
},
module: {
rules: [
{
test: /\.js$/,
use: 'replace-loader',
}
]
}
}
- 如果你不想取別名,還想直接使用loader,那么就定義一下loader的查找位置,loader會(huì)默認(rèn)先從
node_modules
中查找。如果我們希望它也能夠到本地查找,那么就定義一下查找位置。
ResolveLoader 用于配置 webpack 如何尋找 loader,默認(rèn)情況下只會(huì)去 node_modules 目錄下尋找,為了讓 webpack 去加載自定義的 loader,我們需要修改 resolveLoader.modules
比如我們自定義的loader 放在 ./myLoaders 目錄下,則需要如下配置:
module.exports = {
resolveLoader: {
// 去哪些目錄下尋找 loader ,有先后順序之分
// 如下配置中,查找順序是:先在 node_modules 目錄下尋找,若找不到,再到 ./myLoaders 目錄下尋找
modules: ["node_modules", "./myLoaders"],
},
module: {
rules: [
{
test: /\.js$/,
use: 'replace-loader',
}
]
}
}
loader傳入/接收參數(shù)
傳入?yún)?shù)
我們上述代碼的功能是將“小張”替換成“小李”,假如我們希望將“小張”替換成“小王”,換成“小周”,那么我們難道每次都在loader中修改嗎?這肯定不會(huì),因此,我們需要支持配置參數(shù)。loader支持通過(guò)options進(jìn)行配置:
{
test: /\.js$/,
use: [
"replace-loader",
{
loader: "replace-loader",
options: {
name: "小李",
},
},
],
}
接收參數(shù)
- this.query
webpack官方文檔如何讓編寫一個(gè)loader中說(shuō)明了loader只接收一個(gè)參數(shù),這個(gè)參數(shù)是讀取的文件內(nèi)容(一個(gè)包含資源文件內(nèi)容的字符串)。webpack會(huì)把所有的信息都放到上下文this中,我們可以通過(guò)this.query
API來(lái)獲取webpack.config.js中配置的options對(duì)象:
module.exports = function (source) {
// this.query 獲取到的就是在webpack.config.js配置中配置的 options 對(duì)象
// 通過(guò) this.query API 獲取在配置中配置的 name
return source.replace("小張", this.query.name);
};
- loader-utils
雖然我們可以通過(guò)this.query
來(lái)進(jìn)行獲取,但是webpack更加推薦使用loader-utils
來(lái)進(jìn)行操作,它提供了許多有用的工具,最常用的一種工具是獲取傳遞給loader的選項(xiàng)。
- 安裝
npm i loader-utils -D
- 使用
const { getOptions } = require('loader-utils')
module.exports = function(source) {
// const options = this.getOptions();
let { name } = getOptions(this);
const content = source.replace("小張",name);
return content;
}
loader返回值
loader 的原理就是將輸入的源內(nèi)容進(jìn)行處理后返回,loader的返回值涉及到一個(gè)還是多個(gè)返回值。有些情況下比如我們需要返回sourceMap,那么就需要多個(gè)返回值。 如果需要返回一個(gè)返回值,可以直接使用return。
return source
如果只有一個(gè)返回值,可以使用return返回,這種方式返回的是源內(nèi)容轉(zhuǎn)換后的內(nèi)容
const { getOptions } = require('loader-utils')
module.exports = function (source) {
// 處理source
let { name } = getOptions(this)
const content = source.replace("小張", name)
return content // 返回一個(gè)值
}
this.callback()
如果有多個(gè)值需要返回,需要使用loader本身提供的回調(diào)函數(shù)callback。這種方式可以返回除了處理內(nèi)容之外的其他信息。
const { getOptions } = require('loader-utils')
const { SourceMap } = require('module')
module.exports = function (source) {
// 處理 source
let { name } = getOptions(this)
const content = source.replace("小張", name);
// 使用 this.callback 返回內(nèi)容
this.callback(null, content,SourceMap);
};
callback支持的參數(shù)如下:
callback({
// 報(bào)錯(cuò)
error: Error | Null,
// 轉(zhuǎn)換后的內(nèi)容
content: String | Buffer,
// 轉(zhuǎn)換后的內(nèi)容得出的sourceMap
sourceMap?: SourceMap,
// ast
abstractSyntaxTree?: AST
})
注意
:
在Webpack中,每個(gè)loader都可以返回一個(gè)包含多個(gè)屬性的對(duì)象。
- content是該對(duì)象的一個(gè)屬性,用于指定經(jīng)過(guò)loader處理后生成的代碼。
- sourceMap是另一個(gè)屬性,用于指定生成的代碼的源映射表。
因此,content和sourceMap的區(qū)別在于,content是經(jīng)過(guò)loader處理后生成的代碼本身,而sourceMap則是一個(gè)包含了生成的代碼和原始代碼的映射關(guān)系的JSON對(duì)象。
事實(shí)上,如果只有一個(gè)返回值,我們也可以直接使用this.callback。
this.callback(null,content)
同步/異步loader
loader有同步異步之分,上面介紹的loader都是同步loader,因?yàn)樗鼈兊霓D(zhuǎn)換流程都是同步的,即轉(zhuǎn)換完成后再返回結(jié)果。但在某些場(chǎng)景下轉(zhuǎn)換內(nèi)容需要異步才能完成,例如需要通過(guò)網(wǎng)絡(luò)請(qǐng)求才能得到結(jié)果,如果使用同步的方式,網(wǎng)絡(luò)請(qǐng)求就會(huì)阻塞整個(gè)構(gòu)建過(guò)程,導(dǎo)致構(gòu)建變得十分緩慢。
使用async和await進(jìn)行處理
module.exports = async function(source) {
let {name,age} = getOptions(this);
// 這里其實(shí)不是異步的,只是作為示例,可以這樣處理
const content = await source.replace("小張",name);
return content;
}
使用loader提供的this.async進(jìn)行處理
當(dāng)轉(zhuǎn)換內(nèi)容需要異步才能完成時(shí),我們可以使用webpack為loader提供的this.async
將這個(gè)loader變成是一個(gè)異步loader:
const { getOptions } = require('loader-utils')
const { SourceMap } = require('module')
module.exports = function (source) {
let { name } = getOptions(this);
// 使用 setTimeout 模擬異步過(guò)程
setTimeout(() => {
const content = source.replace("小張", name);
// 通過(guò) callback 返回執(zhí)行異步后的結(jié)果
this.async(null, content,SourceMap);
}, 3000);
};
loader單一功能原則
在webpack官網(wǎng)的如何編寫一個(gè)loader中提到,webpack的loader編寫最尋單一功能原則,也就是loader只能實(shí)現(xiàn)一個(gè)功能。比如less-loader用來(lái)處理less文件,css-loader用來(lái)處理css文件,style-loader用來(lái)將樣式插入到style標(biāo)簽中,這些功能雖然可以放到一個(gè)loader中實(shí)現(xiàn),但是為了確保loader的功能純粹,能夠讓不同的loader各司其職,同時(shí)進(jìn)行功能組合,最好每個(gè)loader只負(fù)責(zé)一個(gè)功能。
loader實(shí)戰(zhàn)
style-loader
style-loader做的事情其實(shí)很簡(jiǎn)單,就是把序列化后的css內(nèi)容放到style標(biāo)簽中,然后將style標(biāo)簽插入到HTML頁(yè)面的head標(biāo)簽中
module.exports = function(source) {
return `const styleTag = document.createElement('style');
styleTag.innerHTML = ${source};
document.head.appendChild(styleTag);
`
}
css-loader
css-loader 做的事情也十分的簡(jiǎn)單,將 less-loader 轉(zhuǎn)換后的 css 內(nèi)容進(jìn)行序列化
module.exports = function(source) {
return JSON.stringify(source);
}
less-loader
less-loader 做的事情就是使用 less 模塊,將 less 轉(zhuǎn)換成 css文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-648728.html
// 使用 less 模塊處理 less 語(yǔ)法
const less = require('less');
module.exports = function(source) {
less.render(source, (error, output) => {
let { css } = output;
this.callback(error, css)
})
}
參考文章:
由淺及深實(shí)現(xiàn)一個(gè)自定義loader文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-648728.html
到了這里,關(guān)于【webpack】自定義loader的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!