Loader 本质上是一个导出为函数的 JavaScript 模块
loader runner 库会调用这个函数,然后将上一个 loader 产生的结果或者资源文件传入进去

1
2
3
4
5
6
7
8
9
10
11
12
/**
*
* @param {*} content 资源文件的内容;
* @param {*} map sourcemap相关的数据;
* @param {*} meda 一些元数据;
* @returns
*/
module.exports = function (content, map, meda) {
console.log("loader1", "---", content, map, meda);
return content;
};

webpack.config.js

1
2
3
4
5
6
7
8
module: {
rules: [
{
test: /\.js$/,
use: ["./my_loader/my_loader1.js", "./my_loader/my_loader2.js"],
},
],
},

同步异步

默认创建的 Loader 就是同步的 Loader

这个 Loader 必须通过 return 或者 this.callback 来返回结果,交给下一个 loader 来处理

通常在有错误的情况下,我们会使用 this.callback

this.callback 的用法如下:

  • 第一个参数必须是 Error 或者 null
  • 第二个参数是一个 string 或者 Buffer
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//同步
module.exports = function (content, map, meda) {
console.log("loader2", "---", content, map, meda);
setTimeout(() => {
return content;
});

this.callback(null, content + "aaa");
};

// 异步
module.exports = function (content, map, meda) {
console.log("loader2", "---", content, map, meda);
const callback = this.async();
setTimeout(() => {
callback(null, content + "aaa");
}, 2000);
};

resolveLoader 属性

更加简洁的去加载自己的 loader 文件夹

1
2
3
4
5
6
7
8
9
10
11
12
13
14
resolveLoader: {
modules: ["./my_loader", "node_modules"],
},

module: {
rules: [
{
test: /\.js$/,
// use: ["./my_loader/my_loader1.js", "./my_loader/my_loader2.js"],
use: ["my_loader1.js", "my_loader2.js"],
},
],
},

传入和获取参数

之前需要 npm install loader-utils -D

现在不用,直接 this.getOptions();可以获取

1
2
3
4
5
6
7
8
9
10
11
12
13
module: {
rules: [
{
test: /\.js$/,
// use: ["./my_loader/my_loader1.js", "./my_loader/my_loader2.js"],
use: [
"my_loader1.js",
"my_loader2.js",
{ loader: "my_loader3.js", options: { name: "kkk" } },
],
},
],
},
1
2
3
4
5
6
7
8
9
10
// my_loader3.js
module.exports = function (content, map, meda) {
const options = this.getOptions();
const callback = this.async();
// { name: 'kkk' }
console.log(options);
setTimeout(() => {
callback(null, content + "aaa");
}, 2000);
};

校验参数

npm install schema-utils -D

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
const { validate } = require("schema-utils");
module.exports = function (content, map, meda) {
const options = this.getOptions();
const callback = this.async();
// { name: 'kkk' }
console.log(options);
validate(
{
type: "object",
properties: {
name: { type: "string", definition: "输入你的名称" },
},
},
options
);
setTimeout(() => {
callback(null, content + "aaa");
}, 2000);
};

案例

md 渲染转换 loader

index.md

1
2
3
4
5
6
7
8
## Css

## Js

js 学习

```js
var str = "aa";
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
29
30
31
32
33
34
35
36
37

webpack.config.js

```JavaScript
const path = require("path");
const htmlWebpackPlugin = require("html-webpack-plugin");
module.exports = {
mode: "development",
entry: "./src/main.js",
output: {
path: path.resolve(__dirname, "./build"),
filename: "bundle.js",
},
module: {
rules: [
{
test: /\.js$/,
use: [
{ loader: "my_loader3.js", options: { name: "kkk" } },
],
},
{
test: /\.md$/,
use: "myMd-loader.js",
},
{
test: /\.css$/,
use: ["style-loader", "css-loader"],
},
],
},
plugins: [new htmlWebpackPlugin()],
resolveLoader: {
modules: ["./my_loader", "node_modules"],
},
};

myMd-loader.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
const { marked } = require("marked");
const hljs = require("highlight.js");
module.exports = function (content) {
// 代码高亮
marked.setOptions({
highlight: function (code, lang) {
return hljs.highlight(lang, code).value;
},
});
// md转为htnl结构
const htmlContent = marked(content);
console.log(htmlContent);
// 返回的结果必须是模块化的内容
const innerContet = "`" + htmlContent + "`";
const moduleContent = `var code = ${innerContet}; export default code;`;
return moduleContent;
};

main.js

1
2
3
4
5
6
7
8
9
const index = require("./index");
import code from "./index.md";
// highlight预设的css
import "highlight.js/styles/default.css";
import "./css/index.css";
const message = "自定义loader";
console.log(message, code);
document.body.innerHTML = code;

注意如果不用 highlight 默认的代码高亮 css

需要自己编写 css 如:

1
2
3
.hljs-string{
color:red;
}

因为这一步

1
2
3
4
5
marked.setOptions({
highlight: function (code, lang) {
return hljs.highlight(lang, code).value;
},
});

其实就是给对应的标签节点加上对应的 class