jvinhit//lab

Search posts

Type to search across journal entries.

navigate open esc close

Webpack · Part 5 — Dev Server, HMR & Source Maps

The fast feedback loop: webpack-dev-server, Hot Module Replacement that preserves state, and choosing the right devtool source-map mode for development vs production. With an interactive HMR and source-map lab.

webpack --watch rebuilds on save, but you still have to reload the browser yourself, and you lose all your app state {webpack --watch build lại khi lưu, nhưng bạn vẫn phải tự tải lại trình duyệt, và mất hết state ứng dụng}. The dev server fixes both, and source maps let you debug your original code instead of the bundle {Dev server sửa cả hai, và source map cho bạn gỡ lỗi code gốc thay vì bundle}. Together they make day-to-day development pleasant {Cùng nhau chúng làm phát triển hằng ngày dễ chịu}.

Compare full reload vs HMR, and explore source-map modes below {So sánh reload toàn trang vs HMR, và khám phá các chế độ source-map bên dưới}:


1. webpack-dev-server {webpack-dev-server}

A small Express server that builds in memory (nothing written to disk), serves your app, and live-reloads on change {Một server Express nhỏ build trong bộ nhớ (không ghi xuống đĩa), phục vụ app, và tự tải lại khi thay đổi}:

npm install --save-dev webpack-dev-server
// webpack.config.js
module.exports = {
  mode: "development",
  devServer: {
    static: "./dist",
    port: 3000,
    open: true,         // open the browser
    hot: true,          // Hot Module Replacement
    historyApiFallback: true, // SPA routing → always serve index.html
  },
};
{ "scripts": { "dev": "webpack serve --mode development" } }

Note webpack serve, not webpack {Lưu ý webpack serve, không phải webpack}. Because it builds in memory, the dev server is far faster than writing dist/ each time {Vì build trong bộ nhớ, dev server nhanh hơn nhiều so với ghi dist/ mỗi lần}.


2. Useful devServer options {Tùy chọn devServer hữu ích}

devServer: {
  port: 3000,
  hot: true,
  open: true,
  historyApiFallback: true,   // for client-side routing
  compress: true,             // gzip
  proxy: [                    // forward API calls, dodge CORS in dev
    { context: ["/api"], target: "http://localhost:8080" },
  ],
  client: { overlay: true },  // show errors as a browser overlay
}

