Webpack · Part 12 — Production Capstone + Migration
Assemble every technique from the series into one battle-tested production config, with a copy-paste dev/prod split — then a clear-eyed guide to when and how to migrate to Vite or Rspack. With a config capstone builder.
Eleven parts, one config {Mười một phần, một config}. This finale assembles everything — mode, hashing, splitting, CSS extraction, source maps, budgets, caching — into a production setup you can paste into a real project {Phần kết này ghép tất cả — mode, hash, splitting, tách CSS, source map, ngân sách, cache — thành một thiết lập production bạn có thể dán vào dự án thật}. Then the honest question: should you even still be on Webpack? {Rồi câu hỏi thật lòng: bạn còn nên ở lại Webpack không?}
Toggle each technique below to build the config and watch the production-readiness score {Bật/tắt từng kỹ thuật bên dưới để xây config và xem điểm sẵn-sàng-production}:
1. Split dev and prod {Tách dev và prod}
Don’t cram both modes into one file full of if (isProd) {Đừng nhồi cả hai mode vào một file đầy if (isProd)}. Use a common base and merge mode-specific configs with webpack-merge {Dùng một base chung và gộp config theo mode bằng webpack-merge}:
// webpack.common.js
const path = require("path");
const HtmlWebpackPlugin = require("html-webpack-plugin");
module.exports = {
entry: "./src/index.js",
output: { path: path.resolve(__dirname, "dist"), clean: true },
module: {
rules: [{ test: /\.[jt]sx?$/, exclude: /node_modules/, use: "babel-loader" }],
},
plugins: [new HtmlWebpackPlugin({ template: "./src/index.html" })],
};
// webpack.dev.js
const { merge } = require("webpack-merge");
const common = require("./webpack.common.js");
module.exports = merge(common, {
mode: "development",
devtool: "eval-source-map",
devServer: { hot: true, open: true, historyApiFallback: true },
});
2. The production config {Config production}
// webpack.prod.js
const { merge } = require("webpack-merge");
const common = require("./webpack.common.js");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
module.exports = merge(common, {
mode: "production", // Part 7: minify + tree shake
devtool: "source-map", // Part 5: debuggable prod
output: {
filename: "[name].[contenthash].js", // Part 8: caching
chunkFilename: "[name].[contenthash].js",
},
module: {
rules: [{ test: /\.css$/, use: [MiniCssExtractPlugin.loader, "css-loader"] }],
},
optimization: {
splitChunks: { chunks: "all" }, // Part 6
runtimeChunk: "single", // Part 8
moduleIds: "deterministic", // Part 8: stable hashes
},
performance: { maxEntrypointSize: 250000, hints: "error" }, // Part 9
cache: { type: "filesystem" }, // Part 9: fast rebuilds
plugins: [new MiniCssExtractPlugin({ filename: "[name].[contenthash].css" })],
});
// package.json
{
"scripts": {
"dev": "webpack serve --config webpack.dev.js",
"build": "webpack --config webpack.prod.js"
}
}
Every line traces back to a part of this series {Mỗi dòng truy về một phần của series này}. The capstone demo above is this exact config, assembled toggle by toggle {Demo capstone phía trên chính là config này, ghép từng toggle}.
3. The production checklist {Danh sách production}
-
mode: "production"— the single most important flag (Part 7) {cờ quan trọng nhất}. -
[contenthash]+runtimeChunk+moduleIds: deterministicfor caching (Part 8) {cho caching}. -
splitChunksto separate vendor from app code (Part 6) {tách vendor khỏi code app}. -
source-mapso production stack traces are readable (Part 5) {để stack trace production đọc được}. - Extract CSS with
MiniCssExtractPlugin, notstyle-loader(Part 4) {tách CSS, khôngstyle-loader}. - Performance budget to fail CI on bloat (Part 9) {ngân sách để fail CI khi phình to}.
- Bundle analyzer in CI to track size over time (Part 9) {analyzer trong CI để theo dõi kích thước}.
4. Should you migrate? {Bạn có nên di trú?}
Webpack is mature, flexible, and battle-tested — but newer tools are dramatically faster for dev {Webpack trưởng thành, linh hoạt, thực chiến — nhưng công cụ mới nhanh hơn nhiều cho dev}. Be honest about your situation {Thành thật về tình huống của bạn}:
| Tool | Why move {Vì sao chuyển} | Friction {Trở ngại} |
|---|---|---|
| Vite | instant dev server (esbuild + native ESM), great DX {dev server tức thì, DX tuyệt} | different config model; some plugins differ {mô hình config khác; vài plugin khác} |
| Rspack | Webpack-compatible API, Rust-fast {API tương thích Webpack, nhanh kiểu Rust} | newer, smaller ecosystem {mới hơn, hệ sinh thái nhỏ hơn} |
| Turbopack | Next.js’s bundler, very fast {bundler của Next.js, rất nhanh} | tied to Next.js for now {gắn với Next.js hiện tại} |
Stay on Webpack if you rely on Module Federation, niche loaders/plugins, or a large stable config that works {Ở lại Webpack nếu bạn dựa vào Module Federation, loader/plugin ngách, hoặc một config lớn ổn định đang chạy tốt}. Migrate if slow dev startup hurts daily and your stack is mainstream {Di trú nếu khởi động dev chậm gây đau hằng ngày và stack của bạn phổ thông}.
5. Migrating to Vite — the shape of it {Di trú sang Vite — hình hài}
The mental shift: Vite serves native ESM in dev (no bundling) and uses Rollup for prod builds {Chuyển tư duy: Vite phục vụ ESM native trong dev (không bundle) và dùng Rollup cho build prod}. Most concepts map across {Đa số khái niệm ánh xạ được}:
| Webpack | Vite |
|---|---|
entry | index.html is the entry {index.html là entry} |
| loaders | built-in + plugins (esbuild) {tích hợp sẵn + plugin} |
resolve.alias | resolve.alias (same idea) {cùng ý tưởng} |
splitChunks | automatic via Rollup {tự động qua Rollup} |
DefinePlugin | define / import.meta.env |
devServer | built in, instant {tích hợp, tức thì} |
The good news: everything you learned here transfers {Tin tốt: mọi thứ bạn học ở đây đều chuyển giao}. Code splitting, tree shaking, hashing, and budgets are universal bundler concepts, not Webpack trivia {Code splitting, tree shaking, hash, ngân sách là khái niệm bundler phổ quát, không phải mẹo riêng Webpack}. (That’s exactly what the next series covers.) {(Đó chính là điều series tiếp theo bàn tới.)}
6. Exercises {Bài tập}
1. Why use webpack-merge with a common/dev/prod split instead of one config with process.env.NODE_ENV branches? {Vì sao dùng webpack-merge với tách common/dev/prod thay vì một config đầy nhánh NODE_ENV?}
Solution {Lời giải}
Cleaner, less error-prone, and each file stays focused; the dev and prod configs never accidentally leak settings into each other {Sạch hơn, ít lỗi hơn, mỗi file tập trung; config dev và prod không vô tình rò rỉ thiết lập vào nhau}.
2. In the capstone demo, which single toggle gives the biggest score jump, and why? {Trong demo capstone, toggle đơn nào cho điểm nhảy lớn nhất, và vì sao?}
Solution {Lời giải}
mode: "production" (+20) — it enables minification, tree shaking, and production optimizations all at once {mode: "production" (+20) — nó bật minify, tree shaking, và tối ưu production cùng lúc}.
3. Your team’s pain is slow dev-server startup, your stack is a standard React SPA, and you don’t use Module Federation. Migrate or stay? {Nỗi đau của team là dev-server khởi động chậm, stack là React SPA chuẩn, không dùng Module Federation. Di trú hay ở lại?}
Solution {Lời giải}
Strong case to migrate to Vite — mainstream stack, no federation dependency, and dev speed is the exact thing Vite wins on {Lý do mạnh để di trú sang Vite — stack phổ thông, không phụ thuộc federation, và tốc độ dev đúng là thứ Vite thắng}.
Stretch {Nâng cao}: in the capstone demo, reach a 90+ readiness score, then write out from memory which part of the series taught each enabled technique {trong demo capstone, đạt điểm 90+, rồi tự viết phần nào của series dạy mỗi kỹ thuật đã bật}.
Key takeaways {Điểm chính}
- Split config into common / dev / prod with
webpack-merge{Tách config thành common / dev / prod bằngwebpack-merge}. - A production config is just the series assembled: mode, hashing, splitting, CSS extraction, source maps, budgets, caching {Config production chỉ là series ghép lại}.
mode: "production"is the highest-leverage single setting {mode: "production"là thiết lập đòn bẩy cao nhất}.- Migrate to Vite/Rspack when dev speed hurts and your stack is mainstream; stay for Module Federation and niche setups {Di trú sang Vite/Rspack khi tốc độ dev gây đau và stack phổ thông; ở lại cho Module Federation và thiết lập ngách}.
- The concepts transfer — you now understand bundlers, not just Webpack {Khái niệm chuyển giao — giờ bạn hiểu bundler, không chỉ Webpack}.
Series complete {Hoàn thành series}
You went from “what even is a bundle?” to a production config, a custom loader and plugin, Module Federation, and a migration strategy {Bạn đã đi từ “bundle là cái gì?” tới config production, loader và plugin tùy chỉnh, Module Federation, và chiến lược di trú}. You don’t just use Webpack now — you understand what every bundler is doing under the hood {Giờ bạn không chỉ dùng Webpack — bạn hiểu mọi bundler đang làm gì bên dưới}.