loader 是導出爲一個函數的 node 模塊。該函數在 loader 轉換資源的時候調用。給定的函數將調用 loader API,並經過 this 上下文訪問。
Webpack的配置離不來 loader
,官方也有關於如何編寫一個loader的文檔介紹,這篇文章會經過手寫一些常見的loader
,加深對loader的認識,提升工做中的開發效率。css
loader
是一個函數,接受匹配到的文件資源字符串和SourceMap
,咱們能夠經過修改文件內容的字符串返回給下個一loader
處理:node
module.exports = function(source,map){ return source; }
爲了方便咱們編寫loader
,咱們先準備好webpack
環境:webpack
生成一份 package.json
:web
npm init -y
安裝webpack
:npm
npm install webpack webpack -D
建立webpack.config.js
文件,並輸入如下內容:json
// webpack.config.js const path = require('path') module.exports = { mode: 'development', entry: './src/index.js', output: { filename: 'bundle.js', path: path.resolve(__dirname, 'dist') } }
package.json
加入scripts
命令:babel
"scripts": { "build": "webpack", "dev": "webpack --watch" },
咱們知道,webpack
默認會到 node_modules
裏面找對應的loader
,這樣不方便咱們調試,咱們能夠經過給webpack.config.js
添加resolveLoader
屬性,將loader
指向咱們建立的loaders
文件夾app
... resolveLoader: { modules: [ path.resolve(__dirname, "node_modules"), path.resolve(__dirname, "./loaders"), ], } ...
建立loaders
文件夾,裏面將存放咱們本身編寫的loader
:less
mkdir loaders
另外經過resolveLoader.alias
也能配置loader
別名:異步
resolveLoader: { alias: { loader1: resolve(__dirname, "./loaders/loader1.js"), loader2: resolve(__dirname, "./loaders/loader2.js"), loader3: resolve(__dirname, "./loaders/loader3.js"), }, }, module:{ rules:[ { test:/\.(js)$/, use:["loader1.js","loader2.js","loader3.js] } ] }
先寫一個簡單的loader
練練手,前面說到loader
能夠替換目標資源文件的內容,這裏我要把項目裏面的console.log
都幹掉,畢竟項目上線的時候控制檯出現調試內容不合適。
在loaders
文件夾下新建cleanlog-loader.js
,輸入下面內容:
module.exports = function(source){ return source.replace(/console\.log\(.*\);?\n/g, ''); }
到webpack.config.js
添加配置:
module:{ rules:[{ test: /\.(js)$/, use:"cleanlog-loader" }] }
src
文件夾下新建index.js
寫點console
內容,而後控制檯執行npm run build
,能夠發現編譯後的文件dist/bundle.js
已經沒有console
信息了。
banner-loader
能夠在腳本文件添加註釋信息,配置方式以下:
module:{ rules:[{ test: /\.(js)$/, use:{ loader:"banner-loader", options:{ text:"/**** build from chenwl ****/", } } }] }
這裏有兩個地方須要考慮:
loader-utils
schema-utils
在 loaders
文件夾下建立banner-loader.js
,輸入下面內容:
const fs = require("fs"); const {resolve} = require("path"); const loaderUtils = require("loader-utils"); const { validate } = require("schema-utils"); module.exports = function (source) { // 獲取配置參數 let options= loaderUtils.getOptions(this); let schema = { type: "object", properties: { text: {type: "string"} } } // 校驗參數是否正確 validate(schema, options,"banner-loader") return `${options.text} ${source}`; }
固然也能夠經過讀取文件內容寫入,新增配置參數 filename
,對模板文件進行讀取:
if(options.filename){ // 依賴某個文件變化,作到實時更新; this.addDependency(path.resolve(__dirname, `../${options.filename}`)) return fs.readFileSync(options.filename, "utf-8") + source; }
利用addDependency
,若是目標文件發生變化,能夠在觀察模式(watch mode)下重編譯,用npm run dev
啓動並修改filename
對應的文件試試
babel-loader
依賴@babel-core
和@babel/preset-env
,經過npm先安裝:
npm install @babel-core @babel/preset-env -D
webpack-config.js
添加rules:
{ test: /\.(js)$/, use: [ { loader: "babel-loader", options: { presets: ["@babel/preset-env"], }, }, ] }
能夠經過this.async
方法在loader中編寫異步代碼:
// babel-loader.js const loaderUtils = require("loader-utils"); const babel = require("@babel/core"); module.exports = function(source){ const options = loaderUtils.getOptions(this); const cb = this.async(); // 異步函數 babel.transform(source,{ ...options, sourceMaps:true },function(err,result){ cb(err, result.code) }) }
// less-loader let less = require("less"); module.exports = function (source) { let cssStr = ""; // 用less轉成css less.render(source,function(error,result) { if(!error){ cssStr = result.css } }); return cssStr }
// style-loader module.exports = styleLoader(source) { // js 字符串,生成style標籤插入到模板文件中 let code = ` let styleEl = document.createElement("style"); styleEl.innerHTML = ${JSON.stringify(source)}; document.head.appendChild(styleEl); `; return code.replace(/\/n/,""); }
咱們知道 webpack
是識別不了js之外的其它文件的,因此file-loader
須要設置loader.raw = true
,讓 loader 知道如今處理的是二進制的內容:
const loaderUtils = require("loader-utils"); function fileLoader(source) { // interpolate 插值 let fileUrl = loaderUtils.interpolateName(this, "[hash].[ext]", { content: source, }); this.emitFile(fileUrl,source); // 轉換後的Buffer最終是要被插入到頁面中,返回類型只能是 buffer 或 string // fileUrl 記得加引號,否則會報錯哦 return `module.exports = '${fileUrl}'` } // loader 處理的是二進制的內容 fileLoader.raw = true; module.exports = fileLoader;
url-loader
目的是將小圖轉成base64編碼,不然就用file-loader
處理:
// url-loader.js const loaderUtils = require("loader-utils"); const mime = require("mime"); function urlLoader(source) { let {limit} = loaderUtils.getOptions(this); if(limit > source.length){ let code = `data:${mime.getType(this.resourcePath)};base64,${source.toString('base64')}` return `module.exports = "${code}"` }else{ return require("./file-loader").call(this, source) } } urlLoader.raw = true; module.exports = urlLoader;