likes
comments
collection
share

webpack插件笔记

作者站长头像
站长
· 阅读数 53

插件向第三方开发者提供了 webpack 引擎中完整的能力。

近期在做一些需要独立使用webpack构建jssdk的项目,经常每新开一个项目,都要新写一份webpack配置,于是,是否有一种方式,可以集成这些"默认"基础配置

改造前,编写繁琐,而且配置不全,不够enterprise

webpack插件笔记

改造后,简洁许多,并且可以复用,定制

webpack插件笔记

而这一切,都是通过webpack的插件能力实现的

webpack 中的插件机制

  • 创建 - webpack 在其内部对象上创建各种钩子;
  • 注册 - 插件将自己的方法注册到对应钩子上,交给 webpack;
  • 调用 - webpack 编译过程中,会适时地触发相应钩子,因此也就触发了插件的方法。

创建插件

  • 插件是一个类
  • 类上有一个apply的实例方法
  • apply的参数是compiler
// 最基础插件模版 
class DemoPlugin { 
  constructor(options) { 
    this.options = options; 
   } 
  apply(compiler)  { } 
} 
module.exports = DemoPlugin;

注册插件

  compilation.hooks.chunkAsset.tap('WebpackAssetsPlugin', (chunk, filename) => { 
    console.log(chunk.name || chunk.id, filename); 
  });

对应源码 github.com/webpack/web… 实际上,是调用 new SyncHook(["chunk", "filename"]).tap("WebpackAssetsPlugin") 进行注册

调用插件

if (Array.isArray(plugins)) {
   	for (const plugin of plugins) {
           if (plugin) {
   	plugin.apply(childCompiler);
   }
   }
}

对应源码 github.com/webpack/web… 所以插件的调用顺序是,先进先调用

开始实战💪🏻

案例1 less-config-webpack-plugin

常规webpack项目中,对于less和css的处理都需要解析less文件、样式兼容、生成模式抽取css文件等配置,我们可以将对css/less的配置抽取到这个插件里面

less-config-webpack-plugin/LessConfigWebpackPlugin.js

const defaultOptions = {};

class LessConfigWebpackPlugin {
  /**
   *  options
   */
  constructor(options = {}) {
    this.options = Object.assign({}, defaultOptions, options);
  }

  /**
   * @param {WebpackCompiler} compiler
   */
  apply(compiler) {
	  // 判断是否是生产模式
    const isProductionLikeMode =
      this.options.mode !== undefined
        ? this.options.mode === "production"
        : compiler.options.mode === "production" || !compiler.options.mode;
		// 读取对应模式的对应配置,并且将options透传进去
    const config = isProductionLikeMode
      ? require("../config/production.config")(
        Object.assign({ mode: "production" }, this.options)
      )
      : require("../config/development.config")(
        Object.assign({ mode: "development" }, this.options)
      );
    // 合并配置
    compiler.options.plugins.push(...config.plugins);
    // 注册钩子
    compiler.hooks.afterEnvironment.tap("LessConfigWebpackPlugin", () => {
	    // 修改配置
      compiler.options.module.rules.push(...config.module.rules);

      compiler.options.optimization = {
        ...compiler.options.optimization,
        minimizer: [
          ...compiler.options.optimization.minimizer,
          ...config.optimization.minimizer,
        ],
      };
    });
  }
}

exports = module.exports = LessConfigWebpackPlugin;

接下来编写所需要的配置

less-config-webpack-plugin/config/development.config.js


// 开发配置
let postcssPresetEnv = require('postcss-preset-env');
exports = module.exports = (options={}) => {
  const baseCssConfig  = [
    // compiles Less to CSS
    "style-loader",
    {
      loader: "css-loader",
      options: {
        url: {
          filter: (url) => !(url || "").startsWith("/"),
        },
      },
    },
    {
      loader: require.resolve("postcss-loader"),
      options: {
        postcssOptions: (loader) => {
          return {
            plugins: [
              options.tailwindcss === true ? 'tailwindcss':null,
              postcssPresetEnv({
                browsers: 'last 5 version'
              }),

              require("postcss-flexbugs-fixes"),
              require("autoprefixer")({
                // flexbox: "no-2009" will add prefixes only for final and IE versions of specification.
                // @see https://github.com/postcss/autoprefixer#disabling
                flexbox: "no-2009",
              }),
            ].filter(Boolean),
          };
        },
      },
    },
    {
      loader: require.resolve("resolve-url-loader"),
    },
  ]
  return {
    module: {
      rules: [
        {
          test: /\.(less)$/,
          use: [
            ...baseCssConfig,
            "less-loader",
          ],
        },
        {
          test: /\.(css)$/,
          use:baseCssConfig ,
        },
      ],
    },
    optimization: {
      minimizer: [],
    },
    plugins: [],
  }
}

