loader配置
loader是匯出為一個函式的node模組。該函式在loader轉換資源的時候呼叫。給定的函式將呼叫loader API,並通過this上下文訪問。
function loader(source){
return source
}
loader.pitch = function(){
console.log('pitch')
}
module.exports = loader
複製程式碼
載入自定義loader的幾種方式
- 匹配單個loader(使用絕對路徑)
{
test:/\.js$/,
use:[
loader:path.resolve('path/loaderjs'),
options
]
}
複製程式碼
- alias
resolveLoader:{
alias:{
'loader1':path.resolve('path/loader1')
}
},
module:{
rules:[
{
test:/\.js$/,
use:[
loader:loader1,
options
]
}
]
}
複製程式碼
- 配置多個loader
resolveLoader:{
modules:[
path.resolve('node_modules'),
path.resolve(__dirname, 'src', 'loaders')
]
}
複製程式碼
loader的分類
- 在前面 pre
- 正常 normal
- 在後面 post
loader的順序
pre => normal => inline => post
// index.js 載入順序為 pre + normal + inline + post
let str = require('inline-loader!./a.js')
// index.js 載入順序為 inline + post
// -! 不會讓檔案 再去通過pre+normal loader 來處理了
// ! 沒有normal
// !! 什麼都沒有 只有自身
let str = require('-!inline-loader!./a.js')
複製程式碼
loader的組成
loader執行首先會載入pitch-loader(書寫loader的順序),然後載入資源resource,最後執行normal-loader(正常載入loader的順序) 如果pitch-loader有返回值,則會跳過後面的載入,直接走到上一層的normal-loader裡面。
- pitch-loader
- normal-loader
loader的特點
- 最後載入的loader必須返回一個js指令碼
- 每一個loader都是一個模組
- 每一個loader都是無狀態的,確保loader在不同模組轉換之間不儲存狀態。
各種loader的實現
babel-loader
需要安裝@babel/core、@babel/preset-env
let babel = require('@babel/core');
<!--loader 的工具庫-->
let loaderUtils = require('loader-utils')
function loader(source){
<!--處理非同步-->
let cb = this.async();
<!--this loaderContext-->
let options = loaderUtils.getOptions(this);
<!--babel 轉化原始碼-->
babel.transform(source,{
...options,
sourceMap:true,
filename:this.resourcePath.split('/').pop() // 檔名
},function(err,result){
cb(null,result.code,result.map)
})
}
module.exports = loader
複製程式碼
banner-loader
use:{
loader:'banner-loader',
options:{
text:'xx',
filename:path.resolve(__dirname,'banner.js')
}
}
複製程式碼
let loaderUtils = require('loader-utils')
let fs = require('fs')
<!--驗證傳遞引數-->
let validateOptions = require('schema-utils')
function loader(source){
<!--新增快取-->
this.cacheable && this.cacheable()
let options = loaderUtils.getOptions(this)
let cb = this.async();
let schema = {
type:'object',
properties:{
text:{
type:'string'
},
filename:{
type:'string'
}
}
}
validateOptions(schema,options,'banner-loader')
if(options.filename){
<!--新增監聽 當檔案內容變化 自動打包-->
this.addDependency(options.filename)
fs.readFile(options.filename,'utf8',function(err,data){
cb(err,`/**${data}**/${source}`)
})
}else{
cb(null,`/**${options.text}**/${source}`)
}
}
module.exports = loader
複製程式碼
file-loader && url-loader
import p from './public.jpg'
let img = document.createElement('img');
img.src = p;
document.body.appendChild(img)
複製程式碼
file-loader
- 現將圖片生成一個md5 發射到dist目錄下
- 然後返回當前圖片的路徑
// file-loader
let loaderUtils = require('loader-utils')
function loader(source){
<!--生成md5檔案-->
let filename = loaderUtils.interpolateName(this,'[hash].[ext]',{content:source})
this.emitFile(filename,source)
return `module.exports = "${filename}"`
}
// 將source 轉化為二進位制buffer
loader.raw = true
module.exports = loader
// url-loader
let loaderUtils = require('loader-utils')
let mime = require('mime')
function loader(source){
let {limit} = loaderUtils.getOptions(this);
if(limit && limit > source.length){
return `module.exports = "data:${mime.getType(this.resourcePath)};base64,${source.toString('base64')}"`
}else{
return require('./file-loader').call(this,source)
}
}
loader.raw = true;
module.exports = loader;
複製程式碼
less-loader && style-loader && css-loader
// less-loader
let less = require('less')
function loader(source){
let css;
less.render(source,function(err,r){
css = r.css
})
// 交給css-loader處理
return css;
}
module.exports = loader
// style-loader
function loader(source){
}
loader.pitch = function(request){ // 這個request的就是當前路徑 沒有包含當前
// css-loader less-loader
console.log(request);
let script = `
let style = document.createElement('style');
<!--返回相對路徑 只執行行內loader-->
style.innerHTML = require(${loaderUtils.stringifyRequest(this, '!!' + request)});
document.head.appendChild(style);
`
// 返回的結果 會在瀏覽器中執行
return script;
}
module.exports = loader
// css-loader
// css-loader的作用就是把所有url的內容 都變成require('檔名的形式')
function loader(source) {
let reg = /url\((.*?)\)/g;
let current;
let pos = 0;
let arr = [`let lists = []`];
while (current = reg.exec(source)) { //lastIndex
// 陣列型別 [匹配的字串,分組中的內容]
let [matchUrl, p] = current;
let index = reg.lastIndex - matchUrl.length
// lastIndex就是匹配結束的位置
arr.push(`lists.push(${JSON.stringify(source.slice(pos, index))})`);
arr.push(`lists.push("url("+require(${p})+")")`)
pos = reg.lastIndex;
}
arr.push(`lists.push(${JSON.stringify(source.slice(pos))})`);
arr.push(`module.exports = lists.join('')`);
console.log(arr.join('\r\n'));
return arr.join('\r\n');
}
module.exports = loader;
複製程式碼