前言
loader幾乎是webpack配置必備,但是我們用它的時(shí)候有沒有想過它的原理到底是怎么回事呢喊式?今天就讓我們來探究一下它的原理吧。
loader的作用
在探究原理之前萧朝,我們先來回顧一下常用的loader與及相關(guān)的作用岔留。
如處理css文件的
style-loader, css-loader, sass-loader
module:{
rules: [
{
test: /\.css$/,
use: ['style-loader', 'css-loader', 'sass-loader']
}
]
}
loade執(zhí)行順序是從右往左執(zhí)行的。至于為什么是從右到左執(zhí)行而不是從左到右检柬?因?yàn)閣ebpack選擇了compose這樣的函數(shù)式編程方式献联,而從左到右則是pipe式編程。
所以上面例子的執(zhí)行順序是遇到.scss的文件就先經(jīng)過sass-loader 處理再到 css-loader 再到style-loader何址,我們可以猜測到寫的scss文件經(jīng)過處理得到css字符串里逆,再給css-loader序列化處理,最后給style-loader處理創(chuàng)建style標(biāo)簽插入到html
所以我們可以從這些現(xiàn)象就可以猜出來loader其實(shí)就像一個(gè)函數(shù)用爪,有參數(shù)輸入原押,經(jīng)過處理,然后將處理好的結(jié)果輸出偎血。
可以打開webpack官網(wǎng)loader部分https://webpack.js.org/api/loaders/诸衔,這里詳細(xì)指出了如何寫一個(gè)loader。
如寫一個(gè) sync-loader.js
module.exports = function(content, map, meta) {
return someSyncOperation(content);
};
最后導(dǎo)出一個(gè)函數(shù)颇玷,這個(gè)函數(shù)有幾個(gè)參數(shù)笨农,其中主要用到的就是content這個(gè)參數(shù),它表示讀取到的對應(yīng)的文件的源碼內(nèi)容亚隙,類型可以是string 也可以是buffer磁餐。我們就可以在這個(gè)方法里面對這些源碼進(jìn)行操作了。
laoder實(shí)戰(zhàn)
弄清楚原理之后阿弃,我們自己手寫3個(gè)loader,創(chuàng)建src/myLoaders目錄
創(chuàng)建 my-sass-loader.js羞延、 my-css-loader.js渣淳、 my-style-loader.js,
再配置好webpack.config.js如下
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin')
const { CleanWebpackPlugin } = require('clean-webpack-plugin')
module.exports = {
mode: 'development',
entry: {
index: './src/index.js'
},
output: {
path: path.resolve(__dirname, 'dist'),
filename: '[name].js'
},
resolveLoader: {
modules: ['node_modules', './src/myLoaders'],
},
module: {
rules: [
{
test: /\.css$/,
use: ['style-loader', 'css-loader']
},
{
test: /\.scss$/,
use: ['my-style-loader', 'my-css-loader', 'my-sass-loader']
}
]
},
plugins: [
new HtmlWebpackPlugin(), // 輸出html模板
new CleanWebpackPlugin(), // 清除dist目錄
]
}
注意配置中配置了resolveLoader來自定義loader的存放目錄的
resolveLoader: {
modules: ['node_modules', './src/myLoaders'],
}
和scss 的loader配置改為了我們自己寫的loader
{
test: /\.scss$/,
use: ['my-style-loader', 'my-css-loader', 'my-sass-loader']
}
配置好之后伴箩,分別寫這三個(gè)loader要做的事情入愧,
首先是my-sass-loader, 利用sass的api對scss文件內(nèi)容進(jìn)行編譯
const sass = require('sass')
module.exports = function(content) {
const result = sass.renderSync({
data: content
})
return result.css
}
其次是my-css-loader, 對編譯后的文件進(jìn)行序列化
module.exports = function(content) {
return JSON.stringify(content)
}
最后是my-style-loader, 對css字符串進(jìn)行創(chuàng)建style標(biāo)簽并插入到head中
module.exports = function(content) {
return `
const style = document.createElement('style');
style.setAttribute('id', 'my-style-loader');
style.innerHTML = ${content};
document.head.appendChild(style);
`
}
為了查看效果,我額外加了一句 style.setAttribute('id', 'my-style-loader');
index.scss代碼:
body {
background: red;
div {
background: blue;
color: #fff;
}
}
寫完了嗤谚,npm run dev
打開dist/index.html查看效果
ok, 沒問題棺蛛,自己實(shí)現(xiàn)了三個(gè)loader。有人問為什么要三個(gè)巩步?一個(gè)不行嗎旁赊?其實(shí)一個(gè)也可以的,但是這樣子的寫法不符合函數(shù)式編程的規(guī)范椅野,不符合官方提倡的寫法终畅。官方要求一個(gè)loader只做一件事籍胯。這樣子函數(shù)會(huì)更加純粹,純粹的函數(shù)對于可維護(hù)性离福、可擴(kuò)展性杖狼、可重構(gòu)性大大有利,這才是體現(xiàn)一個(gè)程序員的內(nèi)涵的地方妖爷。否則你養(yǎng)成了經(jīng)常寫一坨偶合代碼的習(xí)慣將會(huì)影響日后的成長蝶涩。
如果要獲取參數(shù)怎么辦?
在loader函數(shù)內(nèi)通過this.query獲刃跏丁绿聘;而且因?yàn)橐褂胻his ,所以笋除,導(dǎo)出的函數(shù)必須是module.exports = function(){}, 不能是箭頭函數(shù)斜友。
console.log(this.query)
如果要返回多個(gè)結(jié)果怎么辦?
可以用 this.callback
this.callback(
err: Error | null,
content: string | Buffer,
sourceMap?: SourceMap,
meta?: any
);
如果要返回異步結(jié)果呢垃它?
以上都是同步執(zhí)行返回loader的處理的結(jié)果鲜屏,同理,業(yè)務(wù)中可能會(huì)有異步處理的結(jié)果国拇,就用this.async洛史,如下
module.exports = function(content) {
const callback = this.async();
setTimeout(() => {
callback(content)
}, 1000)
}
總結(jié)
- loader就是一個(gè)必須有返回值(String | Buffer)的函數(shù)
- loader函數(shù)對源文件進(jìn)行增刪改處理,將處理好的結(jié)果返回給webpack酱吝。
- loader不能是箭頭函數(shù)也殖,因?yàn)閮?nèi)部可以采用this
- loader支持同步異步、返回結(jié)果
- loader的參數(shù)通過this.query獲取
- resolveLoaders可以配置存放loader的路徑
以上是我看法务热,如有不對請留言討論忆嗜,謝謝。