less-config-webpack-plugin/config/production.config.js

// 生产配置
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const CssMinimizerPlugin = require("css-minimizer-webpack-plugin");
let postcssPresetEnv = require('postcss-preset-env');

exports = module.exports = (options={}) => {
  const baseCssConfig = [
    MiniCssExtractPlugin.loader,
    {
      loader: "css-loader",
      options: {
        url: {
          filter: (url) => !(url || "").startsWith("/"),
        },
      },
    },
    {
      loader: require.resolve("postcss-loader"),
      options: {
        postcssOptions: (loader) => {
          return {
            plugins: [
              options.tailwindcss === true ? 'tailwindcss':null,
              postcssPresetEnv({
                browsers: 'last 5 version'
              }),
              require("postcss-flexbugs-fixes"),
              require("autoprefixer")({
                flexbox: "no-2009",
              }),
            ].filter(Boolean),
          };
        },
      },
    },
    {
      loader: require.resolve("resolve-url-loader"),
    },
    
  ]
 return  {
    module: {
      rules: [
        {
          test: /\.(less)$/,
  
          use: [
            ...baseCssConfig,
            {
              loader: "less-loader",
              options: {
                lessOptions: {
                  javascriptEnabled: true,
                },
              },
            },
          ],
        },
        {
          test: /\.(css)$/,
          use: baseCssConfig,
        },
      ],
    },
    optimization: {
      minimizer: [new CssMinimizerPlugin()],
    },
    plugins: [new MiniCssExtractPlugin({
      filename: 'static/css/[name].[contenthash:8].css',
      chunkFilename: 'static/css/[name].[contenthash:8].chunk.css',
    })],
  }
}

使用方式

  plugins: [
    new LessConfigWebpackPlugin({
	    mode:"development"
     })
  ],

案例2: asset-config-webpack-plugin

同理,webpack中需要处理各种静态资源,如图片,字体等

asset-config-webpack-plugin/src/AssetConfigWebpackPlugin.js

class AssetConfigWebpackPlugin {
  /**
   * @param {Partial<AssetConfigWebpackPluginOptions>} options
   */
  constructor(options = {}) {
    this.options = Object.assign({}, options);
  }

  /**
   * @param {WebpackCompiler} compiler
   */
  apply(compiler) {
	  // 暂时不区分开发/生产
    const config = require("../config/shared.config.js")(this.options);
    // 合并配置
    config.plugins.forEach((plugin) => plugin.apply(compiler));
    compiler.hooks.afterEnvironment.tap("AssetConfigWebpackPlugin", () => {
      compiler.options.module.rules.push(...config.module.rules);
    });
  }
}

exports = module.exports = AssetConfigWebpackPlugin;

asset-config-webpack-plugin/config/shared.config.js


exports = module.exports = (options) => ({
  module: {
    rules: [
      {
        test: /\.(gif|jpg|jpeg|png|svg|bmp)$/,
        type: 'asset/resource'

      },
      {
        test: /\.ico$/,
        type: 'asset/inline'
      }, {
        test: /\.txt$/,
        type: 'asset/source'
      },
      {
        test: /\.(woff2?)(\?v=\d+\.\d+\.\d+)?$/,
        type: 'asset/resource'

      },

    ],
  },
  plugins: [

  ],
});

我按照这种套路,编写了 js-config-webpack-plugin ts-config-webpack-plugin 等插件,同时用了 common-config-webpack-plugin 将这些基础能力插件汇总(如果不需要全部能力,只需单独引用需要的插件)

最终效果

const  HtmlWebpackPlugin  = require( "html-webpack-plugin")
const  CommonConfigWebpackPlugin  = require( 'base-config-webpack-plugin')

module.exports =  () => {
  return {
    mode: "development",
    entry: "./src/index.ts",
    output: {
      path: __dirname + "/dist",
      filename: "index_bundle.js",
    },
    plugins: [ 
      new CommonConfigWebpackPlugin({
        mode:process.env.NODE_ENV
    }),
    new HtmlWebpackPlugin({
      template:'./public/index.html',
      inject:true
    })
  ],
  };
};

这样,就完成一个接近于开箱即用(我写的是试用于react+ts),符合自身(企业)要求的webpack配置合集插件,无需再重复配置基础能力,并且可深度定制

全部源码

全部源码 npm

总结

插件向第三方开发者提供了 webpack 引擎中完整的能力。使用阶段式的构建回调,开发者可以在 webpack 构建流程中引入自定义的行为。创建插件比创建 loader 更加高级,因为你需要理解 webpack 底层的特性来处理相应的钩子,所以请做好阅读源码的准备!

转载自:https://juejin.cn/post/7369559976550875146
评论
请登录