> For AI agents: the complete documentation index is available at https://nhanbe.id.vn/llms.txt, the full documentation bundle is available at https://nhanbe.id.vn/llms-full.txt.

# Chrome DevTools: Frontend Debugging Playbook

Một trang web hiện đại thường đi qua nhiều lớp trước khi chạy trong trình duyệt:

```text
.vue / .ts / .js / .scss
        ↓ loader + compiler
module đã transform
        ↓ webpack bundle
chunk JavaScript/CSS
        ↓ minimizer
file đã minimize được trình duyệt thực thi
        ↓ source map (nếu có)
mã nguồn mà developer có thể đọc và debug
```

Chrome luôn thực thi **mã đã build**. Source map chỉ làm cho Sources panel hiển thị và ánh xạ vị trí về mã bạn viết; nó không thay đổi code đang chạy. `sourceURL` lại dành cho source được tạo động bằng `eval()` hoặc `new Function()`. Hai khái niệm này thường bị nhầm khi debug Vue/Webpack.

![Chrome Debugger với editor, breakpoint và Scope panel](https://developer.chrome.com/static/docs/devtools/javascript/image/debugger-in-wide-window_960.png)

Hướng dẫn hình ảnh chính thức: [Debug JavaScript trong Chrome DevTools](https://developer.chrome.com/docs/devtools/javascript).

## Quy trình 60 giây trước khi đặt breakpoint

Khi gặp lỗi, đừng bắt đầu bằng việc thêm thật nhiều `console.log`. Hãy thu hẹp sự kiện gây lỗi trước:

1. Tạo một reproduction nhỏ và ghi lại thao tác chính xác: URL, input, thứ tự click, kích thước màn hình.
2. Mở DevTools bằng `Ctrl+Shift+I`, bật **Preserve log** trong Console và Network.
3. Trong Network, bật **Disable cache** khi DevTools đang mở; chỉ bật khi cần tái hiện tải tài nguyên thật.
4. Chọn đúng frame trong Console nếu trang dùng iframe.
5. Kiểm tra lỗi đầu tiên theo thứ tự thời gian. Lỗi đầu tiên thường là nguyên nhân, các lỗi sau có thể chỉ là hiệu ứng dây chuyền.
6. Chọn một trong bốn điểm dừng: DOM bị đổi, CSS bị ghi đè, event không chạy, hoặc request trả về dữ liệu sai.

Command Menu (`Ctrl+Shift+P`) là cách nhanh nhất để tìm các tính năng ít dùng:

| Lệnh                           | Dùng khi                                                  |
| ------------------------------ | --------------------------------------------------------- |
| `Disable JavaScript`           | Kiểm tra HTML fallback, SSR và progressive enhancement    |
| `Show coverage`                | Tìm JavaScript/CSS đã tải nhưng chưa được dùng            |
| `Show rendering`               | Kiểm tra paint flashing, layout shift, FPS và media query |
| `Show changes`                 | Xem toàn bộ CSS/DOM đã sửa trong phiên DevTools           |
| `Capture full size screenshot` | Chụp trang dài hoặc vùng overflow                         |
| `Show network conditions`      | Đổi User-Agent, ngôn ngữ hoặc mô phỏng offline            |
| `Show sensors`                 | Mô phỏng touch, orientation, geolocation và idle state    |

## 1. Elements: tìm nơi DOM bị thay đổi

### DOM đang chạy khác với HTML nguồn

Tab Elements hiển thị **DOM hiện tại**, sau khi Vue, React hoặc JavaScript đã mount và mutate trang. `View Source` chỉ hiển thị HTML response ban đầu. Khi một node thiếu class, có text sai hoặc biến mất:

1. Chọn node bằng `Ctrl+Shift+C`.
2. Nhấn `H` để ẩn node mà không xóa nó; dùng `Delete` để thử tác động của việc xóa node.
3. Nhìn vào `Attributes`, `Event Listeners`, `Properties` và `Computed` để tìm khác biệt.
4. Dùng `Ctrl+Z` để hoàn tác thay đổi thử nghiệm trong DevTools.

Node đang chọn được gọi là `$0`; bốn node vừa chọn trước đó lần lượt là `$1` đến `$4`.

```js
$0                         // Element đang chọn
$0.parentElement           // Node cha
$0.closest('[data-id]')    // Tìm component boundary hoặc vùng dữ liệu
$0.dataset                 // data-* attributes
$0.matches('.is-active')   // Kiểm tra selector
copy($0.outerHTML)         // Copy HTML hiện tại
```

Lưu ý: DOM sửa trong Elements chỉ là thí nghiệm cục bộ. Nó không sửa Vue template hay code trên server.

### DOM breakpoints: bắt đúng dòng code mutate node

Click phải node → **Break on**:

- **Subtree modifications**: child được thêm/xóa hoặc text của child thay đổi.
- **Attribute modifications**: `class`, `style`, `aria-*`, `data-*` hoặc attribute khác đổi.
- **Node removal**: chính node bị xóa khỏi DOM.

Khi code chạy, debugger dừng tại dòng JavaScript gây mutation. Đây là cách nhanh nhất để tìm script nào liên tục thêm lại class hoặc render lại một vùng UI. Chrome liệt kê các loại DOM breakpoint và XHR/event breakpoint trong tài liệu [Pause your code with breakpoints](https://developer.chrome.com/docs/devtools/javascript/breakpoints).

### Tìm event listener thật sự

```js
getEventListeners($0)       // Bảng listener đã đăng ký trên node
monitorEvents($0, 'click')  // Log click, dừng bằng unmonitorEvents($0)
monitorEvents($0, ['input', 'change'])
```

Nếu click không hoạt động, kiểm tra lần lượt:

- node có bị một phần tử khác che lên không;
- `pointer-events: none` có đang bật không;
- listener có được đăng ký trên node cha do event delegation không;
- handler có chạy nhưng return sớm vì điều kiện hay không;
- một DOM breakpoint có dừng ở đoạn code cập nhật state/render không.

## 2. CSS: phân biệt style sai với layout sai

### Đọc Styles và Computed đúng cách

- **Styles** cho biết rule nào áp dụng, rule nào bị gạch ngang vì specificity hoặc thứ tự cascade.
- **Computed** cho biết giá trị cuối cùng mà browser sử dụng.
- Trong Computed, mở rộng một property để xem rule và file CSS tạo ra giá trị đó.
- Kiểm tra **Inherited from** khi `color`, `font`, `line-height` hoặc `visibility` đến từ ancestor.
- Dùng `:hov` để ép `:hover`, `:focus`, `:focus-within`, `:active` và `:visited`.
- Dùng `cls` để bật/tắt class theo thời gian thực.
- Click dấu `+` để tạo rule tạm trong `element.style`.

![CSS rule breakdown trong Chrome DevTools](https://developer.chrome.com/static/docs/devtools/css/reference/image/a-line-line-breakdown-u-44b66fb47a1d9_960.png)

Tài liệu kèm hình: [CSS features reference](https://developer.chrome.com/docs/devtools/css/reference).

### Các câu lệnh CSS hữu ích

```js
getComputedStyle($0).display
getComputedStyle($0).getPropertyValue('z-index')
$0.getBoundingClientRect()                 // Kích thước/vị trí thực tế
document.elementFromPoint(120, 240)        // Node đang nằm trên điểm đó
getComputedStyle($0).getPropertyValue('--brand-color')
```

Nếu box có kích thước đúng nhưng không nhìn thấy, kiểm tra `opacity`, `visibility`, `display`, `overflow`, `clip-path`, `z-index` và stacking context. Nếu layout nhảy, mở **Layout** và bật overlay Grid/Flex; đừng chỉ nhìn vào giá trị `width` của một node.

### Debug CSS trong bundle đã minimize

1. Bật **Enable CSS source maps** trong Settings → Preferences → Sources.
2. Mở file CSS trong Sources và tìm `.vue`/`.scss` gốc bằng `Ctrl+P`.
3. Nếu không có map, dùng Styles để xác định selector bị ghi đè trước; chỉ sau đó mới tìm chuỗi selector trong file minified.
4. Thử sửa rule trong DevTools. Nếu cần giữ bản sửa trong lúc reload, dùng **Local Overrides** hoặc **Workspaces**.

## 3. JavaScript debugger: dừng đúng thời điểm

![Các loại breakpoint trong Sources panel](https://developer.chrome.com/static/docs/devtools/javascript/breakpoints/image/a-line-code-breakpoint-5534a230be67_960.png)

### Breakpoint theo tình huống

| Loại           | Khi nên dùng                                                              |
| -------------- | ------------------------------------------------------------------------- |
| Line-of-code   | Biết chính xác dòng cần kiểm tra                                          |
| Conditional    | Chỉ dừng khi `index === 42` hoặc payload có giá trị cụ thể                |
| Logpoint       | Muốn log mà không sửa source và không dừng chương trình                   |
| Exception      | Lỗi bị catch hoặc uncaught làm UI hỏng                                    |
| Event listener | Biết event `click`, `keydown`, `submit` có vấn đề nhưng chưa biết handler |
| XHR/fetch      | Request sai URL, sai thời điểm hoặc sai payload                           |
| DOM            | Script nào thay đổi hoặc xóa node                                         |
| Function       | Muốn dừng mỗi lần một function được gọi                                   |

Đặt **Conditional breakpoint** bằng click phải số dòng. Ví dụ điều kiện:

```js
request.url.includes('/checkout') && response.status >= 400
```

**Logpoint** hữu ích khi bug chỉ xuất hiện sau 20 lần click: log `index`, `id` và `state` mà không cần commit `console.log` vào code.

### Điều khiển khi đang paused

| Phím        | Tác vụ                               |
| ----------- | ------------------------------------ |
| `F8`        | Resume                               |
| `F10`       | Step over                            |
| `F11`       | Step into                            |
| `Shift+F11` | Step out                             |
| `Ctrl+\`    | Pause/resume nhanh                   |
| `Esc`       | Mở Console drawer khi đang ở Sources |

Khi paused, dùng **Scope** để xem local/closure/global variables, **Watch** để theo dõi expression và **Call Stack** để xem đường đi tới dòng hiện tại. Right-click frame → **Restart frame** có thể chạy lại function từ đầu, nhưng các side effect bên ngoài như network request hoặc DOM mutation không tự được hoàn tác.

### `debugger;`, `debug()` và `monitor()`

```js
debugger;                  // Dừng đúng dòng này khi DevTools mở
debug(window.fetch);        // Dừng mỗi lần fetch được gọi
undebug(window.fetch);
monitor(myHandler);        // Log mỗi lần function được gọi
unmonitor(myHandler);
console.trace('request');   // In call stack mà không cần paused
```

`debug(fn)` cần nhận **function object**, không phải tên string. Nếu function đang nằm trong closure, hãy đặt breakpoint tại nơi nó còn nằm trong scope rồi gọi `debug(fn)` từ Console.

### Async stack trace

Khi debugger dừng bên trong `fetch`, `Promise`, `setTimeout` hoặc event callback, xem **Async stack traces** để nối callback hiện tại với nơi đã tạo nó. Nếu stepping quá chậm trên ứng dụng lớn, mở menu Sources và tắt async stack traces tạm thời.

## 4. Vue + Webpack: debug code đã transpile hoặc minimize

### `sourceURL` không thay thế source map

`sourceURL` đặt tên cho code được đánh giá động:

```js
const code = `
  function renderBlock() {
    return document.createElement('div');
  }
  //# sourceURL=runtime-render-block.js
`;

new Function(code)();
```

Source này sẽ xuất hiện với tên `runtime-render-block.js` trong Sources và stack trace. Cơ chế này hữu ích cho code sinh bằng `eval()`/`new Function()`, runtime template compiler, một số dev tooling hoặc loader dùng `eval`.

Nó **không thể khôi phục** một bundle Vue đã minimize về file `.vue`. Với bundle tĩnh, browser cần `sourceMappingURL` và file `.map`. Chrome chạy mã minified nhưng hiển thị mã bạn viết khi map hợp lệ: [Debug your original code instead of deployed with source maps](https://developer.chrome.com/docs/devtools/javascript/source-maps).

### Cấu hình Webpack nên dùng

```js title="webpack.config.js"
const isProduction = process.env.NODE_ENV === 'production';

module.exports = {
  mode: isProduction ? 'production' : 'development',
  devtool: isProduction ? 'source-map' : 'eval-source-map',
};
```

- `eval-source-map`: phù hợp development, rebuild tốt và thường cho mapping chất lượng cao.
- `source-map`: phù hợp production/staging khi cần debug bundle sau minification.
- `hidden-source-map`: tạo map cho hệ thống error tracking nhưng không chèn reference công khai vào bundle.
- `nosources-source-map`: map vị trí nhưng không nhúng nội dung source; vẫn cần cân nhắc lộ path/module name.
- Tránh `eval-*` và `inline-*` trong production vì bundle lớn hơn và có thể ảnh hưởng hiệu năng. [Webpack production guide](https://webpack.js.org/guides/production/)

Với Vue CLI, `productionSourceMap` mặc định bật; có thể cấu hình `configureWebpack.devtool` hoặc tắt khi không muốn phát hành map. [Vue CLI configuration](https://cli.vuejs.org/config/#productionsourcemap)

### Bật và kiểm tra source map trong Chrome

1. Settings → Preferences → Sources → bật **Enable JavaScript source maps** và **Enable CSS source maps**.
2. Mở Sources → `Ctrl+P` → tìm tên `.vue`, `.ts`, `.scss`.
3. Kiểm tra cây `webpack://` hoặc `vite://` tùy bundler.
4. Đặt breakpoint trong file gốc, reload và tái hiện lỗi.
5. Khi paused, kiểm tra Call Stack có frame ứng dụng của bạn hay chỉ có `webpack`, `node_modules` và runtime.

Nếu không thấy source gốc, kiểm tra:

- response bundle có `//# sourceMappingURL=...` không;
- URL `.map` có trả về `200` thay vì `404`/`403` không;
- map được deploy cùng đúng hash của bundle không;
- `vue-loader`, Babel, TypeScript và loader trước đó có bật source map không;
- browser có đang dùng bundle cũ từ Service Worker/CDN không;
- source map có bị CORS, CSP hoặc authentication chặn không.

### Khi production không có source map

1. Mở file minified trong Sources.
2. Bấm `{}` để **Pretty print**.
3. Tìm chuỗi UI, endpoint, message lỗi hoặc tên CSS class bằng `Ctrl+Shift+F`.
4. Dùng breakpoint gần chuỗi đó hoặc bật **Pause on exceptions**.
5. Thêm pattern như `/node_modules/`, `webpack-internal:` vào **Ignore List** để stepping bỏ qua framework/vendor.
6. Nếu chỉ có stack trace từ error tracker, dùng đúng artifact/map của build đó; map của build khác hash sẽ cho vị trí sai.

Pretty print chỉ format code đã generate. Nó không phục hồi tên biến, file `.vue` hoặc cấu trúc module đã bị minimizer loại bỏ.

### Vue 2 và Vue 3 trong Console

Vue DevTools là lựa chọn tốt nhất để xem component tree, props, state và Pinia/Vuex. Khi cần chẩn đoán nhanh node đang chọn, có thể thử các thuộc tính nội bộ sau:

```js
// Vue 2 — thường có trong dev build
$0.__vue__

// Vue 3 — internal instance, không phải public API ổn định
$0.__vueParentComponent
$0.__vueParentComponent?.ctx
```

Đây là implementation detail; không dùng trong production code hoặc test. Nếu thuộc tính không tồn tại, hãy dùng Vue DevTools hoặc breakpoint tại handler/state mutation.

## 5. Console recipes cho JavaScript logic

### Quan sát dữ liệu

```js
console.table(items, ['id', 'status', 'price']);
console.dir($0);                         // Xem Element như object JavaScript
copy(JSON.stringify(payload, null, 2));  // Copy payload đã format
keys(window.__APP_STATE__);
values(window.__APP_STATE__);
$_                                      // Kết quả expression vừa chạy
```

### Đo và đếm

```js
console.time('load-products');
await loadProducts();
console.timeEnd('load-products');

console.count('render');
console.countReset('render');
console.assert(items.length > 0, 'Danh sách rỗng');
```

### Tìm object và listener

```js
queryObjects(MyClass);       // Instance còn sống trên heap
getEventListeners($0);
inspect($0);                 // Chuyển tới Elements
inspect(window.fetch);       // Chuyển tới Sources nếu function có source
```

Các hàm `$0`, `copy`, `debug`, `monitor`, `queryObjects` là DevTools Console utilities; chúng không tồn tại trong JavaScript chạy trên người dùng.

## 6. Network: nối request với dòng code gọi nó

Trong Network:

- bật **Preserve log** trước khi chuyển trang;
- lọc `Fetch/XHR`, `status-code:404`, `status-code:401` hoặc `method:POST`;
- xem **Payload**, **Response**, **Timing** và **Initiator**;
- dùng **Initiator** để mở stack trace nơi request được dispatch;
- click phải → **Copy → Copy as fetch** để tái hiện request trong Console;
- dùng **Sources → XHR/fetch Breakpoints** với một phần URL để dừng ngay trước `fetch()`/XHR gửi request.

Khi API trả HTTP 200 nhưng UI sai, đừng chỉ kiểm tra Network. So sánh response với object trong Scope sau bước parse/normalize; lỗi có thể nằm ở mapping field, cache hoặc race condition.

Một số filter nhanh:

| Filter | Ví dụ                    |
| ------ | ------------------------ |
| Domain | `domain:api.example.com` |
| URL    | `url:checkout`           |
| Status | `status-code:500`        |
| Method | `method:POST`            |
| Cache  | `is:from-cache`          |
| Size   | `larger-than:100k`       |

## 7. Các workflow thực tế

### Click button nhưng không có phản ứng

1. Chọn button, kiểm tra `disabled`, `pointer-events`, overlay và computed `cursor`.
2. Chạy `getEventListeners($0)`.
3. Bật Event Listener Breakpoint → Mouse → `click`.
4. Nếu không dừng, kiểm tra listener ở ancestor do event delegation.
5. Nếu dừng trong handler, xem Scope: input có đúng type không, state có bị stale không, branch nào return sớm.
6. Nếu handler chạy nhưng DOM không đổi, đặt DOM breakpoint trên vùng render và kiểm tra state mutation.

### Class bị thêm rồi bị xóa ngay

1. Chọn node → Break on → Attribute modifications.
2. Tái hiện thao tác.
3. Khi paused, xem Call Stack và file gốc trong `webpack://`.
4. Kiểm tra các effect/watch, cleanup của component, và code chạy sau `await`.

### Style đúng trong source nhưng không đúng trên trang

1. Chọn node, kiểm tra Computed property thực tế.
2. Mở rule bị gạch ngang để biết selector nào thắng.
3. Ép `:hover`/`:focus` bằng `:hov`.
4. Kiểm tra CSS source map và CSS chunk đã tải trong Network.
5. Xác nhận không có shadow DOM hoặc iframe ở giữa.

### Lỗi chỉ xuất hiện trong bundle production

1. Lấy tên file và hash từ stack trace/error tracker.
2. Đảm bảo đang dùng source map đúng build.
3. Bật Pause on caught/uncaught exceptions.
4. Nếu map có sẵn, đặt breakpoint trong `.vue`/`.ts`; nếu không, Pretty print và tìm string/endpoint.
5. Kiểm tra khác biệt production: minification, dead-code branch, feature flag, environment variable, CSP và Service Worker.
6. Thử **Local Overrides** để xác nhận giả thuyết trước khi sửa source thật.

### Tìm nơi tạo request sai

1. Network → lọc URL hoặc status.
2. Mở request → **Initiator**.
3. Đặt XHR/fetch breakpoint theo một phần URL.
4. Khi paused, xem giá trị URL, headers và payload trước khi request được gửi.
5. Nếu request được tạo sau nhiều promise, đọc Async stack trace để quay về event ban đầu.

## 8. Performance, memory và render

### Performance

1. Mở Performance, chọn CPU throttle phù hợp và record reload hoặc interaction.
2. Tìm task trên 50 ms ở Main thread.
3. Các block màu tím lặp lại thường liên quan Style/Layout; xem có DOM read/write xen kẽ gây layout thrashing không.
4. Chọn function trong flame chart → xem **Bottom-up** để tìm self time lớn nhất.
5. Kiểm tra marker LCP, CLS và INP.

### Rendering

Command Menu → `Show rendering`:

- **Paint flashing**: vùng xanh là vùng vừa repaint.
- **Layout shift regions**: vùng gây dịch chuyển bố cục.
- **Frame rendering stats**: FPS và frame time.
- **Emulate CSS media**: thử `prefers-color-scheme`, `prefers-reduced-motion`, `forced-colors`.

### Memory leak

1. Memory → Heap snapshot lần 1.
2. Mở/đóng modal hoặc chuyển route nhiều lần.
3. Force garbage collection.
4. Chụp snapshot lần 2 → chọn **Comparison**.
5. Xem object còn sống và mở **Retainers** để tìm listener, timer hoặc closure giữ reference.

## 9. Phím tắt cần nhớ

| Phím                        | Tác vụ                           |
| --------------------------- | -------------------------------- |
| `F12` / `Ctrl+Shift+I`      | Mở DevTools                      |
| `Ctrl+Shift+C`              | Inspect element                  |
| `Ctrl+Shift+J`              | Mở Console                       |
| `Ctrl+Shift+P`              | Command Menu                     |
| `Ctrl+P`                    | Mở nhanh file                    |
| `Ctrl+Shift+F`              | Tìm trong toàn bộ source đã load |
| `Ctrl+Shift+M`              | Device Mode                      |
| `Ctrl+E`                    | Start/stop Performance recording |
| `Esc`                       | Console drawer                   |
| `F8`                        | Resume                           |
| `F10` / `F11` / `Shift+F11` | Step over / into / out           |
| `Ctrl+R` / `Ctrl+Shift+R`   | Reload / hard reload             |

## 10. Checklist trước khi kết luận lỗi

- [ ] Đã tái hiện được lỗi bằng một chuỗi thao tác cụ thể.
- [ ] Đã kiểm tra đúng frame, đúng environment và đúng bundle hash.
- [ ] Đã kiểm tra DOM hiện tại thay vì chỉ xem HTML nguồn.
- [ ] Đã xem Computed và rule thắng trong CSS cascade.
- [ ] Đã bật breakpoint phù hợp thay vì thêm log ngẫu nhiên.
- [ ] Đã kiểm tra source map và file `.map` của đúng build.
- [ ] Đã loại trừ cache, Service Worker, extension và request cũ.
- [ ] Đã phân biệt lỗi dữ liệu API với lỗi render/state.
- [ ] Đã tái hiện lại sau khi reload sạch trước khi sửa code.

## Tài liệu chính thức và hình ảnh tham khảo

- [Chrome DevTools overview](https://developer.chrome.com/docs/devtools/overview)
- [Debug JavaScript](https://developer.chrome.com/docs/devtools/javascript)
- [Breakpoints reference](https://developer.chrome.com/docs/devtools/javascript/breakpoints)
- [Source maps](https://developer.chrome.com/docs/devtools/javascript/source-maps)
- [CSS features reference](https://developer.chrome.com/docs/devtools/css/reference)
- [Network features reference](https://developer.chrome.com/docs/devtools/network/reference)
- [Webpack `devtool` và source maps](https://webpack.js.org/configuration/devtool/)
- [Webpack production source maps](https://webpack.js.org/guides/production/)
- [Vue CLI `productionSourceMap`](https://cli.vuejs.org/config/#productionsourcemap)
- [Vue Loader](https://vue-loader.vuejs.org/)

Các ảnh trong trang lấy trực tiếp từ tài liệu Chrome for Developers; UI có thể thay đổi nhẹ giữa các phiên bản Chrome.
