Nghiên cứu điển hình: Gỡ lỗi góc hiệu quả hơn bằng Công cụ cho nhà phát triển

Cải thiện trải nghiệm gỡ lỗi

Trong vài tháng qua, nhóm Công cụ của Chrome cho nhà phát triển đã cộng tác với nhóm Angular để cải thiện trải nghiệm gỡ lỗi trong Công cụ của Chrome cho nhà phát triển. Các thành viên của cả hai nhóm đã cùng nhau làm việc và thực hiện các bước để cho phép nhà phát triển gỡ lỗi và lập hồ sơ ứng dụng web từ góc độ tác giả: về ngôn ngữ nguồn và cấu trúc dự án, với quyền truy cập vào thông tin quen thuộc và phù hợp với họ.

Bài đăng này sẽ xem xét những thay đổi cần thiết trong Angular và Công cụ cho nhà phát triển Chrome để đạt được điều này. Mặc dù một số thay đổi này được minh hoạ thông qua Angular, nhưng bạn cũng có thể áp dụng các thay đổi đó cho các khung khác. Nhóm Công cụ phát triển Chrome khuyến khích các khung khác sử dụng API bảng điều khiển mới và các điểm mở rộng bản đồ nguồn để họ cũng có thể mang lại trải nghiệm gỡ lỗi tốt hơn cho người dùng.

Mã danh sách bỏ qua

Khi gỡ lỗi ứng dụng bằng Công cụ của Chrome cho nhà phát triển, tác giả thường chỉ muốn xem mã của họ, chứ không phải mã của khung bên dưới hoặc một số phần phụ thuộc nằm trong thư mục node_modules.

Để đạt được điều này, nhóm Công cụ cho nhà phát triển đã giới thiệu một tiện ích cho bản đồ nguồn, có tên là x_google_ignoreList. Tiện ích này dùng để xác định các nguồn của bên thứ ba, chẳng hạn như mã khung hoặc mã do trình tạo gói tạo. Khi một khung sử dụng tiện ích này, tác giả hiện sẽ tự động tránh mã mà họ không muốn xem hoặc bước qua mà không cần phải định cấu hình trước theo cách thủ công.

Trong thực tế, Chrome DevTools có thể tự động ẩn mã được xác định là như vậy trong dấu vết ngăn xếp, cây Nguồn, hộp thoại Mở nhanh, đồng thời cải thiện hành vi bước và tiếp tục trong trình gỡ lỗi.

Ảnh GIF động cho thấy DevTools trước và sau. Lưu ý cách trong hình ảnh sau, DevTools hiển thị Mã do tác giả tạo trong cây, không còn đề xuất bất kỳ tệp khung nào trong trình đơn "Mở nhanh" và hiển thị dấu vết ngăn xếp rõ ràng hơn nhiều ở bên phải.

Tiện ích bản đồ nguồn x_google_ignoreList

Trong bản đồ nguồn, trường x_google_ignoreList mới tham chiếu đến mảng sources và liệt kê các chỉ mục của tất cả các nguồn bên thứ ba đã biết trong bản đồ nguồn đó. Khi phân tích cú pháp bản đồ nguồn, Chrome DevTools sẽ sử dụng thông tin này để xác định những phần mã cần được đưa vào danh sách bỏ qua.

Dưới đây là bản đồ nguồn cho tệp out.js được tạo. Có hai sources ban đầu đã góp phần tạo ra tệp đầu ra: foo.jslib.js. Mã nguồn đầu tiên là mã nguồn do nhà phát triển trang web viết và mã nguồn thứ hai là khung mà họ sử dụng.

{
  "version" : 3,
  "file": "out.js",
  "sourceRoot": "",
  "sources": ["foo.js", "lib.js"],
  "sourcesContent": ["...", "..."],
  "names": ["src", "maps", "are", "fun"],
  "mappings": "A,AAAB;;ABCDE;"
}

sourcesContent được đưa vào cả hai nguồn gốc này và theo mặc định, Công cụ của Chrome cho nhà phát triển sẽ hiển thị các tệp này trên Trình gỡ lỗi:

  • Dưới dạng tệp trong cây Nguồn.
  • Dưới dạng kết quả trong hộp thoại Mở nhanh.
  • Là vị trí khung lệnh gọi được liên kết trong dấu vết ngăn xếp lỗi khi tạm dừng tại một điểm ngắt và khi thực hiện từng bước.

Giờ đây, bạn có thể thêm một thông tin bổ sung vào bản đồ nguồn để xác định nguồn nào là mã gốc hay mã của bên thứ ba:

{
  ...
  "sources": ["foo.js", "lib.js"],
  "x_google_ignoreList": [1],
  ...
}

Trường x_google_ignoreList mới chứa một chỉ mục duy nhất tham chiếu đến mảng sources: 1. Điều này chỉ định rằng các vùng được liên kết với lib.js thực sự là mã của bên thứ ba sẽ tự động được thêm vào danh sách bỏ qua.

