Loader 基本使用
loader 的本质是一个 node 模块,这个模块导出一个函数,用于将不同类型的文件转换为 webpack 可识别的模块。
loader 执行顺序
- 分类:pre 前置,normal 普通,inline 内联,post 后置。
- 执行优先级: pre > normal > inline > post
- 同类型执行顺序:从右向左,从下到上
使用前置、后置 loader:
| { test: /\.js$/, use: { loader: 'loader1' }, enforce: 'pre' }
|
使用内联 loader:
-!
不会让文件再通过 pre + normal loader 处理!
不再用 normal loader!!
不用其他所有,只用行内
let str = require('!!inline-loader!./a.js')
Loader 开发
loader 组成
- loader 默认由两部分组成:
pitch
,normal
pitch
是 loader 上的一个方法,它的作用是阻断 loader 链。
| module.exports.pitch = function () { console.log("pitching graph"); };
|
| //pitch 无返回值 pitch loader3 → loader2 → loader1 ↘ 资源 ↙ normal loader3 ← loader2 ← loader1
// pitch loader - 有返回值 use: [loader3, loader2, loader1] pitch loader3 → loader2 loader1 ↙ 有返回值 资源 ↙ normal loader3 loader2 loader1
|

loader 配置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| use: { loader: path.resolve(__dirname, "loaders/loader1.js"); }
resolveLoader: { alias: { loader1: path.resolve(__dirname, "loaders/loader1.js"); } } use: { ("loader1"); }
resolveLoader: { modules: ["node_modules", path.resolve(__dirname, "loaders")]; }
|
开发 loader 必备工具
- loader-utils
常用的几个方法:
- getOptions 获取 loader 的配置项。
- loader 的配置就是这个:

- interpolateName 处理生成文件的名字。
- stringifyRequest 把绝对路径处理成相对根目录的相对路径。
- schema-utils
可以验证 loader option 配置的合法性:就像TS一样去约束其option的类型
用法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| const loaderUtils = require("loader-utils"); const validate = require("schema-utils"); module.exports = function (source) { let options = loaderUtils.getOptions(this) || {}; let schema = { type: "object", properties: { name: { type: "string", }, }, }; validate(schema, options); return source; };
|
loader 开发准则
官方说明
- 简单易用。
- 使用链式传递。
- 模块化的输出。
- 确保无状态。
- 使用 loader utilities。
- 记录 loader 的依赖。
- 解析模块依赖关系。
- 提取通用代码。
- 避免绝对路径。
- 使用 peer dependencies。
一些常用 loader 简易实现例子
loader 编写例子源码
babel-loader
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| let babel = require("@babel/core"); let loaderUtils = require("loader-utils");
function loader(source) { let callback = this.async();
let options = loaderUtils.getOptions(this); babel.transform( source, { ...options, sourceMap: true, filename: this.resourcePath.split("/").pop(), }, function (err, result) { callback(err, result.code, result.map); } ); }
module.exports = loader;
|
file-loader & url-loader
- file-loader 作用:根据图片生成 md5,发射到 dist 目录下,file-loader 还会返回当前图片路径
| let loaderUtils = require("loader-utils"); function loader(source) { let filename = loaderUtils.interpolateName(this, "[hash].[ext]", { content: source, }); this.emitFile(filename, source); return `module.exports="${filename}"`; } loader.raw = true; module.exports = loader;
|
- url-loader 作用:根据设定的大小限制,选择将图片转换为 base64 或调用 file-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 & css-loader & style-loader
- less-loader
| let less = require("less");
function loader(source) { let css; less.render(source, function (err, result) { css = result.css; }); return css; }
module.exports = loader;
|
- css-loader
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| const { match } = require("micromatch");
function loader(source) { let reg = /url\((.+?)\)/g; let pos = 0; let current; let arr = ["let list = []"]; while ((current = reg.exec(source))) { let [matchUrl, g] = current; let last = reg.lastIndex - matchUrl.length; arr.push(`list.push(${JSON.stringify(source.slice(pos, last))})`); pos = reg.lastIndex; arr.push(`list.push('url('+require(${g})+')')`); arr.push(`list.push(${JSON.stringify(source.slice(pos))})`); arr.push(`module.exports = list.join('')`); return arr.join("\r\n"); } return source; }
module.exports = loader;
|
- style-loader
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| let loaderUtils = require("loader-utils");
function loader(source) { let str = ` let style = document.createElement('style'); style.innerHTML = ${JSON.stringify(source)}; document.head.appendChild(style); `; return str; }
loader.pitch = function (remainingRequest) { let str = ` let style = document.createElement('style'); style.innerHTML = require(${loaderUtils.stringifyRequest( this, "!!" + remainingRequest )}); document.head.appendChild(style); `; return str; }; module.exports = loader;
|