webpack plugins vs loader

100 阅读2分钟

loader

构建过程中用来转换代码的,不能够影响到构建流程,在构建过程中,匹配到对应的文件loader,就会去执行,转换代码; 为什么npm上的loader只需要安装,不需要require,写上名称,因为webpack默认会自己在node_modules里面resolve 格式:

基础定义

// 不可以使用箭头函数,会丢失上下文
module.exports = function(source) {
  return source;
}

input和output都必须是string;

如何使用

  • 在loader中直接写字符串,但是,需要在resolveLoader中,声明字符串的别名
const path = require("path");

module.exports = {
  module: {
    rules: [{
      test: /\.js$/,
      exclude: './node_modules',
      use: ['babel-loader', 'customer-loader']
    },]
  },
  resolveLoader: {
    alias: {
      'customer-loader': path.join(__dirname, 'customer-loader');
    }
  }
}
  • 或者在loader中直接用绝对路径
const path = require("path/posix");

module.exports = {
  module: {
    rules: [{
      test: /\.js$/,
      exclude: './node_modules',
      use: ['babel-loader', path.join(__dirname, 'customer-loader')]
    },]
  },
}

工具箱-context

在loader的context上有什么东西呢?让我们来探索一下吧
loader api文档

  • this.resourcePath 当前处理文件的路径
  • this.rootContext 当先运行的context根目录
  • this.emitFile(name: string, content: Buffer|string, sourceMap: {...}) 向dist提交一个文件
  • this.getOptions() 获取webpack设置的配置
  • this.getLogger() 获取logger对象,可以提交warning啊之类的
  • schema-utils 用于判断当前loader传入的option是否符合规定

plugins

webpack的plugins可以做很多事情,它遍布在webpack的各个生命周期,只要有事件广播出来,他都可以在对应的事件回调中,对资源进行处理;

基础定义

参数是compiler对象,代表了webpack从启动到结束的整个生命周期;基本结构就是一个class,使用的时候需要new 创建一个实例;

module.exports = class BundleSize {
  constructor(options) {
    this.options = options;
  }
  apply(compiler) {
    console.log('BundleSize', compiler);
  }
}

开发一个plugins

const fs = require('fs');
const path = require('path');
module.exports = class BundleSize {
  constructor(options) {
    this.options = options || {
      sizeLimit: 3,
    };
  }
  apply(compiler) {
    console.log('BundleSize', compiler);
    compiler.hooks.done.tap('BundleSizePlugin', (stats) => {
      console.log(stats);
      const { path: outpath } = stats.compilation.options.output;
      const bundlePathArr = [];
      stats.compilation.chunks.forEach(item => {
        console.log(item);
        item.files.forEach(filename => {
          bundlePathArr.push(filename);
        });
      });
      // 打包生成的资源/普通资源做区分
      // 这里用assets会把
      // Object.keys(stats.compilation.assets)
      bundlePathArr.forEach(filename => {
        const bundlePath = path.join(outpath, filename);
        const { size } = fs.statSync(bundlePath);
        console.log(size);
        if (size > this.options.sizeLimit) {
          stats.compilation.logger.warn(`file: ${bundlePath} max ${this.options.sizeLimit}, please optimize this\n`)
        }
      })
    })
  }
}

plugins上常用hooks和对象

待补充

如何划分二者的边界

loader只做代码解析转换 plugin非常强大,可以做执行和生成代码以外的任何事情;同时,plugin不应该做转换代码这种工作