webpack复习-0
2021-05-26 14:54:222024-04-18 20:04:40
构建过程
- 初始化参数。从配置文件和命令行读取参数后合并,得到最终的参数
- 开始编译。用上一步得到的参数初始化
Compiler
对象,加载所有配置的插件,执行Compiler
对象的run
方法开始执行编译 - 确定入口。根据配置中的
entry
找出所有的入口文件 - 编译模块。从入口文件出发,调用所有配置的
loader
对模块进行翻译,再找出该模块依赖的模块,再递归本步骤直到所有入口依赖的文件都经过了本步骤的处理 - 完成模块编译。在经过第 4 步使用
loader
翻译完所有模块后,得到了每个模块被翻译后的最终内容以及它们之间的依赖关系 - 输出资源。根据入口和模块之间的依赖关系,组装成一个个包含多个模块的
chunk
,再把每个chunk
转换成一个单独的文件加入到输出列表,这步是可以修改输出内容的最后机会 - 输出完成。在确定好输出内容后,根据配置确定输出的路径和文件名,把文件内容写入到文件内
Hash&ChunkHash&ContentHash 的区别
hash
,根据构建目录生成,只要项目中有文件修改,则整个项目构建出来的hash
都会改变chunkHash
,和 webpack 打包到 chunk 有关,不同 entry 会生成不同的chunkHash
。chunk 之间互不影响(一般用来打包公用 js)contentHash
,根据文件内容生成hash
,只要文件内容不变,生成的contentHash
就不变(一般用来打包 css)
loader 和 plugin
loader 用来处理非 JavaScript 文件。webpack 将一切文件视为模块,但 webpack 只能处理 JavaScript 文件,如果想将其他文件也打包的话就要用到对应的 loader
plugin 用来扩展 webpack 的能力。webpack 在运行的生命周期中会广播出许多事件,plugin 可以监听这些事件,在合适的时机通过 webpack 提供的 api 改变输出结果
webpack5 新特性
- 启动命令变化
{ // v4 "start": "webpack-dev-server" // v5 "start": "webpack serve" }
- 内置持久化缓存
- 资源模块
- moduleIds 和 chunkIds 优化
- 移除 Node.js 的 polyfill
tree-shaking
module federation
。主要是用来解决多个应用之间代码共享的问题,可以让我们的更加优雅的实现跨应用的代码共享
tree shaking
一种优化,可以去除不必要的代码,减小打包后的体积
基于模块静态加载来实现。由于导入了哪些模块是确定的,编译的时候可以正确判断到底加载了哪些模块和变量,可以删除那些未被使用的变量或者引用
常用配置
const path = require("path");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const ProgressBarPlugin = require("progress-bar-webpack-plugin");
const chalk = require("chalk");
const { CleanWebpackPlugin } = require("clean-webpack-plugin");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const CssMinimizerPlugin = require("css-minimizer-webpack-plugin");
const { DefinePlugin } = require("webpack");
const TerserPlugin = require("terser-webpack-plugin");
const entry = require("./webpack.entry");
const { PORT, MODE, MOCK } = process.env;
function generateHTMLPluginConfig(pages) {
const items = Reflect.ownKeys(pages).map(
(item) =>
new HtmlWebpackPlugin({
chunks: [item, "common"],
template: `./src/html/${item}.html`,
filename: `${item}.html`,
})
);
return items;
}
module.exports = {
mode: MODE,
entry,
output: {
filename: "static/js/[name].[hash:8].js",
path: path.resolve(__dirname, "dist"),
},
plugins: [
new CleanWebpackPlugin(),
...generateHTMLPluginConfig(entry),
new ProgressBarPlugin({
format: `${chalk.green("Progressing")}[:bar]${chalk.green(
":percent"
)}(:elapsed seconds)`,
clear: false,
}),
new MiniCssExtractPlugin({
filename: "static/css/[name].[hash:8].css",
chunkFilename: "static/css/[id].css",
}),
new DefinePlugin({
"process.env": {
MODE: JSON.stringify(MODE),
MOCK,
},
}),
],
module: {
rules: [
{
test: /\.(html)$/,
use: ["html-withimg-loader"],
exclude: /node_modules/,
},
{
test: /\.m?js$/,
use: [
{
loader: "babel-loader",
options: {
presets: ["@babel/preset-env"],
},
},
"eslint-loader",
],
exclude: [/node_modules/, /lib/, /polyfill/],
},
{
test: /\.css$/,
use: [
{
loader: MiniCssExtractPlugin.loader,
options: {
publicPath: "../",
},
},
"css-loader",
"postcss-loader",
],
},
{
test: /\.less$/,
use: [
{
loader: MiniCssExtractPlugin.loader,
options: {
publicPath: "../",
},
},
"css-loader",
"postcss-loader",
"less-loader",
],
},
{
test: /\.(png|jpg|gif|jpeg|svg|ttf)$/,
use: [
{
loader: "url-loader",
options: {
limit: 10240,
esModule: false,
name: "static/img/[name].[hash:6].[ext]",
publicPath: MODE === "development" ? "/" : "/loginfe",
},
},
],
exclude: /node_modules/,
},
],
},
optimization: {
minimize: true,
minimizer: [new CssMinimizerPlugin(), new TerserPlugin()],
splitChunks: {
chunks: "all",
cacheGroups: {
common: {
name: "common",
test: /.js$/,
minChunks: 2,
minSize: 0,
},
},
},
},
devServer: {
contentBase: path.join(__dirname, "dist"),
compress: true,
port: PORT,
disableHostCheck: true,
host: "0.0.0.0",
proxy: {
"/v1/*": {
target: "xxx",
changeOrigin: true,
secure: false,
headers: {},
},
},
},
devtool: MODE === "production" ? false : "eval-source-map",
};
循环引用
在CommonJS
规范中,当遇到require()
语句时,会执行require
模块中的代码,并缓存执行的结果,当下次再次加载时不会重复执行,而是直接取缓存的结果
在ESM
中,因为import
是在编译阶段执行的,这样就使得程序在编译时就能确定模块的依赖关系,一旦发现循环依赖,ES6 本身就不会再去执行依赖的那个模块了,所以程序可以正常结束。这也说明了 ES6 本身就支持循环依赖,保证程序不会因为循环依赖陷入无限调用