Trong ví dụ phức tạp hơn, như minh hoạ bên dưới, các chỉ mục 2, 4 và 5 chỉ định rằng các vùng được liên kết với lib1.ts, lib2.coffeehmr.js đều là mã của bên thứ ba sẽ tự động được thêm vào danh sách bỏ qua.

{
  ...
  "sources": ["foo.html", "bar.css", "lib1.ts", "baz.js", "lib2.coffee", "hmr.js"],
  "x_google_ignoreList": [2, 4, 5],
  ...
}

Nếu bạn là nhà phát triển khung hoặc trình tạo gói, hãy đảm bảo rằng sơ đồ nguồn được tạo trong quá trình xây dựng có trường này để kết nối với các tính năng mới này trong Công cụ dành cho nhà phát triển Chrome.

x_google_ignoreList trong Angular

Kể từ Angular phiên bản 14.1.0, nội dung của thư mục node_moduleswebpack đã được đánh dấu là "bỏ qua".

Điều này đã đạt được thông qua một thay đổi trong angular-cli bằng cách tạo một trình bổ trợ liên kết với mô-đun Compiler của webpack

Trình bổ trợ webpack mà các kỹ sư của chúng tôi đã tạo các trình nối vào giai đoạn PROCESS_ASSETS_STAGE_DEV_TOOLING và điền trường x_google_ignoreList trong bản đồ nguồn cho các thành phần cuối cùng mà webpack tạo ra và trình duyệt tải.

const map = JSON.parse(mapContent) as SourceMap;
const ignoreList = [];

for (const [index, path] of map.sources.entries()) {
  if (path.includes('/node_modules/') || path.startsWith('webpack/')) {
    ignoreList.push(index);
  }
}

map[`x_google_ignoreList`] = ignoreList;
compilation.updateAsset(name, new RawSource(JSON.stringify(map)));

Dấu vết ngăn xếp được liên kết

Dấu vết ngăn xếp trả lời câu hỏi “làm cách nào để tôi đến đây”, nhưng thường thì đây là từ quan điểm của máy và không nhất thiết phải khớp với quan điểm của nhà phát triển hoặc mô hình tinh thần của họ về thời gian chạy ứng dụng. Điều này đặc biệt đúng khi một số thao tác được lên lịch để diễn ra không đồng bộ sau này: bạn vẫn có thể muốn biết "nguyên nhân gốc rễ" hoặc khía cạnh lên lịch của các thao tác như vậy, nhưng đó chính xác là điều sẽ không có trong dấu vết ngăn xếp không đồng bộ.

V8 có một cơ chế nội bộ để theo dõi các tác vụ không đồng bộ như vậy khi sử dụng các nguyên hàm lập lịch trình trình duyệt chuẩn, chẳng hạn như setTimeout. Việc này được thực hiện theo mặc định trong những trường hợp đó, vì vậy, nhà phát triển có thể kiểm tra các lỗi này! Nhưng trong các dự án phức tạp hơn, việc này không đơn giản như vậy, đặc biệt là khi sử dụng một khung có cơ chế lên lịch nâng cao hơn, chẳng hạn như một khung thực hiện tính năng theo dõi vùng, tạo hàng đợi tác vụ tuỳ chỉnh hoặc chia các bản cập nhật thành một số đơn vị công việc được chạy theo thời gian.

Để giải quyết vấn đề này, DevTools hiển thị một cơ chế có tên là "Async Stack Tagging API" (API gắn thẻ ngăn xếp không đồng bộ) trên đối tượng console, cho phép các nhà phát triển khung gợi ý cả vị trí thực hiện các thao tác cũng như vị trí thực thi các thao tác này.

API gắn thẻ ngăn xếp không đồng bộ

Nếu không có tính năng Gắn thẻ ngăn xếp không đồng bộ, dấu vết ngăn xếp cho mã được thực thi không đồng bộ theo cách phức tạp bởi các khung sẽ xuất hiện mà không có mối liên kết nào với mã đã lên lịch.

Dấu vết ngăn xếp của một số mã được thực thi không đồng bộ mà không có thông tin về thời điểm lên lịch. Trình theo dõi ngăn xếp này chỉ hiển thị từ `requestAnimationFrame` nhưng không chứa thông tin về thời điểm được lên lịch.

Với tính năng Gắn thẻ ngăn xếp không đồng bộ, bạn có thể cung cấp ngữ cảnh này và dấu vết ngăn xếp sẽ có dạng như sau:

Dấu vết ngăn xếp của một số mã được thực thi không đồng bộ cùng với thông tin về thời điểm lên lịch. Lưu ý rằng không giống như trước đây, hàm này bao gồm "businessLogic" và "schedule" trong dấu vết ngăn xếp.

