前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >玩转webpack之loader开发

玩转webpack之loader开发

作者头像
IMWeb前端团队
发布2019-12-04 16:19:48
5920
发布2019-12-04 16:19:48
举报
文章被收录于专栏:IMWeb前端团队IMWeb前端团队

本文作者:IMWeb 嵘么么 原文出处:IMWeb社区 未经同意,禁止转载

前言

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

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

一、loader执行顺序

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

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

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

代码语言:javascript
复制
  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内容如下:

代码语言:javascript
复制
module.exports = function(content) {
    return transform(content);  // 对content进行处理并返回给webpack
}

1、loader的传入参数

既然我们说了所谓 loader 只是一个导出为函数的 JavaScript模块,那么它的传入是什么呢?

代码语言: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传入原始的二进制数据

代码语言:javascript
复制
module.exports.raw = true;

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

2、loader处理结果

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

代码语言:javascript
复制
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可见 https://webpack.js.org/api/loaders/ )。

对于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 结构一致的校验。

代码语言:javascript
复制
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官网给出的案例,对于下面的配置:

代码语言:javascript
复制
module.exports = {
  //...
  module: {
    rules: [
      {
        //...
        use: [
          'a-loader',
          'b-loader',
          'c-loader'
        ]
      }
    ]
  }
};

webpack 在实际(从右到左)执行 loader 之前,会先从左到右调用 loader 上的 pitch 方法。所以实际执行顺序如下:

代码语言:javascript
复制
|- 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的执行流程是:

代码语言:javascript
复制
|- a-loader `pitch`
  |- b-loader `pitch` returns a module
|- a-loader normal execution

三、举个栗子

接下来我们来开发一个自己的loader

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

apng-url-resolve.js

代码语言:javascript
复制
module.exports = function(content) {
  return content.replace(/\.apng(.*\.png)?/, '.apng$1?nowebp=1')
}

webpack loader 配置:

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

总结

这篇文章从webpack loader的工作流程出发讲解了如何开发一个自定义loader,下篇文章将讲解webpack plugin 的开发,敬请期待~

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2019-08-07 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前言
  • 一、loader执行顺序
  • 二、loader开发
    • 1、loader的传入参数
      • 2、loader处理结果
        • 3、获取用户自定义参数
          • 4、控制loader执行
          • 三、举个栗子
          • 总结
          领券
          问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档