loader承担的是翻译官的职责,利用其弥补了让webpack只能理解JavaScript和JSON文件的问题,从而能够处理其它类型的文件,因此loader对webpack的重要性不言而喻,因此学习构建一个loader是学习webpack的必经之路。在学习编写一个loader以前,要明确一下loader的职责: 其职责是单一的,只须要完成一种转换。下面将逐步阐述选择loader开发中的几个关键点并实现一个loader。
loader是一个CommonJs风格的函数,接收输入的source后可经过同步或异步的方式进行处理,而后将内容进行输出。
同步loader指的是同步的返回转换后的内容。,因为是在Node.js这样的单线程环境,因此转换过程会阻塞整个构建,构建缓慢,不适用于耗时较长的环境中。对于同步loader,主要有两种方法返回转换后的内容:return和this.callback.
利用return可直接返回转换后结果。css
module.exports = function(source, map, meta){ // ... // output为处理后结果 return output; }
该方法相比于return更加灵活,其参数主要有四个:html
this.callback( err: Error | null, content: string | Buffer, sourceMap?: SourceMap, meta?: any );
(1)第一个参数为没法转换原内容是,Webpack会返回一个Error。<br/>
(2)第二个参数即为通过转换后的内容(为输出的内容)。<br/>
(3)指与编译后代码所映射的源代码,便于调试。为了在此loader中获取该sourceMap,则须要在建立的webpack作一下配置(以js为例,babel-loader会将基础ES6语法进行转换为ES5,经过devtool能够开启source-map):node
// webpack.config.js module.exports = { // ... module: { rules: [ { test: /\.js$/, use: [ 'test-loader',// 该loader即为本身构建的loader { loader: 'babel-loader', options: { presets: [ '@babel/preset-env' ] } } ] } ] }, devtool: 'eval-source-map', }
(4)能够是任何东西,输出该参数,便可在下一个loader中获取并使用,例如经过各loader之间共享通用的AST,加速编译时间。webpack
利用this.callback可返回传递多参数的结果。
module.exports = function(source, map, meta) { // 处理后得到的结果output const output = dealOperation(source); this.callback(null, output, map, meta); }
同步loader只适合于计算量小,速度快的场景,可是对于计量算大、耗时比较长的场景(例如网络请求),使用同步loader会阻塞整个构建过程,致使构建速度变慢,采用异步loader便可避免该问题。对于异步loader,使用this.async()能够获取到callback函数,该函数参数和同步loader中this.callback参数一致。
module.exports = function(content, map, meta) { // 获取callback函数 const callback = this.async(); // 用setTimeout模拟该异步过程 setTimeout(() => { // 处理后得到的结果output const output = dealOperation(source); callback(null, output, map, meta); }, 100) }
默认状况下,资源文件经转化后都是UTF-8格式编码的字符串,可是对于图片这样的文件通过转化后是二进制格式的内容,为了让loader支持接收二进制资源,须要设置raw(以图片资源为例进行展现)
// webpack.config.js module.exports = { // ... module: { rules: [ { test: /\.(png|jpg|gif)$/, use: [ 'url-loader', 'raw-test-loader',// 本身的loader ] } ] } }
// raw-test-loader.js module.exports = function(source, map, meta) { // 处理输入的资源 const output = dealOperation(source); return output; } // 经过该属性告诉webpack该loader是否须要二进制数据 module.exports.raw = true;
对于webpack配置中,loader每每有一些options参数,对于本身编写的loader中为了获取options参数,官方推荐使用loader-utils包,利用该包便可获取options中参数,而后在loader中进行处理。
const loaderUtils = require('loader-utils'); module.exports = function (source, map, meta){ // 获取options const options = loaderUtils.getOptions(this); const output = dealOperation(source); return output; }
对于转换操做须要大量的计算,很是耗时,每次从新构建会让构建过程变的很是缓慢。webpack会默认缓存全部loader的处理结果,即要处理文件和其相关依赖没发生变化就会利用其缓存(注意loader除了this.addDependency里指定的依赖外,不该该有任何外部依赖)。经过this.cacheable可控制其是否进行缓存。
module.exports = function(source, map, meta) { // 关闭缓存 this.cacheable(false); return source; }
本节是loader实战,编写了一个用于字母大小写转换的loader,利用该loader可以实现将txt文件中字母的大小写转换,其loader内容及webpack.config.js相关配置以下所示(详细代码见 github上代码)
// format-letters-loader.js const loaderUtils = require('loader-utils'); const Lowercase2Uppercase = 'L2U'; const Uppercase2Lowercase = 'U2L'; module.exports = function (source, map, meta) { let output = ''; // 获取options const options = loaderUtils.getOptions(this); const { formatType } = options; switch(formatType) { case Lowercase2Uppercase: { output = source.toUpperCase(); break; } case Uppercase2Lowercase: { output = source.toLowerCase(); break; } default: { output = source; } } this.callback(null, output, map, meta); };
// webpack.config.js module.exports = { // ... module: { rules: [ { exclude: /\.(css|js|html|png|jpg|gif)$/, use: [ { loader: 'file-loader', options: { name: '[name].[ext]', outputPath: 'asset', } }, { loader: 'format-letters-loader', options: { formatType: 'U2L' } }, ] } ] }, // 解析loader包是设置模块如何被解析 resolveLoader: { modules: ['./node_modules', './loader'],// 告诉 webpack 解析loader时应该搜索的目录。 }, }
注:本文只是起到抛砖引玉的做用,但愿各位大佬多多指点。git
相关章节<br/>
图解Webpack————基础篇
图解Webpack————优化篇欢迎你们关注公众号(回复“深刻浅出Webpack”获取深刻浅出Webpack的pdf版本,回复“webpack04”获取本节的思惟导图”)
github