webpack插件笔记
插件向第三方开发者提供了 webpack 引擎中完整的能力。
近期在做一些需要独立使用webpack构建jssdk的项目,经常每新开一个项目,都要新写一份webpack配置,于是,是否有一种方式,可以集成这些"默认"基础配置
改造前,编写繁琐,而且配置不全,不够enterprise
改造后,简洁许多,并且可以复用,定制
而这一切,都是通过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配置合集插件,无需再重复配置基础能力,并且可深度定制
全部源码
总结
插件向第三方开发者提供了 webpack 引擎中完整的能力。使用阶段式的构建回调,开发者可以在 webpack 构建流程中引入自定义的行为。创建插件比创建 loader 更加高级,因为你需要理解 webpack 底层的特性来处理相应的钩子,所以请做好阅读源码的准备!
转载自:https://juejin.cn/post/7369559976550875146