8

怎么样才能玩转webpack之loader开发

 4 years ago
source link: https://studygolang.com/articles/25682
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

webpack提倡一切皆模块,所有类型的文件都可以经过文件加载器处理变成我们可加载的模块,那么这个文件加载器便是loader。

那么我们如何开发一个webpack loader呢,让我们一起探索探索吧~

一、loader执行顺序 在开发loader之前,我们先了解一下webpack loader的执行顺序。

webpack是支持loader的链式调用的,即一个文件可以经多个loader处理。当一个文件使用多个loader处理时,他的处理顺序是倒序,即传入loader数组的从右到左执行。

例如,对于scss文件,我们的配置如下,那么它的执行顺序是sass-loader -》 css-loader -》 postcss-loader -》style-loader:

module: { rules: [ { test: /.scss|.css/, use: [ 'style-loader', { loader: 'css-loader', options: { importLoaders: 2, }, }, 'postcss-loader', loader: 'sass-loader', ], } } 二、loader开发 那么如何来开发一个loader呢?让我们慢慢来揭开webpack loader 的什么面纱~

loader其实是一个导出为函数的 JavaScript模块,是不是看起来很简单?实际呢,loader开发也很简单。即

test.loader.js内容如下:

module.exports = function(content) { return transform(content); // 对content进行处理并返回给webpack } 1、loader的传入参数 既然我们说了所谓 loader 只是一个导出为函数的 JavaScript模块,那么它的传入是什么呢?

content: string | Buffer, // 文件内容 sourceMap?: SourceMap, // 上一个loader解析完后生成的 source map meta?: any // 会被 webpack 忽略,可以是任何东西(例如一些元数据) 显然loader就是对文件进行处理的,那么这里的content便是文件内容。

默认情况下,资源文件会被转化为 UTF-8 字符串,然后传给 loader。通过设置 raw,loader 可以接收原始的 Buffer。我们常用的file-loader就设置了raw为true,以告诉webpack传入原始的二进制数据

module.exports.raw = true; 需要注意的是:第一个 loader 的传入参数只有一个:资源文件的内容content。其他都是经过loader处理后可选择传递给下一个loader的。

2、loader处理结果 loader返回的处理结果应该和传入一样是 String 或者 Buffer。 上面有讲到除了content,loader其实还接受两个可选的入参,返回也一样。所以当我们如果是单个处理结果,可以在函数中直接返回。但是如果有多个处理结果,我们则必须通过this.callback()将处理结果传递给下一个loader。

module.exports = function(content) { const newContent = transform(content); // 对content进行处理 this.callback(null,newContent, sourceMaps, meta); return; // 当使用this.callback时函数应该return undefined } 这里的this既不是webpack实例,也不是compiler、compilation、normalModule等这些实例。而是loader-runner构造的loaderContext对象,提供了各种loader API(具体API可见 http://www.5gwanglu.com/a/fenxi/388.html

对于meta参数,一般传入抽象语法树(abstract syntax tree - AST),这样可以在多个 loader 之间共享通用的 AST,这样做有助于加速编译时间。

所以总结来说,loader的工作流程是:

最后的 loader 最早调用,将会传入原始资源内容。 第一个 loader 最后调用,期望值是传出 JavaScript和 source map(可选)。 中间的 loader 执行时,会传入前一个 loader 传出的结果。 3、获取用户自定义参数 到这里基本已经清楚了loader的整个工作流程。我们在使用loader时,经常会传入一些自定义的options,那么loader怎么获取这些options呢?

webpack 提供了loader-utils包和schema-utils 包。loader-utils提供了许多有用的工具,但最常用的一种工具是获取传递给 loader 的选项。schema-utils 包配合 loader-utils,用于保证 loader 选项,进行与 JSON Schema 结构一致的校验。

const loaderUtils = 'loader-utils'; module.exports = function(content) { const options = loaderUtils.getOptions(this); // 用户传入的options return transform(content); // 对content进行处理并返回给webpack } 4、控制loader执行 前面讲到过,webpack的loader执行顺序是从后往前。有些时候我们希望选择性的越过后续loader执行,webpack给每个loader提供了pitch方法进行设置。

根据webpack官网给出的案例,对于下面的配置:

module.exports = { //... module: { rules: [ { //... use: [ 'a-loader', 'b-loader', 'c-loader' ] } ] } }; webpack 在实际(从右到左)执行 loader 之前,会先从左到右调用 loader 上的 pitch 方法。所以实际执行顺序如下:

|- a-loader pitch |- b-loader pitch |- c-loader pitch |- requested module is picked up as a dependency |- c-loader normal execution |- b-loader normal execution |- a-loader normal execution pitch方法若有返回值,则会跳过后续的loader。比如上面如果 b-loader 的 pitch 方法有返回值,那么此时loader的执行流程是:

|- a-loader pitch |- b-loader pitch returns a module |- a-loader normal execution 三、举个栗子 接下来我们来开发一个自己的loader

比如现在有个场景,要求我们给所有的apng 请求url加上参数?nowebp=1。loader代码如下:

apng-url-resolve.js

module.exports = function(content) { return content.replace(/.apng(.*.png)?/, '.apng$1?nowebp=1') } webpack loader 配置:

const path = require('path'); modules: { rules: [ { test: /.js$/ use: path.resolve(__dirname, 'build/loaders/apng-url-resolve.js' ) } ] }

文章来源:http://www.5gwanglu.com/a/fenxi/388.html

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:muxilin131420 备注:入群;或加QQ群:729884609


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK