【webpack】自定义plugin

相关文章

自定义plugin


一、什么是自定义plugin

自定义插件(Custom Plugin)是Webpack中的一个重要概念。插件可以用来扩展Webpack的功能,执行各种任务,如文件压缩、代码优化、资源管理等。Webpack本身提供了许多内置插件,但有时候我们需要根据特定的需求编写自己的插件。

自定义插件是一个具有 apply 方法的 JavaScript 对象。apply 方法会被Webpack compiler调用,并且在整个编译过程中访问 compiler 对象的钩子。这允许我们在编译过程中执行自定义的逻辑。

二、自定义插件的基本结构

类写法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class MyCustomPlugin{
constructor(options) {
this.options = options;
}

apply(compiler, callback) {
compiler.hooks.done.tap('MyCustomPlugin', () => {
// ...
callback()
});
}
}

module.exports = MyCustomPlugin;

函数写法

1
2
3
4
5
6
7
8
9
10
11
12
function MyCustomPlugin(options) {
this.options = options;
}

MyCustomPlugin.prototype.apply = function(compiler, callback) {
compiler.hooks.done.tap('MyCustomPlugin', () => {
// ...
callback()
});
};

module.exports = MyCustomPlugin;

在上面的示例中,MyCustomPlugin 类具有一个构造函数,用于接收插件的选项。它还具有一个 apply 方法,该方法接收一个 compiler 参数,用于访问Webpack compiler对象。在 apply 方法中,我们可以使用 compiler 对象的钩子来执行自定义的逻辑。tap 方法用于在特定的钩子上注册一个回调函数,当钩子触发时,注册的回调函数会被调用。

自定义插件的功能可以非常灵活,可以根据实际需求来进行扩展。常见的自定义插件功能包括:

  • 修改编译过程中的输出结果。
  • 打包前的文件处理,如压缩、优化等。
  • 检查和修复代码错误。
  • 分析编译过程中的各种数据,并生成统计报告。

三、修改编译过程中的输出结果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
class ModifyOutputPlugin {
constructor(options) {
// 接收插件的选项
this.options = options || {};
}

apply(compiler) {
// 在emit钩子上注册一个回调函数,该钩子在资源已经生成到 output 目录之前触发
compiler.hooks.emit.tap('ModifyOutputPlugin', (compilation) => {
// 遍历所有资源文件
Object.keys(compilation.assets).forEach((filename) => {
// 修改文件名
const newFilename = this.modifyFilename(filename);
// 获取原始文件对象
const asset = compilation.assets[filename];
// 删除原始文件
delete compilation.assets[filename];
// 添加修改后的文件
compilation.assets[newFilename] = asset;
});
});
}

// 修改文件名的逻辑,这里简单地在文件名后面添加一个后缀
modifyFilename(filename) {
return filename.replace(/(\.[^.]+)$/, '-modified$1');
}
}

module.exports = ModifyOutputPlugin;

这个插件的功能是在Webpack编译过程中修改输出文件的文件名,具体地说是在文件名后面添加一个后缀 -modified。

四、修改编译过程中的输出结果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
const UglifyJsPlugin = require('uglifyjs-webpack-plugin');

class PrePackagingPlugin {
constructor(options) {
// 接收插件的选项
this.options = options || {};
}

apply(compiler) {
// 在beforeRun钩子上注册一个回调函数,该钩子在编译之前触发
compiler.hooks.beforeRun.tapAsync('PrePackagingPlugin', (compiler, callback) => {
console.log('开始处理文件...');
// 这里可以执行对文件的处理逻辑,比如压缩、优化等
// 在这个示例中,我们使用 UglifyJsPlugin 插件对 JavaScript 文件进行压缩
const uglifyPlugin = new UglifyJsPlugin();
// 调用 UglifyJsPlugin 的 apply 方法来应用插件
uglifyPlugin.apply(compiler);
console.log('文件处理完成。');
// 必须调用 callback 函数,告诉 webpack 编译过程已经完成
callback();
});
}
}

module.exports = PrePackagingPlugin;

这个示例插件在Webpack编译之前会调用 UglifyJsPlugin 对 JavaScript 文件进行压缩。

五、检查和修复代码错误

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
const eslint = require('eslint');

