当前位置: 首页 > 科技观察

调试工具的一般原则:调试四要素

时间:2023-03-23 10:32:53 科技观察

作为前端开发,调试是每天都会遇到的一个概念。你认为调试是什么?有同学说我用ChromeDevTools调试网页。我可以查看元素,网络请求,断点运行JS,用Performance工具分析性能等等,这就是网页调试。有同学说我用VSCodeDebugger调试Node.js,可以同时调试多个进程的代码。这是Node.js调试。有同学说我用ReactDevTools和VueDevTools的chrome插件调试React和Vue组件,也用独立的ReactDevTools调试ReactNative应用。这是我的首选调试工具。是的,这些都是调试。那么他们有什么共同点呢?它们都是把运行状态暴露给调试工具,做一些展示和交互。因此,我们可以给调试下一个定义:代码运行在一定的平台上,以一定的方式暴露运行时状态,传递给开发工具进行UI显示和交互,协助开发者排查问题,梳理流程,以及了解代码运行状态等,这就是调试。这里的某个平台可以是任何可以执行JS代码的平台,比如浏览器、Node.js、Electron、小程序等。暴露的运行时状态可能是调用栈,执行上下文,也可能是DOM的结构,React组件的状态等。暴露这些数据的方式一般是通过基于WebSocket的调试协议,当然也有其他方式。常见的调试工具是如何实现的?有什么一般原则吗?我们分开来看:ChromeDevTools原理ChromeDevTools分为后端和前端两部分:后端和Chrome集成,负责通过调试协议暴露Chrome的网页运行状态。frontend是独立的,负责对接和调试协议,做UI展示和交互。两者之间的调试协议称为ChromeDevToolsProtocol,简称CDP。传输协议数据的方式称为通道(messagechannel),有很多种。例如,当ChromeDevTools嵌入到Chrome中时,两者通过全局函数进行通信;当ChromeDevTools远程调试目标代码时,两者通过WebSocket进行通信。前端、后端、调试协议(CDP)、通道,这是ChromeDevTools的4个组件。后端可以是Chromium、Node.js或V8,这些JS的运行时都支持ChromeDevToolsProtocol。这就是使用ChromeDevTools进行调试的方式。除了ChromeDevTools,VSCodeDebugger也是一个常用的调试工具:VSCodeDebugger原理VSCodeDebugger的原理和ChromeDevTools类似。它也分为前端、后端和调试协议,但它多了一层适配器协议。为了直接使用ChromeDevTools调试Node.js代码,Node.js6及以上版本使用ChromeDevToolsProtocol作为调试协议,所以VSCodeDebugger也使用该协议来调试Node.js。但是中间多了一层适配器协议DebugAdapterProtocol,这是为什么呢?因为VSCode不是JS专用的编辑器,可能用来调试Python代码、Rust代码等,自然不能和某种语言的调试协议深度耦合,所以加了一个适配层。这样,VSCodeDebugger只要连接不同的DebugAdapter进行协议转换,就可以使用同一套UI和逻辑来调试各种语言的代码。这样还有一个好处,就是其他编辑器也可以使用这个DebugAdapterProtocol来实现调试,这样就可以直接复用VSCode各种语言的DebugAdapter。VSCodeDebugger的UI部分作为前端,调试的目标语言作为后端部分,中间通过WebSocket传递调试协议。整体调试原理与ChromeDevTools类似,只是增加了一个适配层来支持前端的跨语言复用。除了ChromeDevTools和VSCodeDebugger,我们在开发Vue或者React应用的时候,也会用到VueDevTools和ReactDevTools:原理还得了解Chrome插件的机制。Chrome插件中可以访问网页DOM的部分称为ContentScript,它在页面启动时生效,可以编写一些操作DOM的逻辑。还有一个在后台运行的部分,叫做Background,在浏览器启动时生效,生命周期比较长,可以做一些常驻逻辑。如果是扩展DevTools的Chrome扩展,还有一部分DevToolsPage,也就是DevTools中显示的页面:ContentScript部分,可以操作DOM,监听DOMEvents。Backgroud部分可以访问扩展api,可以与ContentScript和DevToolsPage进行通信。DevToolsPage部分可以访问devtoolsapi,可以将JS执行注入当前窗口。这是Chrome扩展的大致架构。VueDevTools和ReactDevTools都是基于这个架构来实现调试功能的。如果你查看VueDevTools的源码目录,你会发现它也分为后端和前端。后端在哪里运行,前端在哪里运行,两者如何通信?DevToolsPage可以在页面上对JS进行eval,这样就可以注入后端代码了。后端代码可以获取到Vue组件的信息,通过窗口消息传递给BackGround。BackGround可以和DevToolsPage通信转发消息。DevToolsPage根据获取到的数据渲染组件信息,实现交互功能。ReactDevTools也类似,都是通过后端获取组件信息,然后传递给DevToolsPage进行渲染和交互。然而,ReactDevTools也有一个独立的Electron应用程序,可用于ReactNative调试。这个自定义调试工具是否也使用ChromeDevTools协议?显然不是,CDP协议对调试DOM、JS等有好处,但不好扩展。如果还有其他需求,一般是自定义调试协议。看完了ChromeDevTools、VSCodeDebugger和Vue/ReactDevTools的原理,你有没有发现它们有一些相似之处?没错,有一个backend部分负责获取运行时信息,一个frontend部分负责渲染和交互,还有一个调试协议用来指定不同的数据格式和不同的通道,比如WebSocket,Chrome插件的后台转发等前端、后端、调试协议、通道,这就是调试工具的四大要素。但是,不同的调试工具有不同的设计。比如VSCodeDebugger多了一层DebuggerAdapter用于跨语言复用,ReactDevTools有独立的electron应用。使用自定义调试协议,可以调试ReactNative代码。综上所述,我们会使用ChromeDevTools、VSCodeDebugger、Vue/ReactDevTools等工具调试网页,Node.js、React/Vue代码,这些都是调试工具。调试就是将运行时信息通过某种通道(如WebSocket)传递给开发工具,进行UI的展示和交互,协助开发者排查问题,了解代码的运行状态等。我们简单介绍了这些调试的原理工具:它们有共同的部分,它们都有四个要素:前端、后端、调试协议和通道。也有不同的部分。例如,VSCodeDebugger多了一层DebuggerAdapter用于跨语言复用。Vue/ReactDevTools在页面中注入后端代码,然后通过后台实现双向通信。抓住各种调试工具的相同部分,分析理解不同部分的设计原因,很容易理解各种调试工具的原理。