自定义loader基本方法,节选自 webpack实战。
1、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;在webpack工程目录下使用相对路径安装,会在项目的node_modules中创建一个指向实际force-strict-loader目录的软链,也就是说之后我们可以随时修改loader源码并且不需要重复安装了
- 修改webpack配置
module: { rules:[ { test: /\.js$/, use: 'force-strict-loader' } ] }
我们将这个loader设置为对所有js文件生效,此时对该工程进行打包,应该可以看到js文件的头部都已经加上了启用严格模式的语句。
2、启用缓存
当文件输入和其依赖没有发生变化时,应该让loader直接使用缓存。在webpack中可以使用this.cacheable进行控制,修改我们的loader。
//force-strict-laoder/index.js module.exports = function(content){ if(this.cacheable){ this.cacheable(); } var useStrictPrefix = '\'use strict\';\n\n'; return useStrictPrefix + content; }
通过启用缓存可以加快webpack的打包速度,并且可保证相同的输入产生相同的输出。
3、获取options
loader的配置项通过use.options传进来,如:
{ test: /\.js$/, use: [ { loader: 'force-strict-loader', options: { sourceMap: true } }, ], }
下面我们在loader中获取它
需要先安装一个依赖库 "loader-utils"
// force-strict-laoder/index.js
var loaderUtils = require('loader-utils'); module.exports = (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可以获取到配置对象,这里我们只是把它打印了出来。
4、实现Soure-Map
source-map可以便于实际开发者在浏览器控制台查看源码。如果没有对source-map进行处理,最终也就无法生成正确的map文件,在浏览器的dev tool中可能就会看到错讯的源码。
下面是支持了source-map特性后的版本:
var loaderUtils = require('loader-utils'); var SourceNode = require('source-map').SourceNode; var SourceMapConsumer = require('source-map').SourceMapConsumer; module.exports = (content, sourceMap) => { var useStrictPrefix = '\'use strict\'\n\n'; if(this.cacheable){ this.cacheable(); } // 获取和打印 options var options = loaderUtils.getOptions(this) || {} console.log('options', options); // source-map if(options.sourceMap && sourceMap){ var currentRequest = loaderUtils.getCurrentRequest(this); var node = SourceNode.fromStringWithSourceMap(content, new SourceMapConsumer); } 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。