class CheckAndFixErrorPlugin {
constructor(options) {
// 接收插件的选项
this.options = options || {};
}

apply(compiler) {
// 在beforeRun钩子上注册一个回调函数,该钩子在编译之前触发
compiler.hooks.beforeRun.tapAsync('CheckAndFixErrorPlugin', (compiler, callback) => {
console.log('开始检查和修复代码错误...');
// 创建一个 ESLint 实例
const linter = new eslint.ESLint({
fix: true, // 启用自动修复功能
});
// 获取需要检查和修复的文件路径
const filesToLint = this.options.files || ['./src/**/*.js'];
// 使用 ESLint 对文件进行检查和修复
linter.lintFiles(filesToLint)
.then(results => {
// 输出检查结果
results.forEach(result => {
console.log('文件:', result.filePath);
if (result.messages.length > 0) {
console.log('错误:');
result.messages.forEach(msg => {
console.log(` [${msg.line}:${msg.column}] ${msg.message} (${msg.ruleId})`);
});
} else {
console.log('无错误。');
}
});
console.log('代码检查和修复完成。');
// 必须调用 callback 函数,告诉 webpack 编译过程已经完成
callback();
})
.catch(error => {
console.error('ESLint错误:', error);
callback(error);
});
});
}
}

module.exports = CheckAndFixErrorPlugin;

这个示例插件会在Webpack编译之前使用 ESLint 对指定的 JavaScript 文件进行代码检查和修复。

六、分析编译过程中的各种数据,并生成统计报告。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
class CompilationStatsPlugin {
constructor(options) {
// 接收插件的选项
this.options = options || {};
}

apply(compiler) {
// 在emit钩子上注册一个回调函数,该钩子在资源已经生成到 output 目录之前触发
compiler.hooks.emit.tapAsync('CompilationStatsPlugin', (compilation, callback) => {
console.log('开始生成统计报告...');
// 分析各种数据,并生成统计报告
const stats = this.analyzeCompilation(compilation);
console.log('统计报告:', stats);
console.log('统计报告生成完成。');
// 必须调用 callback 函数,告诉 webpack 编译过程已经完成
callback();
});
}

// 分析编译过程中的数据
analyzeCompilation(compilation) {
const stats = {
entryChunks: this.countEntryChunks(compilation),
modules: this.countModules(compilation),
assets: this.countAssets(compilation),
errors: this.countErrors(compilation),
warnings: this.countWarnings(compilation)
};
return stats;
}

// 统计入口chunk数量
countEntryChunks(compilation) {
return compilation.entrypoints.size;
}

// 统计模块数量
countModules(compilation) {
return compilation.modules.size;
}

// 统计资源文件数量
countAssets(compilation) {
return Object.keys(compilation.assets).length;
}

// 统计错误数量
countErrors(compilation) {
return compilation.errors.length;
}

// 统计警告数量
countWarnings(compilation) {
return compilation.warnings.length;
}
}

module.exports = CompilationStatsPlugin;

这个示例插件会在Webpack编译过程中通过各种钩子收集编译过程中的数据,并生成统计报告。

七、常用插件

HtmlWebpackPlugin

用于生成 HTML 文件,并自动将打包后的资源文件(如 JS、CSS)插入到 HTML 文件中。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
const HtmlWebpackPlugin = require('html-webpack-plugin');
const path = require('path');

module.exports = {
entry: './src/index.js',
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'dist')
},
plugins: [
new HtmlWebpackPlugin({
template: './src/index.html', // 指定HTML模板文件路径
filename: 'index.html' // 指定生成的HTML文件名
})
]
};

这样就完成了使用HtmlWebpackPlugin插件生成HTML文件的配置。WebpackPlugin能够自动处理打包后的资源文件的引入,使得在开发过程中不需要手动修改HTML文件。

MiniCssExtractPlugin

用于将 CSS 文件提取为单独的文件,而不是以 <style> 标签的形式插入到 HTML 文件中。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const path = require('path');

module.exports = {
entry: './src/index.js',
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'dist')
},
module: {
rules: [
{
test: /\.css$/,
use: [
MiniCssExtractPlugin.loader, // 使用MiniCssExtractPlugin.loader来提取CSS文件
'css-loader' // 使用css-loader来解析CSS文件
]
}
]
},
plugins: [
new MiniCssExtractPlugin({
filename: 'styles.css' // 指定提取的CSS文件名
})
]
};

这样就完成了使用MiniCssExtractPlugin插件提取CSS文件的配置。通过MiniCssExtractPlugin,我们可以将CSS文件提取为单独的文件,并在HTML中引入,以减少页面加载时间,提高页面性能。

CleanWebpackPlugin

用于在每次构建前清理输出目录。

1
2
3
4
5
6
7
8
9
10
11
12
13
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
const path = require('path');

module.exports = {
entry: './src/index.js',
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'dist')
},
plugins: [
new CleanWebpackPlugin()
]
};

运行 Webpack 之后,CleanWebpackPlugin 将会在每次构建前清理输出目录(dist 目录),确保该目录是干净的。这样可以防止旧的文件残留在其中,保证每次构建产出的都是最新的文件。

OptimizeCSSAssetsWebpackPlugin

用于压缩优化 CSS 文件。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin');
const TerserPlugin = require('terser-webpack-plugin');
const path = require('path');

