jvinhit//lab

Search posts

Type to search across journal entries.

navigate open esc close

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: deterministic for caching (Part 8) {cho caching}.
  • splitChunks to separate vendor from app code (Part 6) {tách vendor khỏi code app}.
  • source-map so production stack traces are readable (Part 5) {để stack trace production đọc được}.
  • Extract CSS with MiniCssExtractPlugin, not style-loader (Part 4) {tách CSS, không style-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}:

ToolWhy move {Vì sao chuyển}Friction {Trở ngại}
Viteinstant 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}
RspackWebpack-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}
TurbopackNext.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}:

WebpackVite
entryindex.html is the entry {index.html là entry}
loadersbuilt-in + plugins (esbuild) {tích hợp sẵn + plugin}
resolve.aliasresolve.alias (same idea) {cùng ý tưởng}
splitChunksautomatic via Rollup {tự động qua Rollup}
DefinePlugindefine / import.meta.env
devServerbuilt 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ằng webpack-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}.