简单使用
当一个 loader 在资源中使用,这个 loader 只能传入一个参数 - 这个参数是一个包含包含资源文件内容的字符串。
⚠️ 默认情况下,Webpack 传递给 Loader 的原始内容是一个 UTF-8 格式编码的字符串。但是在某些场景下,加载器处理的不是文本文件,而是二进制文件,官网例子 通过 exports.raw 属性告诉 Webpack 该 Loader 是否需要二进制数据。
同步 loader 可以简单的返回一个代表模块转化后的值。在更复杂的情况下,loader 也可以通过使用 this.callback(err, values...) 函数,返回任意数量的值。错误要么传递给这个 this.callback 函数,要么扔进同步 loader 中。loader 会返回一个或者两个值。第一个值的类型是 JavaScript 代码的字符串或者 buffer。第二个参数值是 SourceMap,它是个 JavaScript 对象。
多个loader
⚠️ 当链式调用多个 loader 的时候,请记住它们会以相反的顺序执行。取决于数组写法格式,从右向左或者从下向上执行。
- 最后的 loader 最早调用,将会传入原始资源内容。
- 第一个 loader 最后调用,期望值是传出 JavaScript 和 source map(可选)。
- 中间的 loader 执行时,会传入前一个 loader 传出的结果。
所以,在接下来的例子,foo-loader 被传入原始资源,bar-loader 将接收 foo-loader 的产出,返回最终转化后的模块和一个 source map(可选)
{ ⬆️
test: /\.js/, |
use: [ |
'bar-loader', |
'foo-loader' | 从下向上
] |
}
设计原则
编写 loader 时应该遵循以下准则。它们按重要程度排序,有些仅适用于某些场景,请阅读下面详细的章节以获得更多信息。
- 简单易用。
- 使用链式传递。
- 模块化的输出。
- 确保无状态。
- 使用 loader utilities。
- 记录 loader 的依赖。
- 解析模块依赖关系。
- 提取通用代码。
- 避免绝对路径。
- 使用 peer dependencies。
简单(Simple)
loaders 应该只做单一任务。这不仅使每个 loader 易维护,也可以在更多场景链式调用。
缓存
如果为每个构建重新执行重复的转换操作,这样Webpack构建可能会变得非常慢。
Webpack 默认会缓存所有loader的处理结果,也就是说,当待处理的文件或者依赖的文件没有变化时,不会再次调用对应的loader进行转换操作
module.exports = function (source) {
// 开始缓存
this.cacheable && this.cacheable();
// 在这里按照你的需求处理 source
return source.replace('word', '嘿嘿')
}
一般默认开启缓存,如果不想Webpack这个loader进行缓存,也可以关闭缓存
module.exports = function (source) {
// 关闭缓存
this.cacheable(false);
// 在这里按照你的需求处理 source
return source.replace('word', '嘿嘿')
}
创建项目 loader
Webpack-demo
├── dist
│ └── index.html
├── package-lock.json
├── package.json
├── src
│ └── index.js
└── Webpack.config.js
初始化
npm init -y
添加依赖
npm install webpack@4.39.2 webpack-cli@3.3.6 webpack-dev-server@3.11.0 -D
dist/index.html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<title></title>
</head>
<body>
<script src="./bundle.js"></script>
</body>
</html>
入口src/index.js
document.write('hello app');
webpack.config.js
const path = require('path')
module.exports = {
entry: './src/index.js',
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'bundle.js',
},
devServer: {
contentBase: './dist',
overlay: {
warnings: true,
errors: true,
},
open: true,
},
}
package.json 中配置启动命令
"scripts": {
"dev": "Webpack-dev-server"
},
创建loader
src/loader/text-loader.js
module.exports = function (source) {
// 在这里按照你的需求处理 source
return source.replace('app', '嘿嘿')
}
引用自己的loader
module.exports = {
//...
module: {
rules: [
{
test: /\.js$/,
use: ['text-loader'],
},
],
},
resolveLoader: {
modules: ['node_modules', './src/loader'], // node_modules 找loader,如果找不到就 ./src/loader 找
},
}
**option参数
module: {
rules: [
{
test: /.js$/,
use: [
{
loader: 'my-loader',
options: {
flag: true,
},
},
],
},
],
},
如何在loader中获取这个写入配置信息呢?
const loaderUtils = require('loader-utils')
module.exports = function (source) {
// 获取到用户给当前 Loader 传入的 options
const options = loaderUtils.getOptions(this)
console.log('options-->', options)
// 在这里按照你的需求处理 source
return source.replace('word', ', 哈哈')
}
实现渲染markdown文档loader
安装依赖: 安装依赖 md 转 html 的依赖,使用的 markdown-it
npm install markdown-it@12.0.6 -D
辅助工具 md.js 用来添加 div和 class
- 将
markdown-it处理完成的html 增加类
module.exports = function ModifyStructure(html) {
// 分块, 把h3和h2开头的切成数组
const htmlList = html.replace(/<h3/g, '$*(<h3').replace(/<h2/g, '$*(<h2').split('$*(')
// div套上 .card 类名
return htmlList
.map(item => {
if (item.indexOf('<h3') !== -1) {
return `<div class="card card-3">${item}</div>`
} else if (item.indexOf('<h2') !== -1) {
return `<div class="card card-2">${item}</div>`
}
return item
})
.join('')
}
创建loader
- /src/myLoader/md-loader.js
const { getOptions } = require('loader-utils')
const MarkdownIt = require('markdown-it')
const beautify = require('./md')
module.exports = function (source) {
const options = getOptions(this) || {}
const md = new MarkdownIt({
html: true,
...options,
})
// 这里解析的结果是一个 HTML 字符串。如果直接返回,也会面临Webpack无法解析模块的问题。
// 正确的做法是把这个HTML字符串拼接成一段JS代码。通过module.exports导出这个HTML字符串,
let html = beautify(md.render(source))
// 这样外界在导入模块的时候就可以接收到这个HTML字符串。
console.log("🚀 ~ file: md-trans-loader.js ~ line 16 ~ html", JSON.stringify(html));
html = `module.exports = ${JSON.stringify(html)}`
this.callback(null, html)
}
使用md-trans-loader
const path = require('path');
module.exports = {
entry: './src/index.js',
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'bundle.js',
},
module: {
rules: [
{
test:/.md$/,
use:[
{
loader: 'md-trans-loader'
}
]
},
{
test: /\.js$/,
use: [
{
loader: 'text-trans-loader',
options: {
测试: true,
},
},
],
}
],
},
resolveLoader: {
modules: ['node_modules', './src/loader'], // node_modules 找loader,如果找不到就 ./src/loader 找
},
devServer: {
contentBase: './dist',
overlay: {
warnings: true,
errors: true,
},
open: true,
},
};
选取一个md文件测试渲染
index.js引入home.md文件
document.write('hello world');
import mdHtml from './home.md'
const content = document.createElement('div')
content.className = 'content'
content.innerHTML = mdHtml
document.body.appendChild(content)