module.exports = {
entry: './src/index.js',
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'dist')
},
optimization: {
minimizer: [
new TerserPlugin(), // 使用 TerserPlugin 压缩 JavaScript 文件
new OptimizeCSSAssetsPlugin() // 使用 OptimizeCSSAssetsPlugin 压缩优化 CSS 文件
]
}
};

运行 Webpack 之后,OptimizeCSSAssetsPlugin 将会在打包过程中对 CSS 文件进行压缩优化,以减小文件体积、加快加载速度。这样可以提升网页的加载速度和性能。

UglifyJsPlugin

用于压缩优化 JavaScript 文件。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
const UglifyJsPlugin = require('uglifyjs-webpack-plugin');
const path = require('path');

module.exports = {
entry: './src/index.js',
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'dist')
},
optimization: {
minimizer: [
new UglifyJsPlugin() // 使用 UglifyJsPlugin 压缩 JavaScript 文件
]
}
};

运行 Webpack 之后,UglifyJsPlugin 将会在打包过程中对 JavaScript 文件进行压缩、混淆等操作,以减小文件体积、加快加载速度。这样可以提升网页的加载速度和性能。

CopyWebpackPlugin

用于复制文件或目录到构建目录。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
const CopyWebpackPlugin = require('copy-webpack-plugin');
const path = require('path');

module.exports = {
entry: './src/index.js',
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'dist')
},
plugins: [
new CopyWebpackPlugin({
patterns: [
{ from: 'public', to: 'public' } // 将 public 目录下的所有文件复制到输出目录的 public 目录下
]
})
]
};

运行 Webpack 之后,CopyWebpackPlugin 将会复制 public 目录下的所有文件到输出目录的 public 目录下。这样在构建过程中就会将这些文件复制到输出目录中,使得它们能够被访问到。

DefinePlugin

用于定义全局常量,可以在编译过程中对代码中的变量进行替换。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
const webpack = require('webpack');
const path = require('path');

module.exports = {
entry: './src/index.js',
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'dist')
},
plugins: [
new webpack.DefinePlugin({
'process.env.NODE_ENV': JSON.stringify('production') // 将 process.env.NODE_ENV 替换为 'production'
})
]
};

运行 Webpack 之后,DefinePlugin 将会在编译过程中将 process.env.NODE_ENV 替换为 ‘production’,因此最终打包出来的 bundle.js 文件中的代码将根据这个值输出不同的提示信息。

ProvidePlugin

用于自动加载模块,而不必每次都使用 import 或 require。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
const webpack = require('webpack');
const path = require('path');

module.exports = {
entry: './src/index.js',
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'dist')
},
plugins: [
new webpack.ProvidePlugin({
_: 'lodash' // 在每个模块中自动加载 lodash 库中的 `_` 方法
})
]
};

在这个示例中,我们使用 ProvidePlugin 将 lodash 库中的 _ 方法注册为全局变量 _,这样在每个模块中都可以直接使用 _ 而不必手动引入 lodash。

1
2
// src/index.js
console.log(_.chunk(['a', 'b', 'c', 'd'], 2));

运行 Webpack 之后,ProvidePlugin 将会在每个模块中自动加载 lodash 库中的 _ 方法,因此可以直接在代码中使用 _ 而不必手动引入 lodash。

SplitChunksPlugin

用于将公共的依赖模块提取到单独的文件中,以避免重复打包。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
const path = require('path');

module.exports = {
entry: {
page1: './src/page1.js',
page2: './src/page2.js'
},
output: {
filename: '[name].bundle.js',
path: path.resolve(__dirname, 'dist')
},
optimization: {
splitChunks: {
chunks: 'all'
}
}
};
1
2
3
4
// src/page1.js
import _ from 'lodash';

console.log(_.join(['page1', 'module', 'loaded'], ' '));
1
2
3
4
// src/page2.js
import _ from 'lodash';

console.log(_.join(['page2', 'module', 'loaded'], ' '));

运行 Webpack 之后,SplitChunksPlugin 将会将 lodash 库提取到单独的文件中,并命名为 vendorspage1page2.bundle.js,然后将这个文件加入到两个入口文件的依赖中。这样可以避免重复打包 lodash 库,提高了打包效率和减小了文件体积。

BundleAnalyzerPlugin

用于分析打包后的文件大小,并生成可视化报告,帮助优化打包效果。

1
2
3
4
5
6
7
8
9
10
11
12
13
const { BundleAnalyzerPlugin } = require('webpack-bundle-analyzer');
const path = require('path');

module.exports = {
entry: './src/index.js',
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'dist')
},
plugins: [
new BundleAnalyzerPlugin()
]
};

运行 Webpack 之后,BundleAnalyzerPlugin 将会生成一个 HTML 报告文件,可以在浏览器中打开该文件查看打包后文件大小的可视化报告。通常情况下,报告文件的路径为 ./dist/report.html,可以在浏览器中输入该文件路径打开报告。

