手摸手写个webpack loader

目录:

  1. 基本概念
  2. loader开发入门
  3. 本地loader调试
  4. 发布并引用loader
  5. Demo: 雪碧图loader

1. 基本概念

众所周知,webpack是个模块打包器。但是webpack只能处理js和json文件。

loader 让 webpack 能够去处理其他类型的文件,并将它们转换为有效模块,以供应用程序使用,以及被添加到依赖图中。

loader本质上是一个导出函数的JS模块,函数的入参和出参可以理解为文件流(String或Buffer类),函数对传入的文件流进行处理,然后返回处理后的新文件流。

loader可以是同步的:

module.exports = function (content, map, meta) {
  // content就是传进来的文件内容
  // 对content进行处理
  const newContent = doSomething(content);
  return newContent; // 返回处理后的文件内容
};

也可以是异步的:

module.exports = function (content, map, meta) {
const callback = this.async(); // 获取到callback函数
// 对content进行处理
const newContent = doSomething(content);
// callback的参数有4个,按顺序分别是:
// 1. 错误参数Error或null,必传
// 2. String/Buffer类型的content,必传
// 3. 可选参数sourceMap
// 4. 可选参数meta
callback(null, newContent, map, meta);
};

官方建议尽量用异步loader。

在webpack社区中有很多第三方loader,安装完成后在webpack.config.js中配置即可使用:

// webpack.config.js配置loader
module: {
    rules: [{
        test: /\.css$/i,
        use: ['style-loader', 'css-loader']
    }]
},

上面配置了两个常用的样式处理loader:style-loader和css-loader。

rules规则数组用于指定对模块应用哪些loader。

可以看到rules里的对象有两个属性:

  • test属性的值是个正则表达式,用于进行文件类型匹配,这里匹配的是以.css或.CSS结尾的文件;
  • use属性的值是一个loader数组,指明要对这些css文件执行哪些loader操作。这里多个loader会从右到左进行链式调用,比如上面的配置中,会先执行css-loader,再执行style-loader。和gulp的task有点类似,但是执行顺序不一样,gulp是从左到右顺序执行;而loader更像是复合函数,从右往左执行。

目前为止,我们已大致了解了loader是什么,有什么用,怎么配置使用。

2. loader开发入门

首先npm init -y初始化项目,这里我的项目名为yzz-sprite-loader
cd进入项目,创建index.js文件,内容如下:

// yzz-sprite-loader/index.js
module.exports = function (content, map, meta) {
    const callback = this.async();
    callback(null, content, map, meta);
}

至此,我们就写完了一个啥也不干的异步loader。

官方对于loader开发给出了一些参考准则:

  • 保持简单,一个loader只作一件事
  • 利用链式调用
  • 模块化
  • 无状态,每次运行都与之前的运行结果无关
  • 利用loader-utils包
  • 用addDependency标明使用的外部文件
  • 需要解决代码中的模块依赖问题,比如css中的 @import,可以转换成require方式等
  • 提取公共代码
  • 避免绝对路径
  • 使用 peerDependency

https://webpack.docschina.org/contribute/writing-a-loader/#guidelines

3. 本地loader调试

写完了loader,怎么在项目中引用调试?

先简单搭建一个webpack项目test-webpack

  1. cd test-webpack,进入项目
  2. npm init -y,初始化项目
  3. npm install webpack webpack-cli --save-dev,安装webpack依赖
  4. 新增文件:webpack.config.js,index.html,src/index.js,src/style.css
  5. test-webpack/webpack.config.js中引入我们的loader:
// test-webpack/webpack.config.js
const path = require("path");


module.exports = {
    entry: "./src/index.js",
    mode: "development",
    output: {
        filename: "main.js",
        path: path.resolve(__dirname, "dist"),
    },
    module: {
        rules: [{
            test: /\.css$/i,
            use: [{
                loader: path.resolve("../yzz-sprite-loader/index.js"),
            },
            ],
        },
        ],
    },
};

这里使用相对路径引入了本地的loader。

  1. test-webpack/index.js中引入style.css:
import './style.css'; 

为了验证我们的loader正常运行,在yzz-sprite-loader/index.js中加一句console:

module.exports = function (content, map, meta) {
    const callback = this.async();
    console.log('my loader is running');
    callback(null, content, map, meta);
}

在test-webpack路径下执行yarn run webpack,就能看到控制台打印出my loader is running

这里也可以通过在package.json里配置build:webpack脚本再执行npm/yarn run build

成功!✅

4. 发布并引用loader

只需三步🚶🚶🚶:

  1. cd yzz-sprite-loader进入loader项目
  2. npm login登录npm账号
  3. 执行npm publish

yzz-sprite-loader发布成功

引用方法如下:

在test-webpack项目中执行npm i yzz-sprite-loader -D,并修改webpack.config.js:

rules: [{
    test: /\.css$/i,
    use: ['yzz-sprite-loader'],
}],

至此,我们的loader除了啥也不干之外,已基本上线完成~

5. Demo: 雪碧图loader

是时候加点实用功能了!

相信大部分切图仔都用过或者听说过雪碧图CSS Sprite,实际上就是把多张背景图合并到一张图片中以减少http请求次数,再利用CSS的background-position对合成的图片进行精确定位。

这里我直接用了一个开源的工具包:https://github.com/twolfson/spritesmith

spritesmith的用法如下:

var Spritesmith = require('spritesmith');

// 生成雪碧图
Spritesmith.run({
  src: [], // 要合成的图片数组,值为图片路径
}, function handleResult (err, result) {
  // If there was an error, throw it
  if (err) {
    throw err;
  }

  // 输出雪碧图result.image
  // result.coordinates是个map对象,键为图片路径,值为一个保存了雪碧图信息的对象,包括雪碧图的水平定位x、垂直定位y、图片宽度width、图片高度height
  
});

可以看到,这个插件需要我们把想合成的图片路径都传进去。因此,实现思路如下:

  1. 用正则匹配,收集原始content中有设置背景图的语句,如backgrount:url(…);
  2. 将匹配到的图片路径保存到一个数组里,用于传给Spritesmith以合成雪碧图;
  3. 将匹配到的完整语句保存下来,用于后续的css代码替换;
  4. 将雪碧图保存到某个指定目录下,然后遍历图片路径数组,将content中的对应背景图设置代码替换成雪碧图定位方式;
  5. 最后返回替换后的content。

代码实现比较考验正则功力,这里就不展开细讲了(主要我的正则也很一般,勉强能用~),同时webpack对css的处理还需要搭配style-loader和css-loader使用,感兴趣的朋友可以直接看demo:https://github.com/youzouzou/webpack-sprites-loader-demo

按照项目中的README说明如此这般操作后,可以看到我这周摸的鱼🐟:

两个小图标被合成了一张图sprites.png,运行后可以看到css背景代码也已被替换:

⚠️重要提示:yzz-sprite-loader仅为学习自定义loader的实现,不具备通用性,切勿在生产环境中使用!!!

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值