調試工具的通用原理:調試四要素

作爲前端開發,調試是每天都會接觸的概念。你覺得什麼是調試呢?

有同學說,我用 Chrome DevTools 調試網頁,可以查看元素,網絡請求,斷點運行 JS,用 Performance 工具分析性能等,這是網頁的調試。

有同學說,我用 VSCode Debugger 調試 Node.js,可以同時調試多個進程的代碼。這是 Node.js 的調試。

有同學說,我用 React DevTools 和 Vue DevTools 的 chrome 插件來調試 React、Vue 組件,還會用獨立的 React DevTools 調試 React Native 應用。這是我常用的調試工具。

沒錯,這些都屬於調試。那它們有什麼共同特點呢?

它們都是把運行的狀態暴露給調試工具,做一些展示和交互。

所以,我們可以給調試下個定義:

代碼在某個平臺運行,把運行時的狀態通過某種方式暴露出來,傳遞給開發工具做 UI 的展示和交互,輔助開發者排查問題、梳理流程、瞭解代碼運行狀態等,這個就是調試。

這裏的某個平臺,可以是瀏覽器、Node.js、Electron、小程序等任何能執行 JS 代碼的平臺。

暴露出的運行時狀態,可能是調用棧、執行上下文,或者 DOM 的結構,React 組件的狀態等。

暴露出這些數據的方式一般是通過基於 WebSocket 的調試協議,當然也會有別的方式。

那常見的調試工具都是怎麼實現的,有沒有什麼通用的原理呢?

我們分別來看一下:

Chrome DevTools 原理

Chrome DevTools 分爲兩部分,backend 和 frontend:

兩者之間的調試協議叫做 Chrome DevTools Protocol,簡稱 CDP。

傳輸協議數據的方式叫做信道(message channel),有很多種,比如 Chrome DevTools 嵌入在 Chrome 裏時,兩者通過全局的函數通信;當 Chrome DevTools 遠程調試某個目標的代碼時,兩者通過 WebSocket 通信。

frontend、backend、調試協議(CDP)、信道,這是 Chrome DevTools 的 4 個組成部分。

backend 可以是 Chromium,也可以是 Node.js 或者 V8,這些 JS 的運行時都支持 Chrome DevTools Protocol。

這就是 Chrome DevTools 的調試原理。

除了 Chrome DevTools 之外,VSCode Debugger 也是常用的調試工具:

VSCode Debugger 原理

VSCode Debugger 的原理和 Chrome DevTools 差不多,也是分爲 frontend、backend、調試協議這幾部分,只不過它多了一層適配器協議。

爲了能直接用 Chrome DevTools 調試 Node.js 代碼,Node.js 6 以上就使用 Chrome DevTools Protocol 作爲調試協議了,所以 VSCode Debugger 要調試 Node.js 也是通過這個協議。

但是中間多了一層適配器協議 Debug Adapter Protocol,這是爲什麼呢?

因爲 VSCode 不是 JS 專用編輯器呀,它可能用來調試 Python 代碼、Rust 代碼等等,自然不能和某一種語言的調試協議深度耦合,所以多了一個適配器層。

這樣 VSCode Debugger 就可以用同一套 UI 和邏輯來調試各種語言的代碼,只要對接不同的 Debug Apapter 做協議轉換即可。

這樣還有另一個好處,就是別的編輯器也可以用這個 Debug Adapter Protocol 來實現調試,這樣就可以直接複用 VSCode 的各種語言的 Debug Adapter 了。

VSCode Debugger 的 UI 的部分算是 frontend,而調試的目標語言算是 backend 部分,中間也是通過 WebSocket 傳遞調試協議。

整體和 Chrome DevTools 的調試原理差不多,只不過爲了支持 frontend 的跨語言複用,多了一層適配器層。

除了 Chrome DevTools 和 VSCode Debugger 外,平時我們開發 Vue 或 React 應用,還會用 Vue DevTools 和 React DevTools:

Vue/React DevTools

Vue DevTools 或者 React DevTools 都是以 Chrome 插件(Chrome Extension)的形式存在的,要搞懂它們的原理就得了解 Chrome 插件的機制。

Chrome 插件中可以訪問網頁的 DOM 的部分叫做 Content Script,隨頁面啓動而生效,可以寫一些操作 DOM 的邏輯。還有一部分是後臺運行的,叫做 Background,瀏覽器啓動就生效了,生命週期比較長,可以做一些常駐的邏輯。

如果是擴展 DevTools 的 Chrome 插件,那還有一部分 DevTools Page,是在 DevTools 裏顯示的頁面:

Content Script 部分可以操作 DOM,可以監聽 DOM Event。

Backgroud 部分可以訪問 extension api,可以和 Content Script 還有 DevTools Page 通信。

DevTools Page 部分可以訪問 devtools api,可以向當前 window 注入 JS 執行。

這就是 Chrome 插件的大概架構。

Vue DevTools 和 React DevTools 就是基於這個架構來實現的調試功能。

你看 Vue DevTools 的源碼目錄會發現,它也是分爲 backend 和 frontend 的

那 backend 運行在哪,frontend 運行在哪,兩者怎麼通信呢?

DevTools Page 是可以在頁面 eval JS 的,那就可以注入 backend 的代碼。

backend 的代碼可以拿到 Vue 組件的信息,通過 window message 的方式傳遞給 Background。

Background 可以和 DevTools Page 通信,從而實現消息轉發。

DevTools Page 根據拿到的數據,渲染組件的信息,實現交互功能。

React DevTools 也是類似的,都是通過 backend 拿到組件信息,然後傳遞給 DevTools Page 做渲染和交互。

不過 React DevTools 還有獨立的 Electron 應用,可以用於 React Native 的調試。

這種自定義調試工具也是用的 Chrome DevTools Protocol 協議麼?

明顯不是,CDP 協議用來調試 DOM、JS 等挺不錯的,但是不好擴展,如果有別的需求,一般都是自定義調試協議。

過了一遍 Chrome DevTools、VSCode Debugger、Vue/React DevTools 的原理,有沒有發現它們有一些相同的地方?

沒錯,都有 backend 部分負責拿到運行時的信息,有 frontend 部分負責渲染和交互,也有調試協議用來規定不同數據的格式,還有不同的信道,比如 WebSocket 、Chrome 插件的 background 轉發等。

frontend、backend、調試協議、信道,這是調試工具的四要素。

不過,不同的調試工具都會有不同的設計,比如 VSCode Debugger 爲了跨語言複用,多了一層 Debugger Adapter,React DevTools 有獨立的 electron 應用,用自定義調試協議,可以調試 React Native 代碼。

總結

我們會用 Chrome DevTools、VSCode Debugger、Vue/React DevTools 等工具來調試網頁、Node.js、React/Vue 的代碼,它們都屬於調試工具。

調試就是通過某種信道(比如 WebSocket)把運行時信息傳遞給開發工具,做 UI 的展示和交互,輔助開發者排查問題、瞭解代碼運行狀態等。

我們簡單過了一遍這些調試工具的原理:

它們有通用的部分,都有 frontend、backend、調試協議、信道這四要素。

也有不同的部分,比如 VSCode Debugger 多了一層 Debugger Adapter,用於跨語言的複用,Vue/React DevTools 通過向頁面注入 backend 代碼,然後通過 Background 實現雙向通信等。

抓住它們相同的部分來分析,理解不同的部分的設計原因,就很容易搞懂各種調試工具的原理了。

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/Lztd4q7IzWKZgZmtBPLMzQ