CompressionWebpackPlugin

用于生成 gzip 压缩文件,以减小文件体积,加快网络传输速度。

1
2
3
4
5
6
7
8
9
10
11
12
13
const CompressionWebpackPlugin = require('compression-webpack-plugin');
const path = require('path');

module.exports = {
entry: './src/index.js',
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'dist')
},
plugins: [
new CompressionWebpackPlugin() // 使用默认配置生成 gzip 压缩文件
]
};

运行 Webpack 之后,CompressionWebpackPlugin 将会在输出目录(dist 目录)中生成带有 .gz 后缀的 gzip 压缩文件,这些压缩文件将会对应于输出目录中的每个文件,并且文件名后会附加 .gz 后缀。这样在服务器端配置相应的响应头后,浏览器会自动解压缩这些压缩文件,加快文件传输速度,提高网站的加载速度和性能。

ESLintPlugin

用于集成 ESLint,进行代码规范检查。

1
2
3
4
5
6
7
8
9
10
11
12
13
const ESLintPlugin = require('eslint-webpack-plugin');
const path = require('path');

module.exports = {
entry: './src/index.js',
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'dist')
},
plugins: [
new ESLintPlugin() // 使用默认配置进行 ESLint 检查
]
};

运行 Webpack 之后,ESLintPlugin 将会在打包过程中对代码进行 ESLint 检查,检查结果会输出到控制台。如果代码中存在不符合规范的地方,将会显示相应的警告或错误信息。

StylelintPlugin

用于集成 Stylelint,进行 CSS 或 SCSS 代码规范检查。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
const StylelintPlugin = require('stylelint-webpack-plugin');
const path = require('path');

module.exports = {
entry: './src/index.js',
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'dist')
},
module: {
rules: [
{
test: /\.css$/,
use: ['style-loader', 'css-loader']
}
]
},
plugins: [
new StylelintPlugin() // 使用默认配置进行 Stylelint 检查
]
};

运行 Webpack 之后,StylelintPlugin 将会在打包过程中对样式文件进行 Stylelint 检查,检查结果会输出到控制台。如果样式文件中存在不符合规范的地方,将会显示相应的警告或错误信息。

webpack-bundle-analyzer

用于分析打包后的文件体积,并以可视化方式展示。

1
2
3
4
5
6
7
8
9
10
11
12
13
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
const path = require('path');

module.exports = {
entry: './src/index.js',
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'dist')
},
plugins: [
new BundleAnalyzerPlugin() // 使用默认配置生成报告
]
};

运行 Webpack 之后,webpack-bundle-analyzer 将会自动启动一个本地服务器,通过浏览器打开生成的报告页面。在页面中,你可以看到打包后各个模块的大小、依赖关系,以及文件之间的引用关系。通过这个报告,你可以更好地理解打包后文件的组成结构,从而优化打包效果。

HtmlWebpackInlineSourcePlugin

用于将引用的 CSS 或 JavaScript 文件内联到 HTML 文件中,减少页面请求数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
const HtmlWebpackInlineSourcePlugin = require('html-webpack-inline-source-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const path = require('path');

module.exports = {
entry: './src/index.js',
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'dist')
},
plugins: [
new HtmlWebpackPlugin({
template: './src/index.html', // 指定 HTML 模板文件
inlineSource: '.(js)$' // 需要内联的 JavaScript 文件后缀
}),
new HtmlWebpackInlineSourcePlugin() // 使用默认配置进行内联
]
};

运行 Webpack 之后,HtmlWebpackInlineSourcePlugin 将会将指定的 JavaScript 文件内容内联到 HTML 文件中,生成的 HTML 文件中将不再有外部引用的 JavaScript 文件,而是直接包含在 HTML 文件中。

FriendlyErrorsWebpackPlugin

用于友好地展示编译过程中的错误和警告信息。

1
2
3
4
5
6
7
8
9
10
11
12
13
const FriendlyErrorsWebpackPlugin = require('friendly-errors-webpack-plugin');
const path = require('path');

module.exports = {
entry: './src/index.js',
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'dist')
},
plugins: [
new FriendlyErrorsWebpackPlugin() // 使用默认配置
]
};

运行 Webpack 之后,FriendlyErrorsWebpackPlugin 将会在控制台中以更清晰和易读的方式显示构建过程中的错误和警告信息。例如,它会将错误和警告信息高亮显示,并带有更详细的解释和定位信息,帮助开发者更快地定位和解决问题。


喜欢这篇文章?打赏一下支持一下作者吧!
【webpack】自定义plugin
https://www.cccccl.com/20221104/webpack/自定义plugin/
作者
Jeffrey
发布于
2022年11月4日
许可协议