proxy is the one you’ll reach for constantly — it forwards /api/* to your backend so you avoid CORS during development {proxy là cái bạn sẽ dùng liên tục — nó chuyển tiếp /api/* tới backend để bạn tránh CORS khi phát triển}.


3. Hot Module Replacement {Hot Module Replacement}

HMR swaps just the changed module into the running app without a full reload, preserving state {HMR hoán đổi chỉ module đã thay đổi vào app đang chạy mà không reload toàn trang, giữ nguyên state}. Edit a component and the page updates while your form inputs, scroll position, and counters stay put {Sửa một component và trang cập nhật trong khi input form, vị trí cuộn, và bộ đếm vẫn nguyên}.

Run the demo’s two panels: increment both counters, then “edit a module” {Chạy hai panel của demo: tăng cả hai bộ đếm, rồi “sửa một module”}. The non-HMR side does a full reload and resets to 0; the HMR side keeps the count {Bên không-HMR reload toàn trang và reset về 0; bên HMR giữ con số}.

For plain JS you sometimes accept a module’s update explicitly {Với JS thuần đôi khi bạn chấp nhận cập nhật của module một cách tường minh}:

if (module.hot) {
  module.hot.accept("./render.js", () => rerender());
}

Frameworks wire this up for you — React Fast Refresh, Vue HMR — so you rarely write it by hand {Framework đã nối sẵn cho bạn — React Fast Refresh, Vue HMR — nên hiếm khi bạn tự viết}.


4. Source maps — debug original code {Source map — gỡ lỗi code gốc}

The bundle the browser runs is transformed and minified — useless for debugging {Bundle trình duyệt chạy đã bị biến đổi và minify — vô dụng để gỡ lỗi}. A source map maps bundle positions back to your original files, so DevTools shows src/user.ts:14 instead of main.min.js:1:48213 {Một source map ánh xạ vị trí bundle về file gốc, nên DevTools hiện src/user.ts:14 thay vì main.min.js:1:48213}.

You control it with devtool {Bạn điều khiển bằng devtool}. Switch modes in the demo to see the stack trace and the build/quality trade-off change {Chuyển chế độ trong demo để thấy stack trace và đánh đổi tốc độ/chất lượng đổi}.


5. Choosing a devtool {Chọn một devtool}

ValueSpeedUse {Dùng}
falsefastestno maps {không map}
eval-cheap-module-source-mapfastdevelopment default {mặc định dev}
inline-source-mapslowmaps embedded in bundle {map nhúng trong bundle}
source-mapslowestproduction (separate .map file) {production}

For dev, eval-cheap-module-source-map balances rebuild speed and usable line numbers {Cho dev, eval-cheap-module-source-map cân bằng tốc độ build lại và số dòng dùng được}. For production, use source-map — it emits a separate .map file you upload to your error tracker (Sentry) but do not serve to users {Cho production, dùng source-map — nó phát ra file .map riêng bạn tải lên trình theo dõi lỗi (Sentry) nhưng không phục vụ cho user}.


6. The dev workflow {Quy trình dev}

npm run dev   # webpack serve — in-memory build, HMR, source maps

Edit a file → Webpack rebuilds only the affected modules → HMR pushes the update over a WebSocket → the browser swaps the module, no reload {Sửa file → Webpack build lại chỉ module liên quan → HMR đẩy cập nhật qua WebSocket → trình duyệt hoán module, không reload}. Error overlay shows compile/runtime errors right in the page {Lớp phủ lỗi hiện lỗi biên dịch/chạy ngay trong trang}. That’s the loop you live in all day {Đó là vòng lặp bạn sống cả ngày}.


7. Exercises {Bài tập}

1. You run webpack serve and get a blank page on a /dashboard route refresh (404). Which devServer option fixes it? {Bạn chạy webpack serve và bị trang trắng khi refresh route /dashboard (404). Tùy chọn devServer nào sửa?}

Solution {Lời giải}

historyApiFallback: true — serves index.html for unknown routes so the SPA router handles them {historyApiFallback: true — phục vụ index.html cho route lạ để router SPA xử lý}.

2. Your dev rebuilds feel slow and you have devtool: "source-map". Better choice for dev? {Build lại dev chậm và bạn để devtool: "source-map". Lựa chọn tốt hơn cho dev?}

Solution {Lời giải}

eval-cheap-module-source-map — much faster rebuilds with still-usable maps {eval-cheap-module-source-map — build lại nhanh hơn nhiều với map vẫn dùng được}.

3. In production, a user’s stack trace shows main.min.js:1:48213. How do you get readable traces without shipping source to users? {Ở production, stack trace của user hiện main.min.js:1:48213. Làm sao có trace đọc được mà không ship source cho user?}

Solution {Lời giải}

Build with devtool: "source-map" and upload the .map to your error tracker; don’t deploy the map publicly {Build với devtool: "source-map" và tải .map lên trình theo dõi lỗi; đừng deploy map công khai}.

Stretch {Nâng cao}: in the source-map tab, compare false and eval-cheap-module-source-map — note the stack trace flip from minified main.min.js to src/user.ts:14 {trong tab source-map, so falseeval-cheap-module-source-map — để ý stack trace đổi từ main.min.js minify sang src/user.ts:14}.


Key takeaways {Điểm chính}

  • webpack serve builds in memory, serves your app, and live-reloads {webpack serve build trong bộ nhớ, phục vụ app, và tự tải lại}.
  • HMR swaps changed modules without a full reload, preserving state {HMR hoán module đã đổi mà không reload toàn trang, giữ state}.
  • Use devServer.proxy to forward API calls and dodge CORS in dev {Dùng devServer.proxy để chuyển tiếp API và tránh CORS khi dev}.
  • Source maps map the bundle back to your original files {Source map ánh xạ bundle về file gốc}.
  • eval-cheap-module-source-map for dev, source-map for production {eval-cheap-module-source-map cho dev, source-map cho production}.

Next up {Tiếp theo}

Part 6 — Code splitting & lazy loading: stop shipping one giant bundle — dynamic import(), splitChunks for vendor/shared code, and runtimeChunk for caching {Phần 6 — Chia tách code & tải lười: ngừng ship một bundle khổng lồ — import() động, splitChunks cho code vendor/chung, và runtimeChunk cho caching}.