Để thực hiện việc này, hãy sử dụng phương thức console mới có tên là console.createTask() do API gắn thẻ ngăn xếp không đồng bộ cung cấp. Chữ ký của hàm này như sau:

interface Console {
  createTask(name: string): Task;
}

interface Task {
  run<T>(f: () => T): T;
}

Việc gọi console.createTask() sẽ trả về một thực thể Task mà sau này bạn có thể dùng để chạy mã không đồng bộ.

// Task Creation
const task = console.createTask(name);

// Task Execution
task.run(f);

Các thao tác không đồng bộ cũng có thể được lồng và "nguyên nhân gốc rễ" sẽ hiển thị trong dấu vết ngăn xếp theo trình tự.

Bạn có thể chạy tác vụ bất kỳ số lần nào và tải trọng công việc có thể khác nhau giữa mỗi lần chạy. Ngăn xếp lệnh gọi tại trang web lên lịch sẽ được ghi nhớ cho đến khi đối tượng tác vụ được thu gom rác.

API gắn thẻ ngăn xếp không đồng bộ trong Angular

Trong Angular, chúng tôi đã thực hiện các thay đổi đối với NgZone – ngữ cảnh thực thi của Angular vẫn tồn tại trên các tác vụ không đồng bộ.

Khi lên lịch một tác vụ, ứng dụng sẽ sử dụng console.createTask() nếu có. Thực thể Task thu được sẽ được lưu trữ để sử dụng sau này. Khi gọi tác vụ, NgZone sẽ sử dụng thực thể Task đã lưu trữ để chạy tác vụ đó.

Những thay đổi này đã được đưa vào NgZone 0.11.8 của Angular thông qua các yêu cầu kéo #46693#46958.

Khung lệnh gọi thân thiện

Khung thường tạo mã từ mọi loại ngôn ngữ tạo mẫu khi xây dựng dự án, chẳng hạn như mẫu Angular hoặc JSX chuyển mã có giao diện HTML thành JavaScript thuần tuý và cuối cùng chạy trong trình duyệt. Đôi khi, các loại hàm được tạo này được đặt tên không thân thiện — tên chữ cái đơn sau khi được rút gọn hoặc một số tên khó hiểu hoặc không quen thuộc ngay cả khi không phải vậy.

Trong Angular, bạn thường thấy các khung lệnh gọi có tên như AppComponent_Template_app_button_handleClick_1_listener trong dấu vết ngăn xếp.

Ảnh chụp màn hình của dấu vết ngăn xếp có tên hàm được tạo tự động.

Để giải quyết vấn đề này, Chrome DevTools hiện hỗ trợ việc đổi tên các hàm này thông qua bản đồ nguồn. Nếu bản đồ nguồn có mục nhập tên cho phần bắt đầu của phạm vi hàm (tức là dấu ngoặc đơn bên trái của danh sách tham số), thì khung lệnh gọi sẽ hiển thị tên đó trong dấu vết ngăn xếp.

Khung lệnh gọi thân thiện trong Angular

Việc đổi tên khung lệnh gọi trong Angular là một nỗ lực liên tục. Chúng tôi dự kiến những điểm cải tiến này sẽ được triển khai dần theo thời gian.

Trong khi phân tích cú pháp các mẫu HTML mà tác giả đã viết, trình biên dịch Angular sẽ tạo mã TypeScript. Cuối cùng, mã này sẽ được chuyển đổi sang mã JavaScript mà trình duyệt tải và chạy.

Trong quá trình tạo mã này, bản đồ nguồn cũng được tạo. Chúng tôi hiện đang tìm hiểu các cách đưa tên hàm vào trường "tên" của bản đồ nguồn và tham chiếu các tên đó trong mối liên kết giữa mã được tạo và mã gốc.

Ví dụ: nếu một hàm cho trình nghe sự kiện được tạo và tên của hàm đó không thân thiện hoặc bị xoá trong quá trình rút gọn, thì bản đồ nguồn hiện có thể bao gồm tên thân thiện hơn cho hàm này trong trường "tên" và mối liên kết cho phần đầu của phạm vi hàm hiện có thể tham chiếu đến tên này (tức là dấu ngoặc đơn bên trái của danh sách tham số). Sau đó, Chrome DevTools sẽ sử dụng các tên này để đổi tên khung lệnh gọi trong dấu vết ngăn xếp.

Hướng đến tương lai

Việc sử dụng Angular làm chương trình thí điểm kiểm thử để xác minh công việc của chúng tôi là một trải nghiệm tuyệt vời. Chúng tôi rất mong nhận được ý kiến của các nhà phát triển khung và ý kiến phản hồi của bạn về các điểm mở rộng này.

Chúng tôi muốn khám phá thêm nhiều lĩnh vực khác. Cụ thể là cách cải thiện trải nghiệm phân tích tài nguyên trong Công cụ cho nhà phát triển.