file-loader、url-loader实现

废话不多说,下面开始实现自己的 file-loader 和 style-loader。

1. 准备工作

基本环境请参考之前的文章《babel-loader实现》,下面着重介绍一下改动部分。
根目录下新建 images 文件夹,并放入名为 logo.jpg 的图片用于测试。

mkdir images

loaders 文件夹下新建 file-loader.js、url-loader.js 文件。
删除已经存在的 a.js 文件,删除 index.js 的无用逻辑。

 cd loaders && touch file-loader.js && touch url-loader.js

index.js 新增如下代码。

/**
 * @file 入口文件
 * @module src/index
 * @version 0.1.1
 * @author yueluo <yueluo.yang@qq.com>
 * @time 2020-06-27
 */

/**
 * @requires images/logo  测试图片
 */
import logo from '../images/logo.jpg';

// 创建IMG标签
const img = document.createElement('img');

// 为IMG标签设置引用路径
img.src = logo;

// 添加IMG标签到body元素中
document.body.appendChild(img);

dist 目录中新建 index.html 文件,并引入打包后的 bundle.js 文件。

cd dist && touch index.html

当前目录结构如下。

file1.png

2. file-loader实现

(1)修改webpack.config.js文件

增加匹配图片的规则,并指明使用的 loader 为 file-loader。

/**
 * @file webapck配置文件
 * @module webpack.config.js
 * @version 0.1.1
 * @author yueluo <yueluo.yang@qq.com>
 * @time 2020-06-27
 */

/**
 * @requires node_modules/path node内置模块
 */
const path = require('path');

// 导出webpack配置
module.exports = {
  mode: 'development',
  entry: './src/index.js',
  output: {
    filename: 'bundle.js',
    path: path.resolve(__dirname, 'dist')
  },
  resolveLoader: {
    modules: ['node_modules', path.resolve(__dirname, 'loaders')]
  },
  devtool: 'source-map',
  module: {
    rules: [
      {
        test: /\.jpg$/,
        use: 'file-loader'
      }
    ]
  }
}

(2)编写file-loader

使用 webpack 工具类(loader-utils)对图片文件进行处理,并指定文件名称。
文件类型的数据,还需要设置 loader 的 raw 属性为 true,将其作为二进制数据处理。

/**
 * @file file-loader
 * @module loaders/file-loader
 * @version 0.1.1
 * @author yueluo <yueluo.yang@qq.com>
 * @time 2020-06-27
 */

/**
 * @requires node_modules/loader-utils webpack工具
 */
const loaderUtils = require('loader-utils');

/**
 * @description 自定义loader
 * @param {string} sourceCode - 源代码 
 * @return {string}
 */
function loader (sourceCode) {
  const filename = loaderUtils.interpolateName(this, '[hash].[ext]', {
    content: sourceCode
  });

  // 文件提交
  this.emitFile(filename, sourceCode);

  return `module.exports = "${filename}"`;
}

// 二进制数据处理
loader.raw = true;

// 导出自定义loader
module.exports = loader;

编写完 file-loader 后,运行 npx webpack 命令。

file2.png

运行 dist 目录下的 index.html。

file3.png

可以看到图片已经正常加载出来,图片的引用路径也是打包后的路径。

3. url-loader实现

url-loader 可以看作是 file-loader 的升级版,使用 url-loader 必须安装 file-loader。
url-loader 内部依赖 file-loader,它可以根据指定的字节大小,对图片进行 base64 编码或者图片文件的解析处理。下面就开始实现 url-loader。

(1)修改webpack.config.js文件

修改匹配到图片时处理的 loader 为 url-loader,并添加字节大小限制。

/**
 * @file webapck配置文件
 * @module webpack.config.js
 * @version 0.1.1
 * @author yueluo <yueluo.yang@qq.com>
 * @time 2020-06-27
 */

/**
 * @requires node_modules/path node内置模块
 */
const path = require('path');

// 导出webpack配置
module.exports = {
  mode: 'development',
  entry: './src/index.js',
  output: {
    filename: 'bundle.js',
    path: path.resolve(__dirname, 'dist')
  },
  resolveLoader: {
    modules: ['node_modules', path.resolve(__dirname, 'loaders')]
  },
  devtool: 'source-map',
  module: {
    rules: [
      {
        test: /\.jpg$/,
        use: {
          loader: 'url-loader',
          options: {
            limit: 100 * 1024
          }
        }
      }
    ]
  }
}

(2)url-loader实现

安装图片格式处理的库 mime。

npm i mime --save-dev 

url-loader的核心逻辑就是根据其配置项,进行 base64 处理或者使用 file-loader 处理。

/**
 * @file url-loader
 * @module loaders/url-loader
 * @version 0.1.1
 * @author yueluo <yueluo.yang@qq.com>
 * @time 2020-06-27
 */

/**
 * @requires node_modules/loader-utils webpack工具
 * @requires node_modules/mime 图片格式处理工具
 */
const loaderUtils = require('loader-utils'),
      mime = require('mime');

/**
 * @description 自定义loader
 * @param {string} sourceCode - 源代码 
 * @return {string}
 */
function loader (sourceCode) {
  const options = loaderUtils.getOptions(this),
        limit = options.limit;

  if (limit && limit > sourceCode.length) {
    // base64处理
    return `module.exports = "data:${mime.getType(this.resourcePath)};base64,${sourceCode.toString('base64')}"`;
  } else {
    // 使用file-loader处理
    return require('./file-loader').call(this, sourceCode);
  }
}

// 二进制数据处理
loader.raw = true;

// 导出自定义loader
module.exports = loader;

好了,url-loader 实现完毕。执行 npx webpack 命令进行打包。

file4.png

可以很明显的看到,处理的文件中并没有图片文件,这是个好消息。
下面再运行 dist 目录下的 index.html 进行测试。

file5.png

可以看到,图片依旧可以正常显示,并且引入的方式是 base64 编码格式。
这张图片的大小是 40kb,webpack配置的限制为 100kb,所以图片被 base64 处理。
下面修改一下字节限制为 20kb。再运行 npx webpack 命令进行打包。

module: {
  rules: [
    {
      test: /\.jpg$/,
      use: {
        loader: 'url-loader',
        options: {
          limit: 20 * 1024
        }
      }
    }
  ]
}

file7.png

可以看到,图片文件经过 url-loader 时,选择 file-loader 进行处理。

4. 总结

通过 file-loader 和 url-loader 的实现,希望你可以对 loader 的理解更加深刻。
在之后的工作中,也可以根据项目的实际需求,实现自己的 loader。💪