個案研究:使用開發人員工具改善 Angular 偵錯服務

改善偵錯體驗

過去幾個月來,Chrome 開發人員工具團隊與 Angular 團隊合作,推出改善 Chrome 開發人員工具的偵錯體驗。這兩個團隊的成員都攜手合作,讓開發人員從編寫的角度對網頁應用程式偵錯及剖析:以來源語言和專案結構來說,使用者可以存取他們熟悉且感興趣的資訊。

這篇文章會詳細介紹 Angular 和 Chrome 開發人員工具中的哪些變更才能完成。雖然其中部分變更可以透過 Angular 呈現,但也可以套用至其他架構。Chrome 開發人員工具團隊鼓勵其他架構採用新版控制台 API 和來源對應擴充功能點,為使用者提供更優質的偵錯體驗。

忽略清單程式碼

使用 Chrome 開發人員工具對應用程式進行偵錯時,作者通常只想查看「其程式碼」,而不是查看下方架構的架構,或部分依附元件未出現在 node_modules 資料夾中。

為此,開發人員工具團隊推出來源對應的擴充功能 (名為 x_google_ignoreList)。這個擴充功能可用來識別第三方來源,例如架構程式碼或套裝組合產生的程式碼。架構使用這項擴充功能時,作者現在會自動避免不想查看或逐步執行的程式碼,不必事先手動設定

在實際操作時,Chrome 開發人員工具會自動隱藏堆疊追蹤、「來源」樹狀結構和「快速開啟」對話方塊中辨識的程式碼,同時改善偵錯工具的步行和繼續行為。

顯示開發人員工具前後對照的 GIF 動畫。請注意,在圖片開發人員工具中,開發人員工具會在樹狀結構中顯示經編寫的程式碼,不再建議「快速開啟」選單中的任何架構檔案,並在右側顯示更簡潔的堆疊追蹤。

x_google_ignoreList 來源對應擴充功能

在來源對應中,新的 x_google_ignoreList 欄位會參照 sources 陣列,並會列出該來源對應中所有已知第三方來源的索引。剖析來源對應時,Chrome 開發人員工具會據此判斷應忽略程式碼的哪些區段

以下是產生的檔案 out.js 的來源對應。產生輸出檔案時使用的兩個原始 sourcesfoo.jslib.js。前者是網站程式開發人員編寫的內容,後者則是他們所使用的架構。

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

以下原始來源均包含 sourcesContent,Chrome 開發人員工具則預設會在 Debugger 中顯示這些檔案:

  • 「來源」樹狀結構中的檔案。
  • 系統會顯示「快速開啟」對話方塊。
  • 在中斷點和踏步時暫停時,錯誤堆疊追蹤中對應的呼叫框架位置。

現在您還可以在來源對應中加入一項額外資訊,以找出這些來源是第一個或第三方程式碼:

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

新的 x_google_ignoreList 欄位包含一個參照 sources 陣列的索引:1。這會指定對應至 lib.js 的區域確實是第三方程式碼,應自動新增至忽略清單。

如下方範例所示,索引 2、4 和 5 指定對應至 lib1.tslib2.coffeehmr.js 的區域,都是應自動新增至忽略清單的第三方程式碼。

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

如果你是架構或套裝組合開發人員,請務必在建構過程中產生的來源對應包含這個欄位,以使用 Chrome 開發人員工具中的這些新功能。

Angular 的 x_google_ignoreList

Angular v14.1.0 起,node_moduleswebpack 資料夾的內容已標示為「可忽略」

方法是建立可掛鉤至 webpack Compiler 模組的外掛程式,藉此達成 angular-cli 的變動

我們的工程師建立的 webpack 外掛程式是建立掛鉤至 PROCESS_ASSETS_STAGE_DEV_TOOLING 階段,並在 Webpack 產生以及瀏覽器載入的最終資產來源對應中,填入 x_google_ignoreList 欄位。

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)));

連結的堆疊追蹤

堆疊追蹤可解答「我怎麼到這裡」的問題,但這通常是從機器的角度出發,不一定符合開發人員對應用程式執行階段的想法或心態模型。當某些作業安排稍後以非同步的方式進行時,這一點尤其重要。雖然知道這些作業的「根本原因」或排程端還是很有意思,但這項功能並不會包含在非同步堆疊追蹤中。

