webpack loaders插件编写

本文主要目的是让阅读者能简单的实现一个loader插件,并且了解loader基本运行机制。

1. 了解什么是loader

官方解释:loader 用于对模块的源代码进行转换。loader 可以使你在 import 或"加载"模块时预处理文件。因此,loader 类似于其他构建工具中"任务(task)",并提供了处理前端构建步骤的强大方法。loader 可以将文件从不同的语言(如 TypeScript)转换为 JavaScript,或将内联图像转换为 data URL。loader 甚至允许你直接在 JavaScript 模块中 import CSS文件!

而我对此定义就是,loader用于处理webpack加载的每个文件

2. 如何加载一个loader

在为webpack做配置的webpack.config.js文件中,loader存放于导出的module.rules中。

ini 复制代码
module.exports = {
  module:{
    rules:[
      {...loader配置项},
      {...loader配置项},
      {...loader配置项}
    ]
  }
}

webpack支持配置内联CLI方式。个人推荐使用配置方式,来为webpack配置一个loader(具体三者区别,请查看文末使用loader)。

现在,来为webpack配置一个CSS解析器用于代码运行时候将打包后CSS代码通过<style>添加到页面头部。

css 复制代码
module: {
    rules: [
      {
        test: /\.css$/,
        use: [
          { loader: 'style-loader' }
        ]
      }
    ]
  }

这个配置中,test 表示如果通过了这个正则表达式文件,则使用use数组中的loader对文件进行处理。在这里有个规则需要说明一下,loader加载解析器的先后顺序是从右往左从下往上

执行webpack则默认开始执行编译,如果项目中包含了CSS文件,会调用style-loader对他进行处理。

另外注意的是loader是运行在node环境下的,也就是说在我们编写的loader是可以调用node上下文与node包来进行各种处理的,而loader插件的区别在于,loader用于解析模块,而插件则是介入了整个webpack生命周期。

3. 编写第一个loader

最近在处理个业务需求,就是将less文件中的图片放置到阿里的CDN上,优化打包速度,减小包体积,并且利用浏览器缓存机制缓存图片。

创建一个loader.js文件,用node导出方式,编写一个空的loader

javascript 复制代码
module.exports = function () {
  
}
// 其实也支持 方式,看喜欢用那种方式吧。
export default function () {
  
}

现在遇到了第一个问题,就是,如何将这个loader文件加入到webpack编译过程中。

官方文件告诉了我们这个结果:

loader 遵循标准的模块解析。多数情况下,loader 将从模块路径(通常将模块路径认为是 npm install, node_modules)解析。

修改配置文件为下面格式:

css 复制代码
module: {
    rules: [
      {
        test: /\.css$/,
        use: [
          { loader: 'style-loader' },
          { loader: path.resolve(__dirname,'./loader.js') }
        ]
      }
    ]
  }

注意这一行{ loader: path.resolve(__dirname,'./loader.js') },我们将整个loader的路径传入给webpack。webpack会使用此路径载入这个loader。就如同const loader = require(path.resolve(__dirname,'./loader.js')) 一样。

OK,现在我们知道了webpack运行loader的小机制之后,开始着手编写这个loader

ini 复制代码
const postcss = require('postcss');
const fs = require('fs');

const file = [];
const errlist = [];

module.exports = function (source) {
      postcss([])
    .process(source)
    .then(value => {
      ...
    });
    return source;
}

由于我们要识别整个CSS中包含的规则,所以使用Postcss来处理css取得css文件的ASTsource为文件内容,传入内容可能是正常的文本内容,也有可能是二进制流(默认均为utf-8编码传入,需要buffer请参考API文档设置 raw)

我们根据需求,从AST中得到自己想要的信息,再把source原模原样传出去,交给下一个loader

现在,我们来测试一下这个解析器是否能正常工作。

运行webpack之后,你会看到类似promise reject之类的异常信息,loader并没有正常执行。这是因为Postcss是异步执行逻辑,而我们的return source则是同步模式所使用的规则。

现在修改loader为异步模式。

javascript 复制代码
const postcss = require('postcss');
const fs = require('fs');

// 保存图片资源地址
const file = []

module.exports = function (source) {
  const callback = this.async();
  	// 加载一个空的 postcss 配置文件
      postcss([])
    // 将source(css text)解析成css AST
    .process(source)
    .then(value => {
      // 遍历所有css name,如: .classname #idname 等等
      value.root.walkRules(rule => {
        // 遍历当前css name下包含的所有的规则
        rule.walkDecls(decl => {
          // {prop,value}
          // prop:规则名
          // value: 规则值
          // 如 backaground:red; 则内容格式如下
          // {prop:'background',value:'red'}
          if (decl.prop === 'background-image' || decl.prop === 'background') {
            // 取得图片地址,并且清洗干净
            // 接着push如存放图片地址的数组
            let url = /url\(.*?\)/.exec(decl.value);
            if (url && url.length > 0) {
              // eslint-disable-next-line prefer-destructuring
              url = url[0];
              url = url.replace('url(', '').replace(')', '');
              url = url.replace(/"/gi, '').replace(/\'/gi, '');
              if (!file.includes(url)) {
                file.push(url);
              }
            } else if (decl.value.indexOf('url') > -1) {
              // 容错机制
              file.push(decl.value);
            }
          }
        });
      });
      // 保存输出内容
      fs.writeFileSync(
        './cssImageFile.txt',
        file.join('\n')
      );
      return callback(source);
    });
}

这样,我们就完成了一个异步处理的loader

this上,webpack挂接了很多相关的信息,如sourcepathasync。编写loader时,请及时查阅loader API文档。

参考

官方文档:使用loader

www.webpackjs.com/concepts/lo...

官方文档:编写一个loader

www.webpackjs.com/contribute/...

官方文档:loader API

webpack.docschina.org/api/loaders...

By: yodfz

相关推荐
Martin -Tang32 分钟前
vite和webpack的区别
前端·webpack·node.js·vite
迷途小码农零零发33 分钟前
解锁微前端的优秀库
前端
王解1 小时前
webpack loader全解析,从入门到精通(10)
前端·webpack·node.js
老码沉思录1 小时前
写给初学者的React Native 全栈开发实战班
javascript·react native·react.js
我不当帕鲁谁当帕鲁2 小时前
arcgis for js实现FeatureLayer图层弹窗展示所有field字段
前端·javascript·arcgis
那一抹阳光多灿烂2 小时前
工程化实战内功修炼测试题
前端·javascript
放逐者-保持本心,方可放逐2 小时前
微信小程序=》基础=》常见问题=》性能总结
前端·微信小程序·小程序·前端框架
毋若成5 小时前
前端三大组件之CSS,三大选择器,游戏网页仿写
前端·css
红中马喽5 小时前
JS学习日记(webAPI—DOM)
开发语言·前端·javascript·笔记·vscode·学习
Black蜡笔小新6 小时前
网页直播/点播播放器EasyPlayer.js播放器OffscreenCanvas这个特性是否需要特殊的环境和硬件支持
前端·javascript·html