企业级实践
大型前端项目在 Webpack 场景下的多环境拆分、Docker 构建缓存、自定义 CLI 与微前端(Module Federation / single-spa)等实践。入门见 工程化概览。
一、多环境配置拆分
生产、开发、测试等环境往往需要不同的配置(API 地址、source map 策略、优化选项等)。推荐使用 webpack-merge 将公共配置与环境特定配置分离。
1. 文件结构
build/
webpack.common.js # 公共配置(entry、output、module.rules)
webpack.dev.js # 开发配置(devServer、cheap-source-map)
webpack.prod.js # 生产配置(压缩、contenthash、splitChunks)
2. 使用 webpack-merge 合并
// webpack.dev.js
const { merge } = require("webpack-merge");
const common = require("./webpack.common.js");
module.exports = merge(common, {
mode: "development",
devtool: "eval-cheap-module-source-map",
devServer: { hot: true, port: 8080 },
});
// webpack.prod.js
const { merge } = require("webpack-merge");
const common = require("./webpack.common.js");
module.exports = merge(common, {
mode: "production",
output: { filename: "[name].[contenthash].js" },
optimization: { minimize: true },
});
3. 通过 --env 传递环境变量
在 package.json 中使用 --env 参数:
"scripts": {
"build:dev": "webpack --env production=false --config build/webpack.common.js",
"build:prod": "webpack --env production=true"
}
Webpack 配置接收 env 参数:
// webpack.common.js
module.exports = (env, argv) => {
const isProd = env.production;
return {
// ... 公共配置
plugins: [
new webpack.DefinePlugin({
"process.env.API_BASE": JSON.stringify(
isProd ? "https://api.prod.com" : "https://api.dev.com",
),
}),
],
};
};
二、构建流程的 Docker 化
将 Webpack 构建封装到 Docker 中,可以保证环境一致性,并利用缓存加速 CI。
1. 多阶段构建 Dockerfile
# 第一阶段:安装依赖(缓存 node_modules)
FROM node:22-alpine AS deps
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci --only=production
# 第二阶段:开发/构建阶段
FROM node:22-alpine AS builder
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY . .
RUN npm run build
# 第三阶段:生产运行(可选,若只需构建产物)
FROM nginx:alpine
COPY --from=builder /app/dist /usr/share/nginx/html
2. 利用 Webpack 持久化缓存优化 CI
Webpack 5 支持 cache: filesystem,可以将模块编译结果缓存到磁盘,加速二次构建。
// webpack.common.js
module.exports = {
cache: {
type: "filesystem",
buildDependencies: {
config: [__filename], // 配置文件变化时重新缓存
},
},
};
在 Docker 中,可以将缓存目录挂载为卷,保留跨构建的缓存:
# 构建时挂载 cache 目录
RUN --mount=type=cache,target=/app/node_modules/.cache/webpack npm run build
或在 docker-compose.yml 中:
services:
build:
volumes:
- webpack_cache:/app/node_modules/.cache/webpack
volumes:
webpack_cache:
3. CI 优化技巧
- 使用
npm ci替代npm install(严格依据 lockfile,更快)。 - 对
node_modules进行缓存(GitLab CI:cache、GitHub Actions:actions/cache)。 - 将 Webpack 构建与 Docker 镜像构建分离,只打包 dist 到最终镜像。
三、自定义 CLI 封装
对于中大型团队,往往需要封装内部脚手架,统一构建规范。可以基于 webpack Node API + commander 实现。
1. 基本结构
// bin/cli.js
#!/usr/bin/env node
const { program } = require('commander');
const webpack = require('webpack');
const devServer = require('webpack-dev-server');
const getConfig = require('../config');
program
.command('dev')
.description('启动开发服务器')
.action(() => {
const config = getConfig('development');
const compiler = webpack(config);
const server = new devServer(compiler, config.devServer);
server.start();
});
program
.command('build')
.description('生产构建')
.option('-p, --production', '生产模式')
.action((options) => {
const config = getConfig('production');
webpack(config, (err, stats) => {
if (err) throw err;
console.log(stats.toString({ colors: true }));
});
});
program.parse(process.argv);
2. 动态配置生成(getConfig 函数)
const { merge } = require("webpack-merge");
const baseConfig = require("../webpack.common.js");
module.exports = (mode) => {
const envConfig =
mode === "development" ? require("../webpack.dev.js") : require("../webpack.prod.js");
return merge(baseConfig, envConfig, { mode });
};
3. 发布到 npm
- 在
package.json中设置bin字段:"bin": { "mycli": "./bin/cli.js" } - 本地测试:
npm link之后即可全局使用mycli dev
四、微前端集成:Module Federation + single-spa
目前主流的微前端方案之一是将 Webpack 5 的 Module Federation 与 single-spa 结合,实现主子应用独立开发部署,运行时共享依赖。
1. Module Federation 配置
主应用(shell):
new ModuleFederationPlugin({
name: "shell",
remotes: {
app1: "app1@http://localhost:3001/remoteEntry.js",
app2: "app2@http://localhost:3002/remoteEntry.js",
},
shared: {
react: { singleton: true, eager: true, requiredVersion: "^18.0.0" },
"react-dom": { singleton: true, eager: true },
},
});
子应用(app1):
new ModuleFederationPlugin({
name: "app1",
filename: "remoteEntry.js",
exposes: {
"./bootstrap": "./src/bootstrap",
},
shared: {
react: { singleton: true, requiredVersion: "^18.0.0" },
"react-dom": { singleton: true },
},
});
2. 集成 single-spa
- 每个子应用需要导出
bootstrap函数,并注册到 single-spa。 - 主应用使用
System动态加载远程模块。
子应用入口(bootstrap.js):
import React from "react";
import ReactDOM from "react-dom";
import singleSpaReact from "single-spa-react";
import Root from "./Root";
const reactLifecycles = singleSpaReact({
React,
ReactDOM,
rootComponent: Root,
errorBoundary(err, info, props) {
return <div>Error</div>;
},
});
export const { bootstrap, mount, unmount } = reactLifecycles;
主应用注册子应用:
import { registerApplication, start } from "single-spa";
registerApplication({
name: "@org/app1",
app: () => import("app1/bootstrap"), // 来自 Module Federation remote
activeWhen: ["/app1"],
});
start();
3. 共享依赖策略与版本冲突
- 使用
singleton: true确保 React 等库只加载一次。 - 使用
requiredVersion声明兼容版本范围,Webpack 会在加载时检查主从版本是否匹配,不匹配时会在控制台 warning 但继续执行(可选择eager: true强制主应用提供)。 - 对于非核心库(如 lodash),可以设置
singleton: false,允许主子应用各自加载独立版本,避免版本冲突。
4. 构建与部署
- 每个子应用独立构建,输出
remoteEntry.js及各自的 chunk。 - 主应用构建时不需要知道子应用的具体代码,只需远程入口地址(可在运行时动态注入)。
- 生产环境可将子应用的
remoteEntry.js上传到 CDN,主应用通过环境变量读取地址。
总结
| 实践方向 | 关键技术 | 收益 |
|---|---|---|
| 多环境配置 | webpack-merge + --env | 配置复用、环境隔离 |
| Docker 化构建 | 多阶段构建、Webpack 持久化缓存、volume 缓存 | 环境一致、CI 提速 |
| 自定义 CLI | commander + Webpack Node API | 统一团队构建命令,可定制 |
| 微前端集成 | Module Federation + single-spa | 独立部署、技术栈无关、依赖共享 |
具体方案须结合团队基础设施与发布流程评估,不宜照搬配置。
参考文献
以下链接在编写时均可正常访问:
| 资料 | 说明 |
|---|---|
| webpack-merge | 配置合并 |
| Module Federation | 微前端 |
| single-spa | 微前端编排 |
相关文章
工作原理
Webpack 是静态模块打包器:从入口递归解析依赖,经 Loader 转换后输出一个或多个 bundle。与 Vite 对比见 Webpack 与 Vite。
产物优化
Webpack 通过代码分割、Tree Shaking、压缩与资源模块等减少体积、改善缓存。原理背景见 工作原理。
Loader
Loader 在 Webpack 构建链中把源文件转为可打包的 JS 模块(如 TS、SCSS、Vue SFC)。见 工作原理。
进阶配置
Webpack 进阶场景:多页面(MPA)、devServer、环境变量注入、Source Map、Module Federation 等。基础见 工作原理。
Plugin
Webpack 插件在构建生命周期钩子上扩展能力(修改资源、生成 HTML、上传 CDN 等)。背景见 工作原理;与 Loader 分工:Loader 转换单文件,插件面向整体构建。
其他配置
除 ESLint、Prettier 等规范文件外,仓库中常见的工具链与包管理配置如下。见 工程化概览。