使用標準瀏覽器排程基本功能時,V8 內部有一套機制可以追蹤這類非同步工作,例如 setTimeout。系統預設會完成這些操作,因此開發人員可以自行檢查!但在更複雜的專案中,這並不是那麼簡單,尤其在使用具備進階排程機制的架構時更是如此,例如用於執行區域追蹤、自訂工作佇列,或將更新分割成數個隨著時間執行的多個工作單元。

為解決這個問題,開發人員工具會在 console 物件上公開名為「Async Stack Tagging API」的機制,讓架構開發人員提示作業的排定位置和執行作業的位置。

非同步堆疊標記 API

如未設定非同步堆疊標記,針對依架構以複雜方式非同步執行的程式碼堆疊追蹤,將無法顯示與排定程式碼的程式碼連結。

某些非同步已執行程式碼的堆疊追蹤,但不含排定時間的資訊。這個頁面只會顯示從「requestAnimationFrame」開始的堆疊追蹤,但不會顯示排定時間的任何資訊。

使用非同步堆疊標記即可提供背景資訊,堆疊追蹤如下所示:

某些非同步已執行程式碼的堆疊追蹤,內含排程時間的相關資訊。請留意,這與先前不同,堆疊追蹤中會包含 `businessLogic` 和 `schedule`。

為此,請使用 Async Stack Tagging API 提供且名為 console.createTask() 的新 console 方法。其簽名如下所示:

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

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

叫用 console.createTask() 會傳回 Task 例項,您稍後可以用來執行非同步程式碼。

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

// Task Execution
task.run(f);

此外,非同步作業也可以建立巢狀結構,而「根本原因」會依序顯示在堆疊追蹤中。

工作可執行無數次,每次執行的工作酬載可能有所不同。系統會記住排程網站的呼叫堆疊,直到收集工作物件為垃圾收集為止。

Angular 中的 Async Stack Tagging API

在 Angular 中,對 NgZone 所做的變更經過變更 – Angular 的執行環境,會在非同步工作中保留。

排程工作時,工作會使用 console.createTask() (如果可用)。系統會儲存產生的 Task 例項,以供日後使用。叫用工作後,NgZone 會使用已儲存的 Task 執行個體執行該工作。

這些變更透過 #46693#46958 提取要求,導入 Angular 的 NgZone 0.11.8。

友善的來電框

在建立專案時,架構通常會根據各種範本語言產生程式碼,例如 Angular 或 JSX 範本,這類範本會將 HTML 樣式程式碼轉成純文字,最後在瀏覽器中執行。有時候,這類產生的函式會有名稱不易使用的名稱,例如經過壓縮後輸入單一字母名稱,或是模糊不清或陌生的名稱,即使名稱並非如此。

在 Angular 中,經常會在堆疊追蹤中看到名稱為 AppComponent_Template_app_button_handleClick_1_listener 的呼叫影格。

包含自動產生的函式名稱的堆疊追蹤螢幕截圖。

為解決這個問題,Chrome 開發人員工具現已支援透過來源對應重新命名這些函式。如果來源對應有函式範圍開始的名稱項目 (也就是參數清單的左括號),呼叫頁框應在堆疊追蹤中顯示該名稱。

Angular 的友善呼叫框

將 Angular 中的呼叫框重新命名需要持續進行。我們預期這些改善措施會逐漸推行。

剖析作者編寫的 HTML 範本時,Angular 編譯器會產生 TypeScript 程式碼,最終會轉譯為瀏覽器載入並執行的 JavaScript 程式碼。

在這個程式碼產生過程中,系統也會建立來源對應。我們正在研究如何把函式名稱加入來源對應的「名稱」欄位,並在產生的程式碼和原始程式碼之間的對應中參照這些名稱。

舉例來說,如果事件接聽程式已產生一個函式,且其名稱在壓縮期間不易辨識或移除,現在來源對應可以在「names」欄位中加入這個函式更易記的名稱,而函式範圍開頭的對應現在可參照這個名稱 (也就是參數清單的左括號)。Chrome 開發人員工具會使用這些名稱來重新命名堆疊追蹤中的呼叫頁框。

展望未來

使用 Angular 做為測試前測計畫,確認我們所做的成果很滿意。我們很樂意聆聽架構開發人員的想法,並針對這些延長使用期限提供意見

還有更多值得我們探索的地方。特別是如何改善開發人員工具中的剖析體驗。