有时开发者会遇到现有loader无法很好满足需求的情况,这时就需要对其进行修改,或者编写新的loader。
下面以一个简单的示例,讲解以下如何实现一个loader。
需求
实现一个loader,为所有JS文件启用严格模式。也就是说,它会在文件头部加上“use strict”。
创建一个force-strict-loader目录,然后在该目录下执行npm初始化命令。npm init -y
接着创建index.js,也就是loader的主体。
module.exports = function (content) {
var useStrictPrefix = "'use strict';\n\n";
return useStrictPrefix + content;
};
现在就可以在Webpack工程中安装并使用这个loader了。
npm install <path-to-loader>/force-strict-loader -D
在Webpack工程目录下使用相对路径安装,会在项目的node_modules中创建一个指向实际force-strict-loader目录的软链,也就是说之后开发者可以随时修改loader源码并且不需要重复安装了。
下面修改Webpack配置。
// webpack.config.js
module.exports = {
...
module: {
rules: [{
test: /\.js$/,
use: 'force-strict-loader'
}]
}
}
上面的配置为对所有JS文件生效。此时对该工程进行打包,应该可以看到JS文件的头部都已经加上了启用严格模式的语句。
当文件输入和其依赖没有发生变化时,应该让loader直接使用缓存,而不是重复进行转换的工作。在Webpack中可以使用this.cacheable进行控制,修改自定义的loader。
// force-strict-loader/index.js
module.exports = function(content) {
if(this.cacheable) {
this.cacheable();
}
var useStrictPrefix = "\'use strict\';\n\n";
return useStrictPrefix + content;
}
通过启用缓存可以加快Webpack打包速度,并且可保证相同的输入产生相同的输出。
在之前的文章讲过,loader的配置项通过use.options传进来。就是说,在工程配置文件中配置了loader的options对象。
// webpack.config.js
module.exports = {
...
module: {
rules: [{
test: /\.js$/,
use: {
loader: 'force-strict-loader',
options: {
sourceMap: true,
}
}
}]
}
}
上面的配置为force-strict-loader传入了一个配置项sourceMap。要在loader中获取,需要在force-strict-loader中安装一个依赖库loader-utils,它主要用于提供一些帮助函数。在force-strict-loader目录下执行以下命令。
npm install loader-utils -D
接着更改loader。
// force-strict-loader/index.js
var loaderUtils = require("loader-utils");
module.exports = function(content) {
if(this.cacheable) {
this.cacheable();
}
// 获取和打印options
var options = loaderUtils.getOptions(this) || {};
console.log("options",options);
// 处理content
var useStrictPrefix = "'use strict\';\n\n";
return useStrictPrefix + content;
}
通过loaderUtils.getOptions可以获取到配置对象。
开启source-map可以便于开发者在浏览器的开发者工具中查看源码。在上面的示例中,工程配置已开启source-map,但是在force-strict-loader中没有对source-map进行处理,这样会使Webpack无法生成正确的map文件。需要修改force-strict-loader。
// force-strict-loader/index.js
var loaderUtils = require("loader-utils");
var SourceNode = require("source-map").SourceNode;
var SourceMapConsumer = require("source-map").SourceMapConsumer;
module.exports = function(content,sourceMap) {
var useStrictPrefix = "\'use strict\';\n\n";
if(this.cacheable) {
this,cacheable();
}
// source-map
var options = loaderUtils.getOptions(this) || {};
if (options.sourceMap && sourceMap) {
var currentRequest = loaderUtils.getCurrentRequest(this);
var node = SourceNode.fromStringWithSourceMap(content,new SourceMapConsumer(sourceMap));
node.prepend(useStrictPrefix);
var result = node.toStringWithSourceMap({file:currentRequest});
var callback = this.async();
callback(null,result.code,result.map.toJSON());
}
// 不支持source-map情况
return useStrictPrefix + content;
}
首先,在loader函数的参数中获取到sourceMap对象,这时由Webpack或者上一个loader传递下来的,只有当它存在时loader才能进行继续处理和向下传递。
之后通过source-map这个库来对map进行操作,包括接收和消费之前的文件内容和source-map,对内容节点进行修改,最后产生新的source-map。
在函数返回的时候要使用this.async获取callback函数(主要是为了一次性返回多个值)。callback函数的3个参数分别是抛出的错误、处理后的源码,以及source-map。
示例代码库
https://gitee.com/zero_79152105/webpack-vblog
原创文章,作者:ZERO,如若转载,请注明出处:https://www.edu24.cn/course/webpack-custom-loader.html