webpack5打包原理及应用(插件篇)
概念:
- loader:通常用来完成非JavaScript模块的转化成webpack能处理的模块;
- plugin:能够完成更多定制化操作的插件
🚀 如果想要插件起作用,需要在webpack.config.js
中的 plugins 数组中进行实例的传递
// webpack.config.js基础配置
const path = require('path')
module.exports = {
entry: './src/main.js',
output: {
filename: 'build.js',
path: path.resolve(__dirname, 'dist'),
assetModuleFilename: 'img/[name]-[hash:6][ext]'
},
stats: 'errors-only', // 只显示错误信息
// 此处并未列出完整的module, 请参考以前的文章
module: {}
}
1. clean-webpack-plugin 的使用
🎪 插件说明:该插件可以将原本产出的dist目录进行自动删除
// 在webpack.config.js进行如下配置
// -- 以解构的方式进行导入
const { CleanWebpackPlugin } = require('clean-webpack-plugin')
// -- 在暴露的对象中加入插件
plugins: [
new CleanWebpackPlugin()
]
// -- 效果 => 每次执行build自动删除上次的dist目录
备注:首先执行 npm install clean-webpack-plugin -D
安装成开发依赖
2. html-webpack-plugin 的使用
🎪 插件说明:该插件主要用于入口 index.html 动态生成,默认情况我们不需要提供index.html,会自动生成到产出目录
// 在webpack.config.js进行如下配置
// -- 导入HtmlWebpackPlugin, 不需要解构的原因是考虑插件的自身导出规则
const HtmlWebpackPlugin = require('clean-webpack-plugin')
// -- 插件列表如下:
plugins: [
new CleanWebpackPlugin(),
new HtmlWebpackPlugin()
]
🎪 自动生成的 index.html 如下
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>Webpack App</title>
<meta name="viewport" content="width=device-width,initial-scale=1">
<script defer="defer" src="build.js"></script>
</head>
<body></body>
</html>
🎪 可以对index.html进行参数的设置(原因是由插件的默认ejs来控制)
// -- 对应插件实例化时,进行参数的传递
plugins: [
new CleanWebpackPlugin(),
new HtmlWebpackPlugin({
title: 'html-webpack-plugin-learn'
})
]
// -- 效果 => 在生成index.html中,对应的head中title替换成了 html-webpack-plugin-learn
🎪 可以对index.html进行自定义模板提供
<!-- 提供如下index.html模板到public模块中 -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>
<%= htmlWebpackPlugin.options.title %>
</title>
</head>
<body>
<div id="app">
测试 html-webpack-plugin
</div>
</body>
</html>
<!-- -- -- -- -- -- -- -- -- -- --
// 需要提供对应的 template 路径
plugins: [
new CleanWebpackPlugin(),
new HtmlWebpackPlugin({
title: 'html-webpack-plugin-learn',
template: './public/index.html'
})
]
-- -- -- -- -- -- -- -- -- -- -->
<!-- 经过测试打包后的 index.html 如下 -->
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>html-webpack-plugin-learn</title>
<script defer="defer" src="build.js"></script>
</head>
<body>
<div id="app">测试 html-webpack-plugin</div>
</body>
</html>
🎪 如果使用如下的vue的默认index.html模板
<!-- 默认的vue模板index.html如下 -->
<!DOCTYPE html>
<html lang="">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
<title><%= htmlWebpackPlugin.options.title %></title>
</head>
<body>
<noscript>
<strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
</noscript>
<div id="app"></div>
<!-- built files will be auto injected -->
</body>
</html>
备注:首先执行 npm i html-webpack-plugin -D
安装成开发依赖
3. define-plugin 的使用
🎄 提供了上述的index.html模板页面进行处理时,会产生ERROR in Template execution failed: ReferenceError: BASE_URL is not defined
错误
🍺 解决方案是配置 webpack 的 BASE_URL
// 使用webpack内置的插件 DefinePlugin
const { DefinePlugin } = require('webpack')
// 对应插件列表如下:
plugins: [
new CleanWebpackPlugin(),
new HtmlWebpackPlugin({
title: 'html-webpack-plugin-learn',
template: './public/index.html'
}),
new DefinePlugin({
BASE_URL: '"./"' // 此处会将我们设置的值原封不动的进行去除,所以需要加上引号
})
]
// -- 效果 => 打包成功,注意需要自己提供一个favicon.ico文件
4. babel 的使用
🎪 为什么需要babel?JSX、TS、ES6+语法 需要转化成浏览器平台可以使用的js
// 提供main.js如下:
const title = '前端'
const foo = () => {
console.log(title)
}
foo()
// 在webpack.config.js中设置mode为development npm run build后可以观察到eval函数中并没有对es6+语法进行处理
4.1 进行babel的安装
PS > npm i @babel/core -D
# 如果要在命令进行操作,则需要安装如下包
PS > npm i @babel/cli -D
# ---------------------- 使用npx babel .\src\main.js ----------------------
PS > npx babel .\src\main.js
const title = '前端';
const foo = () => {
console.log(title);
};
foo();
# ------- 上述操作可以发现并未作任何处理 这是因为 babel/core 只是一个微内核 -------
# 如果需要处理箭头函数和块级作用域则需要导入别的包
PS > npm i @babel/plugin-transform-arrow-functions @babel/plugin-transform-block-scoping -D
# ---------------- 此时再次使用babel进行处理 ----------------
PS > npx babel .\src\main.js --plugins=@babel/plugin-transform-arrow-functions,@babel/plugin-transform-block-scoping
var title = '前端';
var foo = function () {
console.log(title);
};
foo();
# ---------------- 可以发现此时已经进行了转化 ----------------
# 可以通过安装babel提供的预设方案(包括了常见的转化依赖)
PS > npm i @babel/preset-env -D
# ---------------- 在命令行中再次执行处理操作 ----------------
PS > npx babel .\src\main.js --presets=@babel/preset-env
"use strict";
var title = '前端';
var foo = function foo() {
console.log(title);
};
foo();
# ----------------- 此时的输出结果是预期内的 -----------------
4.2 babel-loader 的使用
// 在 webpack.config.js 中的module的rules新增一条规则
{
test: /\.js$/,
// use: ['babel-loader'] // 此种方式无法进行插件处理
use: {
loader: 'babel-loader',
options: {
plugins: [
'@babel/plugin-transform-arrow-functions',
'@babel/plugin-transform-block-scoping'
]
}
}
}
// -- 效果 => 此时的dist目录中 build.js 则是经过处理后的js
// 下面采用预设的方式进行处理
{
test: /\.js$/,
// use: ['babel-loader'] // 此种方式无法进行插件处理
use: {
loader: 'babel-loader',
options: {
presets: ['@babel/preset-env']
}
}
}
// -- 效果 => 此时的dist目录中 build.js 与前面效果相同
🎄 注意:babel需要 browerslist 的配合,会根据需要兼容的浏览器来确定babel转化的结果!
在.browserslistrc
中内容更改成 chrome 91
,babel处理后的结果不会进行转化,因为该浏览器支持。
注意: babel-loader 会默认读取.browserslistrc文件~
// 如果进行了如下的配置,为preset指定默认的targets,则会以该targets优先
{
test: /\.js$/,
// use: ['babel-loader'] // 此种情况 该方式暂无法进行插件处理
use: {
loader: 'babel-loader',
options: {
presets: [
[
'@babel/preset-env', {
targets: 'chrome 91'
}
]
]
}
}
}
🎪 babel-loader 相关的配置文件(可以有如下几种表现形式)
- babel.config.js(json cjs mjs)
- babelrc.json(js)
- .babelrc
// webpack.config.js rules如下:
{
test: /\.js$/,
use: ['babel-loader'] // 由于单独配置babel-loader的配置文件
}
// babel.config.js 如下:
module.exports = {
presets: ['@babel/preset-env']
}
备注:首先执行 npm i babel-loader -D
安装成开发依赖
4.3 polyfill 的使用
ployfill
可以理解为打补丁(官方解释是:用来为旧浏览器提供它没有原生支持的较新的功能。)
比如promise浏览器无法实现,需要以其他方式进行替代实现
备注:首先执行 npm i core-js regenerator-runtime
安装成生产依赖
// 在babel.config.js中配置
module.exports = {
presets: [
[
'@babel/preset-env',
{
// false: 不对当前js作polyfill填充
// usage: 会根据需要进行polyfill,记得要指定corejs为3版本(因为安装的3版本,会出现报错)
// entry: 首先需要在main.js导入,会将所有的都进行polyfill了
useBuiltIns: 'entry',
corejs: 3
}
]
]
}
5. copy-webpack-plugin 的使用
🎪 该插件可以用来完成一些文件的复制操作
// webpack.config.js 相应配置如下:
const CopyWebpackPlugin = require('copy-webpack-plugin')
plugins: [
new CleanWebpackPlugin(),
new HtmlWebpackPlugin({
title: 'html-webpack-plugin-learn',
template: './public/index.html'
}),
new DefinePlugin({
BASE_URL: '"./"' // 此处会将我们设置的值原封不动的进行去除,所以需要加上引号
}),
new CopyWebpackPlugin({
patterns: [
{
from: 'public' // 从public复制到dist目录
}
]
})
]
🎄 注意事项:使用如上方式进行配置会出现ERROR in Conflict: Multiple assets emit different content to the same filename index.html
错误,原因是前面html-webpack-plugin已经对 index.html 进行过了复制操作
🍺 解决方案:使用globOptions中的ignore进行文件忽略, 具体信息如下:
new CopyWebpackPlugin({
patterns: [
{
from: 'public', // 从public复制到dist目录
globOptions: {
ignore: ['**/index.html'] // 需要加上 **
}
}
]
})
备注:首先执行 npm i copy-webpack-plugin -D
安装成开发依赖