流程
- 合并参数:从shell等命令中合并参数
- 执行编译:初始化 Compiler 对象,加载所有配置的插件,执行对象的 run 方法开始执行编译
- 入口:根据配置的 entry 找出所有的入口
- 编译模块:根据入口,利用 loader 编译依赖的模块,找出依赖模块依赖的模块(套娃递归),完成后,得到了编译完成的模块和依赖关系
- 输出:根据入口和模块之间的依赖关系,组装成一个个包含多个模块的 Chunk,再把每个 Chunk 转换成一个单独的文件加入到输出列表,这步是可以修改输出内容的最后机会
- 输出完成:根据配置 output,将文件写入文件系统
入口 (entry)
单入口
通常的,只有一个入口文件,是 webpack
用来编译的起点,从此文件一步步找到所依赖的模块依次进行编译
示例:
module.exports = {
entry: './src/index.js'
}
多入口
当然,也可以设置多个入口
-
示例 1:
module.exports = { entry:{ demo1: './src/demo1.js', demo2: './src/demo2.js' } }
-
示例 2:
// 也可以根据传入的路径数组进行动态配置 const path = require('path'); module.exports = (env, args) => { const pages = env.pages.split(","); // 传入的数组 const srcPagesDir = path.resolve(__dirname, "src/pages/"); const entry = {}; pages.forEach( (el) => (entry[el] = path.resolve(srcPagesDir, el, "index.js")) ); return { entry } }
输出 (output)
最终的产物输出,无论单入口还是多入口,只能有一个 output
示例:
const path = require('path');
module.exports = {
output: {
// filename: 'main.js' // 输出的文件名
filename: '[name].js' // 也可以进行动态配置
path: path.resolve(__dirname, 'dist') // 输出的路径,默认为 dist
clean: true // 可以在每次编译或打包时将上次的产物清除
publicPath: '/'
}
}
chunk
loader
用来处理各种文件,指定 rules
规则,loader
的执行顺序为自下而上、从右至左
js
rules: [
{
test: /\.js|jsx$/, // 匹配的文件格式
include: path.resolve(__dirname, "../src"), // 指定处理文件夹
use: [
{
loader: 'babel-loader',
options: {
presets: ["@babel/preset-env"], // es6 => es5
}
}
]
}
]
css
const MiniCssExtractPlugin = require("mini-css-extract-plugin"); // 抽离为单独的 css 文件
// postcss: 一个用 js 处理 css 的工具,拥有丰富的 API 和插件 (https://www.postcss.com.cn/)
const postcssLoader = {
loader: "postcss-loader",
options: {
postcssOptions: {
plugins: [
[
'postcss-preset-env',
{
// 其他选项
},
],
],
},
};
rules: [
{
test: /\.css$/,
use: [
MiniCssExtractPlugin.loader,
'css-loader',
postcssLoader
]
},
{
test: /\.less$/,
use: [
MiniCssExtractPlugin.loader,
"css-loader",
postcssLoader,
"less-loader",
]
}
]
html
rules: [{
test: /\.html$ /,
exclude: /node_modules/, // 忽略处理
loader: "html-loader",
options: {
esModule: false,
},
}]
图片
rules: [{
test: /\.(jpg|png|jpeg|gif)$/,
exclude: /node_modules/,
use: [
{
loader: "url-loader",
options: {
limit: 1024 * 8,
name: "[name].[ext]",
esModule: false,
outputPath: "static/assets/images",
},
},
]
}]
其他文件
处理其他的文件,例如 vue
rules: [
{
test: /\.vue$/,
use: {
loader: "vue-loader",
},
}
]
还有引入的其他类型的文件,可以使用 file-loader 等插件进行处理
当然,如果没有满足个人需要的 loader,还可以进行自定义 loader
plugin
目的在于解决 loader 解决不了的事
示例:
const HtmlWebpackPlugin = require('html-webpack-plugin');
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const CssMinimizerPlugin = require("css-minimizer-webpack-plugin");
const path = require('path');
module.exports = (env, args) => {
const pages = env.pages.split(",");
return {
plugins: [
// 处理 html
new HtmlWebpackPlugin({
filename: 'index.html',
template: path.resolve(__dirname, "src/index.html")
});
// 如果需要做静态化,处理 html 时也可以参考下面的代码
// ...pages.map((pageName) => {
// return new HtmlWebpackPlugin({
// filename: `${pageName}/index.html`,
// chunks: [pageName],
// template: path.resolve(__dirname, "src/index.html"),
// });
// }),
// 提取单独的CSS
new MiniCssExtractPlugin({
filename: "[name]/main.[contenthash:10].css",
}),
// 压缩css
new CssMinimizerPlugin(),
]
}
}
外部扩展 (externals)
防止将某些 import
的包打包到 bundle 中,一般使用CDN引入,可以有效减小打包文件的大小
示例:
// 在 index.html 中需引入相应的 CDN 包
module.exports = {
externals: {
react: 'React',
'react-dom': 'ReactDOM',
'antd': 'antd',
'mobx': 'mobx',
'mobx-react': 'mobxReact'
}
}
调试
在 webpack 5.x 版本中,所提供的属性已经满足大部分的调试需求
// # npm i webpack-dev-server --save-dev
module.exports = {
devServer: {
hot: true // 开启热更新
},
devtool: 'eval-source-map' // 使用源码还是编译后的 (https://webpack.docschina.org/configuration/devtool)
}
HMR
本质上是客户端拉取服务端的文件,可以理解为维护了一个websocket,本地监听文件改动时,会改变文件的hash, 客户端与上次进行对比,不同时则会发起请求重新读取文件列表