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 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. Dev server sửa cả hai, và source map cho bạn gỡ lỗi code gốc thay vì bundle. Cùng nhau chúng làm phát triển hằng ngày dễ chịu.
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
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" } }
Lưu ý webpack serve, không phải webpack. Vì build trong bộ nhớ, dev server nhanh hơn nhiều so với ghi dist/ mỗi lần.
2. 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 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
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. 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.
Chạy hai panel của demo: tăng cả hai bộ đếm, rồi “sửa một module”. Bên không-HMR reload toàn trang và reset về 0; bên HMR giữ con số.
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());
}
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 map — gỡ lỗi code gốc
Bundle trình duyệt chạy đã bị biến đổi và minify — vô dụng để gỡ lỗi. 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.
Bạn điều khiển bằng devtool. Chuyển chế độ trong demo để thấy stack trace và đánh đổi tốc độ/chất lượng đổi.
5. Chọn một devtool
| Value | Speed | Dùng |
|---|---|---|
false | fastest | không map |
eval-cheap-module-source-map | fast | mặc định dev |
inline-source-map | slow | map nhúng trong bundle |
source-map | slowest | production |
Cho dev, eval-cheap-module-source-map cân bằng tốc độ build lại và số dòng dùng được. 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. Quy trình dev
npm run dev # webpack serve — in-memory build, HMR, source maps
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. Lớp phủ lỗi hiện lỗi biên dịch/chạy ngay trong trang. Đó là vòng lặp bạn sống cả ngày.
7. Bài tập
1. 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?
Lời giải
historyApiFallback: true — phục vụ index.html cho route lạ để router SPA xử lý.
2. Build lại dev chậm và bạn để devtool: "source-map". Lựa chọn tốt hơn cho dev?
Lời giải
eval-cheap-module-source-map — build lại nhanh hơn nhiều với map vẫn dùng được.
3. Ở 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?
Lời giải
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.
Nâng cao:trong tab source-map, so false và eval-cheap-module-source-map — để ý stack trace đổi từ main.min.js minify sang src/user.ts:14.
Điểm chính
webpack servebuild trong bộ nhớ, phục vụ app, và tự tải lại.- HMR hoán module đã đổi mà không reload toàn trang, giữ state.
- Dùng
devServer.proxyđể chuyển tiếp API và tránh CORS khi dev. - Source map ánh xạ bundle về file gốc.
eval-cheap-module-source-mapcho dev,source-mapcho production.
Tiếp theo
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.