Webpack loader
一、什么是loader?
loader 是一个文件加载器,能够加载不同的资源,并对这些文件进行操作,如编译,压缩,最终打包到指定的文件。
- 处理一个文件可以使用多个loader进行解析,loader的加载顺序是相反的,会从最后一个loader向上执行
- 每个loader文件都提供一个函数,并且参数是解析文件的内容,当执行多个loader时,每个loader的参数是上个loader的执行结果
二、手写一个loader
需求:删除Javascript代码中所有的console.log
目录结构
|-- package-lock.json
|-- package.json
|-- README.md
|-- webpack.config.js // webpack 配置
|-- loaders
| |-- del-log-loader.js // 删除console.log的loader
| |-- tranform-async.js // 处理异步loader
|-- src
|-- index.js // 目标文件
复制代码
1. 实现删除代码中的console.log
1.1 webpack 配置导出如下
const path = require('path')
module.exports = {
mode: 'development',
entry: {
main: './src/index.js',
},
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'index.js'
},
module: {
rules: [{
test: /\.js$/,
use: [
{
loader: path.resolve(__dirname, 'loaders/del-log-loader.js')
}
]
}],
},
};
复制代码
1.2 src/index.js 源码 如下
class Animation {
run(a, b) {
console.log('running...');
return a + b;
}
}
const an = new Animation();
an.run(1, 2);
复制代码
1.3 编写del-log-loader,该loader主要功能接收目标js,将代码中的console.log
去掉,并返回处理后的结果。
module.exports = function (source) {
const reg = /console.log\([\s\S]*?\);/g;
source = source.replace(reg, '');
return source;
}
复制代码
1.4 在package.json
文件中添加"build": "webpack"
指令
"scripts": {
+ "build": "webpack",
"test": "echo \"Error: no test specified\" && exit 1"
}
复制代码
1.5 执行npm run build
命令,得到处理后的结果
eval("\r\nclass Animation {\r\n run(a, b) {\r\n \r\n return a + b;\r\n }\r\n}\r\nconst an = new Animation()\r\nan.run(1, 2)\n\n//# sourceURL=webpack://demo1/./src/index.js?");
复制代码
2. loader 返回多个参数
通常写loader时,有时需要给下一个loader传额外参数,由于return返回值局限性,然幸运的是loader中提供了一个callback
会掉函数接受多个参数(注意:前三个参数必须是error,source和map)
2.1 修改del-log-loader
module.exports = function (source) {
const reg = /console.log\([\s\S]*?\);/g;
+ const error = null
+ const map = {
+ name: '张三'
+ }
source = source.replace(reg, '');
+ this.callback(error, source, map)
}
复制代码
2.2 添加另一个 loader ,检验结果
module.exports = function (source, map) {
console.log(map)
return source
}
复制代码
3.校验loader中的options参数
在webpack的配置中,通常会给loader传入options以便在loader中使用,为了获取并校验options,官方提供了两个工具包loader-utils
和scheme-utils
。继续完善del-log-loader
,为了让使用者更加灵活,可以通过传参的方式决定生成的sourcemap文件名和是否删除console.log
3.1 webpack.config.js 修改配置
rules: [{
test: /\.js$/,
use: [
{
loader: path.resolve(__dirname, 'loaders/tranform-async.js')
},
{
loader: path.resolve(__dirname, 'loaders/del-log-loader.js'),
+ options: {
+ isDelLog: process.env.NODE_ENV === "production"
+ }
}
]
}],
复制代码
3.2 修改del-log-loader.js,增加获取和校验options的代码
+ const schemaUtils = require('schema-utils'); // 校验options
+ const loaderUtils = require('loader-utils'); // 获取options
+ const schema = {
+ type: 'object',
+ properties: {
+ isDelLog: { type: 'boolean' }
+ },
+ additionalProperties: false
+};
module.exports = function (source) {
+ const options = loaderUtils.getOptions(this);
+ schemaUtils.validate(schema, options, 'del-log-loader');
+ const { isDelLog } = options;
+ if (isDelLog) {
+ const reg = /console.log\([\s\S]*?\);/g;
+ source = source.replace(reg, '');
+ }
const error = null
const map = {
name: '张三'
}
this.callback(error, source, map)
}
复制代码
4. transform-loader开发
transform-loader 是一个异步loader,主要作用是将es6语法转为es5,并将转换后的代码返回。异步loader和同步loader的开发基本一致,区别就是异步loader必须在返回前调用loader的上下文中的async方法。
继续修改webpack.config.js,在rules中增加transform-loader,把transform-loader放在del-log-loader前面,因为普通loader的执行顺序是从后往前,并依次传入返回的值。
use: [
+ {
+ loader: path.resolve(__dirname, 'loaders/tranform-async.js')
+ },
{
loader: path.resolve(__dirname, 'loaders/del-log-loader.js'),
options: {
isDelLog: process.env.NODE_ENV === "production"
}
}
]
复制代码
编辑transform-loader.js,接受del-log-loader返回的source和map进行转换后返回。
const babel = require('@babel/core'); // 使用babel进行转换
module.exports = function (source) {
const callback = this.async();
babel.transform(source, {
presets: ['@babel/preset-env']
}, (err, result) => {
const { code } = result;
callback(err, code, map);
});
}
复制代码
得到结果
var Animation = /*#__PURE__*/function () {
function Animation() {
_classCallCheck(this, Animation);
}
_createClass(Animation, [{
key: "run",
value: function run(a, b) {
console.log('running...');
return a + b;
}
}]);
return Animation;
}();
var an = new Animation();
an.run(1, 2);
复制代码
总结
从一个loader的基本实现,参数的传递,以及异步loader开发,在平日开发中基本能够应付自定义一个loader的所需的一些知识,更加详细可以看webpack的官网。