Webpack Loaders 详解

495 阅读1分钟

webpack loaders是webpack实现对非js文件打包的重要工具,本文章主要记录自身对loaders工作流程的学习。

  1. webpack.config.js中使用loaders

针对特定结尾文件,使用loaders有两种方法,第一种是use属性配置为数组,包含所需要的loader,第二种方法是use属性配置为对象,可以进行options配置。

如果不进行特殊配置,loader知会在node_modules里面查找是否有安装,如果需要执行其他loader,需要在webpack.conf.js配置resolveLoader属性:

// <dir_root>/loader/loader1.js
function loader(params) {
  console.log("loader1~~~~~");
  return params;
}
module.exports = loader;

// <dir_root>/loader/loader2.js
function loader(params) {
  console.log("loader1~~~~~"); 
  return params;
}
module.exports = loader;

// <dir_root>/loader/loader3.js
function loader(params) {
  console.log("loader1~~~~~");
  return params;
}
module.exports = loader;

// <dir_root>/webpack.config.js
module.exports = {
  mode: "development",
  // JavaScript 执行入口文件
  entry: "./src/main.js",
  output: {
    // 把所有依赖的模块合并输出到一个 bundle.js 文件
    filename: "bundle.js",
    // 输出文件都放到 dist 目录下
    path: path.resolve(__dirname, "./dist"),
  },
  // 先到node_modules里面找,找不到就去loader目录下找
  resolveLoader: {
    modules: ["node_modules", path.resolve(__dirname, "loader")],
  },
  devtool: "source-map",
  // watch: true,
  module: {
    rules: [
     // 第一种
      {
        test: /\.js$/,
        use: ["loader3", "css-loader2", "less-loader1"],
      },
      // 第二种
      {
        test: /\.js$/,
        use: {
          loader: "loader3",
        },
      },
      {
        test: /\.js$/,
        use: {
          loader: "loader2",
        },
      },
      {
        test: /\.js$/,
        use: {
          loader: "loader1",
        },
      },

      
    ],
  },
};

  1. loader的执行顺序

loader的执行顺序总结为从右到左,从下到上,上述例子中两种配置方式输出都是loader1、loader2、loader3。

如果需要调整顺序,需要loader的enforce属性。enforce如果被配置为pre,则会最新执行,post则会最后执行,上述例子中第二种方式如果改为:

{
  test: /\.js$/,
  use: {
    loader: "loader3",
  },
  enforce: 'pre'
},
{
  test: /\.js$/,
  use: {
    loader: "loader2",
  },
},
{
  test: /\.js$/,
  use: {
    loader: "loader1",
  },
  enforce: 'post'
},

输出会变为loader3,loader2,loader1。

根据配置,loader被分为4类: pre,normal,post,iniline。下面介绍inline

  1. inline-loader

除了在webpack.config.js中配置loader,也可以使用行内loader,在引用一个文件时即指定该文件处理的loader

// <dir_root>/loader/inline-loader.js
function loader(params) {
  console.log("inline-loader~~~~~");
  return params;
}
module.exports = loader;

//入口文件 <dir_root>/src/main.js
let b = require("inline-loader!./b");
console.log("b");

// <dir_root>/src/b.js
module.exports = "b";

上述配置加上enforce后输出为

// 处理main.js
loader3
loader2
loader1
// 处理b.js
loader3
loader2
inline-loader
loader1

** 执行顺序:pre normal inline post **

如果不希望执行inline-loader前面的loader,则配置为 let b = require("-!inline-loader!./b");,具体有关文档参考官方,官方文档中configured loaders意为通过webpack.config.js配置的文档。

pitch

如果loader有被设置pitch,那么先执行pitch,再执行loader本体。

//loader1
loader.pitch = function(remainingRequest, precedingRequest, data) {
  console.log('loader1')
};
//loader2 loader3 类上

按照上述修改loader,去掉对b文件的引用,输出为:

// pitch 中的输出
loader1
loader2
loader3
// loader函数的输出
loader3
loader2
loader1

执行顺序为:loader3.pitch -> loader2.pitch -> loader1.pitch -> 获取资源 ->loader1 -> loader2 ->loader3

如果 pitch 中 loader2.pitch 有返回值 则直接跳到 normal 中的 loader3 中间全部不执行

如果 pitch 中 loader3.pitch 有返回值 则只执行 loader3-pitch

** 注意最后一个执行的 loader 或者说排在第一个的 loader 必须返回 eval 函数可执行的 string or buffer **

  1. 尝试手写loader

手写loader前首先了解一下什么是loaderContext,对于loader函数,this均被设置为loaderContext,拥有很多自带属性和自带方法。

手写file-loader用于加载文件或图片

loader的基本格式如下:

let loaderUtils = require("loader-utils");
function loader(source) {
  
}
module.exports = loader;

loaderUtils库是非常常用的处理库,可以不引用,但一般都会用到。

file-loader做到的功能就是将传入的文件重命名为哈希值加上文件扩展名,发送到输出文件夹中,由于处理文件需要输入为二进制,因此需要设置为loader.raw = true,实现如下:

let loaderUtils = require("loader-utils");
function loader(source) {
	
  // interpolateName获取文件的hash值,并插入值,生成唯一的文件名
  let filename = loaderUtils.interpolateName(this, "[hash].[ext]", {
    content: source,
  });
  // this.emitFile为loaderContext中自带的函数,发送文件到输出文件夹
  this.emitFile(filename, source);
  return `module.exports = "${filename}"`;
}
loader.raw = true; //这样拿到的源码就成了二进制

module.exports = loader;

编写url-loader,该loader可以判断图片文件大小,如果文件太大,则使用file-loader发送,否则将图片转为base64编码

//webpack.config.js
  {
    test: /\.jpg$/,
    use: {
      loader: "url-loader",
      options: {
        limit: 200 * 1024,
      },
    },
  },
  
 // url-loader 
let loaderUtils = require("loader-utils");
let fileLoader = require("./file-loader");
let fs = require("fs");
let mime = require("mime");
let path = require("path");
function loader(source) {
  let { limit } = loaderUtils.getOptions(this);
  // 判断大小
  if (limit && source.length < limit) {
  	// mime 用于获取文件类型
    return `module.exports="data:${mime.getType(
      source
    )};base64,${source.toString("base64")}"`;
  } else {
    // 返回file-loader处理
    return fileLoader.call(this, source);
  }
}
loader.raw = true;
module.exports = loader;