0x01 概述
(1)简介
- 官网链接:https://webpack.js.org
- Webpack 是一个打包工具,作为前端工程化的重要工具被广泛应用
- Webpack 相比过去使用 JavaScript 脚本的方式,能够解决作用域、代码拆分、浏览器模块支持等问题
- 对于作用域问题,Webpack 采用 IIFE(立即调用函数表达式)的方式解决
- 对于代码拆分问题,Webpack 通过运行在 NodeJS 环境中,使用 CommonJS 规范下的
require
解决
- 类似的工具包括:PARCEL、rollup.js、Vite
(2)环境搭建
-
新建一个空目录作为项目目录,其中使用命令
npm init -y
快速创建 NodeJS 环境版本信息:Node v23.2.0,npm v10.9.0
-
使用命令
npm install --save-dev webpack webpack-cli
安装 Webpack 及其脚手架工具 -
创建以下文件:
-
src\say.js
function say() { console.log("hello world"); } export default say;
-
src\main.js
import say from "./say"; say();
-
index.html
<!DOCTYPE html> <html> <head></head> <body></body> </html>
-
-
在项目根目录创建 Webpack 配置文件 webpack.config.js
const path = require("path"); module.exports = { entry: "./src/main.js", output: { filename: "bundle.js", path: path.resolve(__dirname, "dist"), }, mode: "none", };
-
使用命令
npx webpack
打包,或使用命令npx webpack --stats detailed
打包并查看打包过程 -
修改 index.html
<!DOCTYPE html> <html> <head></head> <body> <script src="dist/bundle.js"></script> </body> </html>
0x02 引入插件
(1)自动引入资源
a. html-webpack-plugin
用于自动生成一个 HTML 文件,其中导入了打包好的 JavaScript 脚本文件
-
使用命令
npm install --save-dev html-webpack-plugin
安装插件 -
修改 webpack.config.js,实例化插件
const HtmlWebpackPlugin = require("html-webpack-plugin"); const path = require("path"); module.exports = { // ... plugins: [ new HtmlWebpackPlugin() ] };
-
使用命令
npx webpack
打包,此时 dist 目录中会生成 bundle.js 和 index.html -
修改 webpack.config.js,添加一些配置
module.exports = { // ... plugins: [ new HtmlWebpackPlugin({ template: "./index.html", // 采用根目录下的 index.html 作为模板 filename: "app.html", // 生成的 HTML 文件命名为 app.html inject: "body", // 将打包后的 JavaScript 插入 body 中 }), ], };
b. 清理 dist
修改 webpack.config.js,添加配置项 output.clean
module.exports = {
// ...
output: {
// ...
clean: true,
},
// ...
};
(2)开发环境搭建
-
修改 webpack.config.js,修改模式
mode
为开发模式module.exports = { // ... mode: "development", // ... };
-
修改 webpack.config.js,引入一种 source-map
module.exports = { // ... devtool: "inline-source-map", };
并修改 src/say.js,添加一处报错
function say() { console.log("hello world"); throw new Error("error"); } export default say;
-
使用命令
npx webpack --watch
打包,其中--watch
选项能够监听源码变更并实现热更新 -
使用命令
npm install --save-dev webpack-dev-server
安装用于实时刷新浏览器的插件 -
修改 webpack.config.js,实例化插件
module.exports = { // ... devServer: { static: "./dist", }, };
-
使用命令
npx webpack-dev-server --open
打包并开启开发服务器,默认将项目运行在 8080 端口,其中--open
选项能够自动开启浏览器并进入 http://localhost:8080/
最终的 webpack.config.js
const HtmlWebpackPlugin = require("html-webpack-plugin"); const path = require("path"); module.exports = { entry: "./src/main.js", output: { filename: "bundle.js", path: path.resolve(__dirname, "dist"), clean: true, }, plugins: [ new HtmlWebpackPlugin({ template: "./index.html", filename: "app.html", inject: "body", }), ], mode: "development", devtool: "inline-source-map", devServer: { static: "./dist", }, };
(3)资源模块
-
Webpack 提供了内置的资源模块(asset module)来引入任何其他类型的资源
-
资源模块类型包括:
- asset/resource:发送一个单独的文件并导出 URL
- asset/inline:导出一个资源的 Data URL
- asset/source:导出资源的源代码
- asset:在发送一个单独的文件和导出一个资源的 Data URL之间自动选择
-
修改 webpack.config.js,配置资源文件
module.exports = { // ... module: { rules: [ { test: /\.png$/, type: "asset/resource", generator: { filename: "assets/[hash][ext]", // 输出格式为`哈希+后缀`的文件名 }, }, { test: /\.svg$/, type: "asset/inline", generator: { filename: "assets/[hash][ext]", }, }, { test: /\.txt$/, type: "asset/source", generator: { filename: "assets/[hash][ext]", }, }, { test: /\.jpg$/, type: "asset", generator: { filename: "assets/[hash][ext]", }, parser: { dataUrlCondition: { maxSize: 4 * 1024, // 当图片小于 4kb 的时候,使用 base64 编码 }, }, }, ], }, };
(4)资源载入
- Webpack 支持通过加载器(loader)引入其他资源
a. CSS
I. 载入
-
使用命令
npm install --save-dev style-loader css-loader
安装相关加载器- style-loader 将 CSS 代码插入到 JavaScript 模块中
- css-loader 将 CSS 文件转换为 CommonJS 模块
-
修改 webpack.config.js,配置资源文件
module.exports = { // ... module: { rules: [ { test: /\.css$/i, use: ["style-loader", "css-loader"], // 顺序不可颠倒 }, ], }, };
-
先使用 style-loader,再使用 css-loader,最后使用 CSS 预编译工具,如 Less、Sass 等
-
以 Less 文件为例:
module.exports = { // ... module: { rules: [ { test: /\.(css|less)$/i, use: ["style-loader", "css-loader", "less-loader"], }, ], }, };
-
-
在 src 目录下创建样式文件 style.css
body { margin: 0; padding: 0; box-sizing: border-box; min-height: 100vh; display: flex; flex-direction: column; justify-content: center; align-items: center; background-color: skyblue; color: red; }
-
修改 main.js,导入 CSS 文件
import "./style.css"; // ...
II. 抽离
-
使用命令
npm install --save-dev mini-css-extract-plugin
安装用于将 CSS 抽离成单独文件的插件该插件需要在 Webpack5 中使用
-
修改 webpack.config.js,实例化插件
// ... const MiniCssExtractPlugin = require("mini-css-extract-plugin"); module.exports = { // ... plugins: [ // ... new MiniCssExtractPlugin(), ], // ... module: { rules: [ { test: /\.css$/i, use: [MiniCssExtractPlugin.loader, "css-loader"], }, ], }, };
III. 压缩
-
使用命令
npm install --save-dev css-minimizer-webpack-plugin
安装用于将 CSS 抽离成单独文件的插件 -
修改 webpack.config.js,实例化插件
// ... const CssMinimizerPlugin = require("css-minimizer-webpack-plugin"); module.exports = { // ... mode: "production", // ... optimization: { minimizer: [new CssMinimizerPlugin()], }, };
b. 数据
- 数据文件格式一般为 JSON、CSV、TSV、XML 等,其中:
- JSON 默认内置支持
- CSV、TSV 需要 csv-loader
- XML 需要 xml-loader
I. 类 JSON
类 JSON 的数据格式包括 json5、toml、yaml 等
-
使用命令
npm install --save-dev json5 toml yaml
安装相关包 -
修改 webpack.config.js,配置资源文件
// ... const json5 = require("json5"); const toml = require("toml"); const yaml = require("yaml"); module.exports = { // ... module: { rules: [ { test: /\.json5$/i, type: "json", parser: { parse: json5.parse, }, }, { test: /\.toml$/i, type: "json", parser: { parse: toml.parse, }, }, { test: /\.yaml$/i, type: "json", parser: { parse: yaml.parse, }, }, ], }, };
-
修改 main.js,导入数据文件
import json5 from "data.json5"; import toml from "data.toml"; import yaml from "data.yaml"; console.log(json5, toml, yaml); // ...
II. CSV、TSV、XML
-
使用命令
npm install --save-dev csv-loader xml-loader
安装相关的加载器 -
修改 webpack.config.js,配置资源文件
module.exports = { // ... module: { rules: [ { test: /\.(csv|tsv)$/i, use: "csv-loader", }, { test: /\.xml$/i, use: "xml-loader", }, ], }, };
-
修改 main.js,导入数据文件
import ID from "data.csv"; import Name from "data.xml"; console.log(ID, Name); // ...
(5)Babel
Babel 用于将 ES6 语法转换成低版本浏览器也可以支持的语法
-
使用命令
npm install --save-dev babel-loader @babel/core @babel/preset-env @babel/runtime @babel/plugin-transform-runtime
安装相关包- babel-loader 用于在 Webpack 中加载 Babel
- @babel/core 是 Babel 核心模块
- @babel/preset-env 是 Babel 的预设
- @babel/runtime 包含了必要的 regeneratorRuntime
- @babel/plugin-transform-runtime 可以在需要 regeneratorRuntime 的地方自动导入包
-
修改 webpack.config.js,配置资源文件
module.exports = { // ... module: { rules: [ { test: /\.js$/i, exclude: /node_modules/, use: { loader: "babel-loader", options: { presets: ["@babel/preset-env"], plugins: [["@babel/plugin-transform-runtime"]], }, }, }, ], }, };
-
修改 main.js,使用一些 ES6 语法
// ... (async () => { await new Promise((resolve) => { setTimeout(resolve, 2000); console.log("waiting..."); }); })();
(6)JavaScript 压缩
-
使用命令
npm install --save-dev terser-webpack-plugin
安装相关插件 -
修改 webpack.config.js,实例化插件
// ... const TerserPlugin = require("terser-webpack-plugin"); module.exports = { // ... mode: "production", // ... optimization: { minimizer: [new TerserPlugin()], }, };
(7)PostCSS 与 CSS 模块
PostCSS 是用 JavaScript 转换 CSS 的工具,官网链接:https://postcss.org/
-
使用命令
npm install --save-dev style-loader css-loader postcss-loader autoprefixer
安装相关加载器与插件 -
修改 Webpack 配置文件
module.exports = { // ... module: { rules: [ { test: /\.css$/i, // include: path.resolve(__dirname, "/src"), exclude: /node_modules/, use: [ { loader: "style-loader", }, { loader: "css-loader", options: { importLoaders: 1, // 在处理 CSS 文件时,会额外使用一个加载器来处理 @import 语句 modules: true, // 启用 CSS 模块化,使得每个 CSS 类名都具有局部作用域 localIdentName: "[local]-[hash:base64:6]", // 定义生成的局部类名的格式 [原始类名]-[6 位 base64 编码哈希值] }, }, { loader: "postcss-loader", }, ], }, ], }, };
-
创建 postcss.config.js 作为 PostCSS 配置文件
module.exports = { plugins: [require("autoprefixer")], };
(8)TypeScript
-
使用命令
npm install --save-dev typescript ts-loader
安装相关插件和加载器 -
使用命令
npx tsc --init
生成 TypeScript 配置文件 tsconfig.json,在其中开启(取消注释)需要的配置 -
修改 Webpack 配置文件
module: { rules: [ { test: /\.ts$/i, exclude: /node_modules/, use: "ts-loader", }, ], }, resolve: { extensions: [".ts", ".js"], },
0x03 输出管理
(1)代码分离
a. 入口起点
通过不同的入口起点实现代码分离
-
修改 webpack.config.js
module.exports = { entry: { // 多个入口 main: "./src/main.js", say: "./src/say.js", }, output: { filename: "[name].bundle.js", // [name] 是 webpack 提供的占位符,用来表示当前 chunk 的名称 path: path.resolve(__dirname, "dist"), clean: true, }, // ... };
此时,不同的入口使用重复的代码或依赖会被重复打包
-
继续修改 webpack.config.js,通过添加一些配置防止重复打包
-
入口依赖
module.exports = { entry: { main: { import: "./src/main.js", dependOn: "shared", }, say: { import: "./src/say.js", dependOn: "shared" }, shared: "./src/shared.js", // 共享模块 }, // ... };
-
内置插件
module.exports = { entry: { main: "./src/main.js", say: "./src/say.js", }, output: { filename: "[name].bundle.js", path: path.resolve(__dirname, "dist"), clean: true, }, optimization: { // 使用内置插件 splitChunks: { chunks: "all", }, }, // ... };
-
b. 动态导入
通过
import
语法或require.ensure
语法实现动态代码拆分
-
修改 say.js
function say(text) { return text; } export default say;
-
在 src 目录下创建 async-module.js 文件
function getComponent() { return import("./say.js").then(({ default: say }) => { const element = document.createElement("div"); element.innerHTML = say("hello world"); return element; }); } getComponent().then((element) => { document.body.appendChild(element); });
-
修改 main.js
import "./async-module.js";
-
修改 webpack.config.js
const HtmlWebpackPlugin = require("html-webpack-plugin"); const path = require("path"); module.exports = { entry: "./src/main.js", output: { filename: "bundle.js", path: path.resolve(__dirname, "dist"), clean: true, }, mode: "development", plugins: [ new HtmlWebpackPlugin({ template: "./index.html", inject: "body", }), ], devtool: "inline-source-map", devServer: { static: "./dist", }, optimization: { splitChunks: { // 当需要同时实现动态导入和静态导入时开启 chunks: "all", }, }, };
c. 按需加载(懒加载)
修改 main.js
const buttonElement = document.createElement("button");
buttonElement.innerText = "Click me";
buttonElement.addEventListener("click", () => {
import(/* webpackChunkName: 'say' */ "./say.js").then(({ default: say }) => {
const element = document.createElement("div");
element.innerHTML = say("hello world");
document.body.appendChild(element);
});
});
document.body.appendChild(buttonElement);
d. 预获取 / 预加载
修改 main.js,通过魔法注释 webpackPrefetch: true
开启预获取,实现预加载
const buttonElement = document.createElement("button");
buttonElement.innerText = "Click me";
buttonElement.addEventListener("click", () => {
import(/* webpackChunkName: 'say', webpackPrefetch: true */ "./say.js").then(({ default: say }) => {
const element = document.createElement("div");
element.innerHTML = say("hello world");
document.body.appendChild(element);
});
});
document.body.appendChild(buttonElement);
(2)浏览器缓存
浏览器通过缓存技术,根据资源名称,当网站的请求命中缓存时使用缓存,从而减少网络流量,提高加载速度;当新版本的资源文件名没有改变,浏览器则会继续使用缓存中的旧版本,造成问题
-
修改 webpack.config.js,调整输出文件的文件名
module.exports = { // ... output: { filename: "[name].[contenthash].js", // ... }, // ... };
-
继续修改 webpack.config.js,缓存第三方库
module.exports = { // ... optimization: { splitChunks: { cacheGroups: { vendor: { test: /[\\/]node_modules[\\/]/, name: "vendors", chunks: "all", }, }, }, }, };
0x04 环境分合
- 项目的环境主要包括开发环境(development)与生产环境(production)
- 可以将开发环境与生产环境的配置间存在的相同部分抽离,并基于 Webpack 命令的
--env
选项传值,实现分环境打包
-
在项目根目录下新建 config 目录,其中创建以下三个配置文件,实现配置拆分
-
webpack.config.dev.js
module.exports = { output: { filename: "[name].js", }, mode: "development", devtool: "inline-source-map", devServer: { static: "./dist", }, };
-
webpack.config.prod.js
const CssMinimizerPlugin = require("css-minimizer-webpack-plugin"); const MiniCssExtractPlugin = require("mini-css-extract-plugin"); const TerserPlugin = require("terser-webpack-plugin"); module.exports = { output: { filename: "[name].[contenthash].js", publicPath: "", }, mode: "production", plugins: [ new MiniCssExtractPlugin({ filename: "[name].[contenthash].css", }), ], optimization: { minimizer: [new TerserPlugin(), new CssMinimizerPlugin()], }, performance: { hints: false, }, };
-
webpack.config.common.js
const HtmlWebpackPlugin = require("html-webpack-plugin"); const path = require("path"); module.exports = { entry: "./src/main.js", output: { path: path.resolve(__dirname, "../dist"), clean: true, }, plugins: [ new HtmlWebpackPlugin({ template: "./index.html", inject: "body", }), ], optimization: { splitChunks: { cacheGroups: { vendor: { test: /[\\/]node_modules[\\/]/, name: "vendors", chunks: "all", }, }, }, }, };
-
-
使用命令
npm install --save-dev webpack-merge
安装合并配置的依赖 -
修改 webpack.config.js,合并配置文件
const { merge } = require("webpack-merge"); const common = require("./config/webpack.config.common"); const production = require("./config/webpack.config.prod"); const development = require("./config/webpack.config.dev"); module.exports = (env) => { if (env.production) { return merge(common, production); } else { return merge(common, development); } };
-
修改 package.json,通过 npm 脚本简化打包命令
{ // ... "scripts": { "dev": "webpack serve --env development", "build": "webpack --env production" }, // ... }
其中,命令选项
--env
用于配置环境变量
0x05 开发效率
(1)source-map
-
在 Webpack 的配置文件中,属性
devtool
用于配置 source-map -
source-map 用于在调试过程中,把报错的代码映射到源码,因此不建议将 source-map 用于生产环境
-
Webpack 内置了七种 source-map:
名称 功能 eval 每个模块会封装到 eval()
中执行,并在结尾追加//# sourceURL=webpack://webpack/[文件相对路径]
eval-source-map 与 eval 类似,但对应的 DataUrl 形式的 source-map 生成在结尾的注释中 source-map 每个模块会生成对应的 .map 文件 hidden-source-map 与 source-map 类似,但不会在结尾追加注释 cheap-source-map 生成没有列信息的 .map 文件,不包含加载器的 source-map cheap-module-source-map 与 cheap-source-map 类似,但加载器的 source-map 被简化为只包含对应行 inline-source-map 每个模块会在各自打包文件结尾,生成对应的 DataUrl 形式的 source-map
(2)devServer
-
devServer 用于启动一个 Web 服务来模拟访问,其中包含多个配置项,如 Web 服务根目录、端口等
devServer: { static: { directory: path.join(__dirname, "dist"), }, port: 8081, },
-
devServer 还提供了代理功能
devServer: { proxy: { "/api": { target: "http://localhost:3000", pathRewrite: { '^/api': "", // 将请求接口时开头的 /api 字符串删除 }, }, }, },
上述代理实现了把对 /api/auth 的请求代理到 http://localhost:3000/auth
-
devServer 支持使用 HTTPS、HTTP/2 等协议
devServer: { // https: true, // 将本地 HTTP 服务变成 HTTPS 服务 https: { // 提供证书 cacert: "./server.pem", }, http2: true, // 开启 HTTP/2 },
-
devServer 支持通过
historyApiFallback
配置 404 时的静态资源响应devServer: { // historyApiFallback: true, // 默认重定向到 index.html historyApiFallback: { rewrites: [ { from: /^\$/, to: "a.html" }, { from: /./, to: "b.html" }, ] } },
-
devServer 支持同一局域网下的其他用户访问该 Web 服务
devServer: { host: "0.0.0.0", },
-
devServer 支持配置模块热替换与热更新
devServer: { hot: true, // 热替换 liveReload: true, // 热更新 },
0x06 模块与依赖
(1)模块
-
Webpack 模块主要包括:
- ECMAScript 模块,如
import handler from "./module";
- CommonJS 模块,如
const handler = require("./module");
- AMD 模块,如 RequireJS
- Assets 模块,如 CSS 的
url()
同时 Webpack 支持通过加载器导入其他类型的模块
- ECMAScript 模块,如
-
Webpack 模块的解析通过配置文件中的属性
resolve
来配置resolve: { alias: { // 别名 '@': path.resolve(__dirname, './src'), }, extensions: [ // 按顺序依次尝试导入同名不同扩展名的模块 ".json", ".js", ".vue", ], },
-
对于如 jQuery 等通过 CDN 引入不会被打包过程修改的第三方库,可以在配置文件中,通过外部扩展属性
externals
的方式导入externals: { jquery: '$', },
(2)依赖
-
依赖:当 A 文件的一些功能需要借助 B 文件实现时,则称 A 依赖 B
-
Webpack 会从入口
entry
开始,根据文件间的依赖关系,递归地构建出依赖图 -
举例:通过官方分析工具查看依赖图
-
使用命令
npm install --save-dev webpack-bundle-analyzer
安装相关工具 -
修改开发环境的配置文件
const { BundleAnalyzerPlugin } = require("webpack-bundle-analyzer"); module.exports = { // ... plugins: [new BundleAnalyzerPlugin()], };
-
(3)预置依赖
a. Shimming
Webpack 通过 Shimming 技术实现
- Shimming 译为垫片,用于在现有代码中插入小的代码片段,以增加功能或修复问题
-
修改配置文件,将自定义变量与引入的模块关联起来,以 Lodash 为例
const webpack = require("webpack"); module.exports = { // ... plugins: [ // ... new webpack.ProvidePlugin({ _: "lodash", }), ], };
此时,在其他模块使用 Lodash 时,无需使用语句
import _ from 'lodash';
而可以直接使用 Lodash 的语法 -
使用命令
npm install --save-dev imports-loader
安装用于解决 CommonJS 上下文中this
指向错误的加载器 -
修改配置文件,配置加载器
module: { rules: [ { test: require.resolve("./src/main.js"), use: "imports-loader?wrapper=window", }, ], },
-
使用命令
npm install --save-dev exports-loader
安装用于把某个模块创建为一个全局变量的加载器 -
在项目根目录下创建 global.js
const file = "filename"; const helpers = { test: () => console.log("test"), parse: () => console.log("parse"), };
-
修改配置文件,配置加载器
module: { rules: [ { test: require.resolve("./src/global.js"), use: "exports-loader?type=commonjs&exports=file,multiple|helpers.parse|parse", }, ], },
其中,关于加载器的配置内容的可以理解为
module.exports = { ...file, parse: helpers.parse, };
b. Polyfill
Webpack 通过 Polyfill 技术结合 Shimming 实现在旧浏览器中不支持的现代 Web API
-
安装 Babel 相关包和加载器
-
使用命令
npm install --save-dev core-js@3
安装 core-js -
修改配置文件
module.exports = { // ... module: { rules: [ { test: /\.js$/, exclude: /node_modules/, use: { loader: "babel-loader", options: { presets: [ [ "@babel/preset-env", { targets: [ "last 1 version", // 最新一个版本 "> 1%", // 使用率超过 1% 的浏览器 ], useBuiltIns: "usage", // 按需加载 corejs: 3, // core-js 版本为 3 }, ], ], plugins: [["@babel/plugin-transform-runtime"]], }, }, }, ], }, };
也可以使用命令
npm install --save-dev @babel/polyfill
安装 Babel 提供的 Polyfill 插件,在相关模块中直接导入,但是会导致打包体积骤增,不建议直接使用
(4) Tree Shaking
-
Tree Shaking 是用于描述 “移除 JavaScript 上下文中未引用的代码” 的术语
- 自 Webpack2 开始应用了 Tree Shaking
-
举例:
-
在 src 目录下创建 math.js
export const add = (a, b) => a + b; export const sub = (a, b) => a - b;
-
修改 main.js
import { add } from "./math"; console.log(add(1, 2)); // 未使用 sub
-
修改配置文件
module.exports = { // ... optimization: { usedExports: true, }, };
-
使用命令
npx webpack
完成打包-
开发环境:
// ... /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { /* harmony export */ __webpack_require__.d(__webpack_exports__, { /* harmony export */ add: () => (/* binding */ add) /* harmony export */ }); /* unused harmony export sub */ const add = (a, b) => a + b; const sub = (a, b) => a - b; /***/ }) // ...
-
生产环境:
(()=>{"use strict";console.log(1+2)})();
-
-
-
对于如 CSS 之类代码的导入属于虽然未在 JavaScript 中使用,但依然不可删除,这种代码被称为具有副作用;对于有副作用的代码,需要 Tree Shaking 保留
-
Webpack4 默认所有代码都有副作用,即默认不进行 Tree Shaking;Webpack5 默认进行 Tree Shaking,需要在 package.json 中配置 sideEffects 属性,该属性取值包括:
取值 说明 true 设置所有代码具有副作用 false 设置所有代码不具有副作用 [ ] 数组,指定具有副作用的代码,如 ["*.css"]
0x07 应用程序
(1) 多页面应用(MPA)
-
多页面应用由多个独立的 HTML 页面组成,每个页面都有自己独立的 URL,用户在浏览器中访问不同的 URL 时会加载不同的页面资源
-
举例:配置同一网站的全球版和中文版
const HtmlWebpackPlugin = require("html-webpack-plugin"); module.exports = { entry: { global: { import: ["./src/global.js", "./src/global2.js"], filename: "global/main.js", }, "zh-CN": { import: "./src/zh-CN.js", filename: "zh-CN/main.js", }, }, plugins: [ new HtmlWebpackPlugin({ title: "global", // 设定标题,可以在模板中使用 template: "./global/index.html", // 使用 page1 目录下的模板 inject: "body", filename: "global/index.html", // 输出到 page1 目录 chunks: ["global"], // 使用名为 main 的入口串口 publicPath: "example.com/", // 配置全球版公共路径 }), new HtmlWebpackPlugin({ title: "中文", template: "./zh-CN/index.html", inject: "body", filename: "zh-CN/index.html", chunks: ["zh-CN"], publicPath: "example.com/zh-CN/", // 配置中文版公共路径 }), ], // ... };
(2)渐进式网络应用(PWA)
-
渐进式网络应用是一种利用现代 Web 技术提升用户体验,使其能够提供类似原生应用的交互和功能,同时保持网页的可访问性和连接性的网络应用
- 之前的应用都是需要服务器在线才能正常访问
-
举例:创建一个 PWA
-
使用命令
npm install --save-dev workbox-webpack-plugin
安装 Workbox 插件 -
修改配置文件,实例化插件
const WorkboxPlugin = require("workbox-webpack-plugin"); module.exports = { // ... plugins: [ // ... new WorkboxPlugin.GenerateSW({ clientsClaim: true, // 客户端获取到新的资源后,立即获取 skipWaiting: true, // 跳过等待 }), ], };
-
修改 main.js,注册 Service Worker
// ... if ("serviceWorker" in navigator) { window.addEventListener("load", () => { navigator.serviceWorker .register("/service-worker.js") .then((registration) => { console.log("SW registered: ", registration); }) .catch((registrationError) => { console.log("SW registration failed: ", registrationError); }); }); }
-
使用命令
npx webpack serve --open
打包并运行
-
(3) 自定义库(轮子)
修改配置文件
module.exports = {
// ...
output: {
// ...
library: {
name: "myLib", // 库名称
type: "umd", // 输出类型(umd 兼容 cjs、ejs、amd 等类型)
globalObject: "globalThis", // 全局对象
}
},
//...
};
登录后发布至 npm
(4) 模块联邦(微前端)
-
模块联邦用于解决多前端项目间模块共享的问题
- 模块列表是 Webpack5 支持的新特性
-
举例:在 A 项目中使用 B 项目的模块
-
修改 B 项目的配置文件
const { ModuleFederationPlugin } = require("webpack").container; module.exports = { // ... plugins: [ // ... new ModuleFederationPlugin({ name: "bProject", // 模块联邦名称 filename: "bRemoteEntry.js", // 模块联邦入口文件 remotes: {}, // 模块联邦远程模块 exposes: { // 模块联邦暴露模块 // 暴露模块 "./myMath": "./src/math.js", }, shared: [], // 模块联邦共享模块 }), ], };
-
修改 A 项目的配置文件
const { ModuleFederationPlugin } = require("webpack").container; module.exports = { // ... plugins: [ // ... new ModuleFederationPlugin({ name: "aProject", filename: "aRemoteEntry.js", remotes: { bModules: "bProject@http://localhost:8081/bRemoteEntry.js", }, exposes: {}, shared: [], }), ], };
其中,远程模块的格式为:
自定义名称: "模块联邦名称@模块联邦地址/模块联邦入口文件"
-
在 A 项目的某个文件中导入 B 项目的模块
import("bModules/myMath").then((module) => { console.log(module.add(1, 2)); });
其中,导入的格式为:
import("自定义名称/暴露模块名称")
-
0x08 性能优化
构建性能的优化主要分为三个方向:
- 通用环境,即包括开发环境和生产环境
- 开发环境
- 生产环境
(1)通用环境
- 版本:更新 Webpack 以及 NodeJS 相关工具和环境到最新版本
- 加载器:将加载器应用于最少数量的必要模块(
include
、exclude
) - 工具:尽量减少额外插件、加载器等工具的使用;对于自定义的工具需要进行概要分析,避免引入性能问题
- 模块解析:减少
resolve
项中modules
、extensions
、mainFiles
、descriptionFiles
的条目数量,及时关闭symlinks
、cacheWithContext
等未使用的选项 - 体积:减少编译结果的整体大小
- 使用体积小、数量少的依赖
- 在 MPA 中使用 SplitChunksPlugin 并开启异步模式
- 合理引入 Tree Shaking
- 缓存:将 webpack.config.js 的 cache 类型设置为内存或文件系统,并使用 package.json 中的
postinstall
清除缓存目录 webpack.ProgressPlugin
:合理使用webpack.ProgressPlugin
,视情况删除后可以缩短构建时间webpack.DllPlugin
:webpack.DllPlugin
可以为更改不频繁的代码(如第三方库)生成单独的编译结果(缺点:增加构建过程的复杂度)- worker 池:通过 thread-loader 将非常消耗资源的加载器分流给一个 worker 池(不宜使用过多的 worker)
(2)开发环境
- 增量编译:在打包开发时,通过 Webpack 命令内置的
--watch
开启监听模式,实现增量编译 - 内存中编译:通过 webpack-dev-server 等工具实现在内存中编译,替代写入磁盘
stats.toJson
加速:Webpack4 默认使用stats.toJson
输出大量数据,非必要时避免使用- 调试工具:对于不同的 source-map,最好的选择是使用 eval-cheap-module-source-map
- 环境区分:避免在开发环境使用一些生产环境需要的工具,如代码压缩、混淆等工具
- 最小化入口串口:确保在生成入口串口时,尽量减少其体积以提供性能,可以通过
optimization.runtimeChunk
为运行时创建额外的串口 - 避免额外优化:Webpack 通过执行额外的算法任务来优化输出结果的体积和加载性能,但仅适用于小型代码库,对于大型代码库需要关闭
optimization.removeAvailableModules
、optimization.removeEmptyChunks
、optimization.splitChunks
等优化项 - 避免额外输出:Webpack 打包过程中会默认输出生成的路径信息,可以通过
output.pathinfo
关闭
(3)生产环境
环境区分:避免在生产环境使用一些开发环境需要的工具,如 source-map、devServer 等工具
-End-
标签:...,exports,webpack,module,js,Webpack,loader From: https://www.cnblogs.com/SRIGT/p/18646259