什么是调试?
代码在某个平台运行,把运行时的状态通过某种方式暴露出来,传递给开发工具做 UI 的展示和交互,辅助开发者排查问题、梳理流程、了解代码运行状态等,这个就是调试。
这里的某个平台,可以是浏览器、Node.js、Electron、小程序等任何能执行 JS 代码的平台。
暴露出的运行时状态,可能是调用栈、执行上下文,或者 DOM 的结构,React 组件的状态等。
暴露出这些数据的方式一般是通过基于 WebSocket 的调试协议,当然也会有别的方式。
调试网页,我们一般用用 Chrome DevTools,可以查看元素,网络请求,断点运行 JS,用 Performance 工具分析性能等。
Chrome DevTools 分为两部分,backend 和 frontend:
- backend 和 Chrome 集成,负责把 Chrome 的网页运行时状态通过调试协议暴露出来。
- frontend 是独立的,负责对接调试协议,做 UI 的展示和交互。
两者之间的调试协议叫做 Chrome DevTools Protocol,简称 CDP。
传输协议数据的方式叫做信道(message channel),有很多种,比如 Chrome DevTools 嵌入在 Chrome 里时,两者通过全局的函数通信;当 Chrome DevTools 远程调试某个目标的代码时,两者通过 WebSocket 通信。
frontend、backend、调试协议(CDP)、信道,这是 Chrome DevTools 的 4 个组成部分。
react 调试
我们以 React 项目为例,用 vite 创建一个 react 项目:
进入项目目录,执行 npm run dev, 启动项目,打开 Chrome DevTools,在 Sources 面板找到 src/main.tsx,打上个断点:
然后刷新就可以开始调试了。
其实调试网页的 JS,除了 Chrome DevTools 外,还有一种更好用的调试方式: VSCode Debugger。
用 VSCode 打开项目目录,创建 .vscode/launch.json 文件,点击右下角的 Add Configuration... 按钮,选择 Chrome: Launch。
{
"version": "0.2.0",
"configurations": [
{
"type": "chrome",
"request": "launch",
"name": "Launch Chrome against localhost",
"url": "http://localhost:5173",
"webRoot": "${workspaceFolder}"
}
]
}
- 确保 Vite 开发服务已启动(
npm run dev不关闭),把launch.json的 url 改为开发服务器启动的地址:http://localhost:5173。
launch 它仅表示 “启动调试器并打开指定的浏览器 / 应用”,但不会自动启动你的前端开发服务器(Vite、Webpack 等)。调试器(vscode debugger)只是 “连接” 到已运行的浏览器,而非 “启动” 前端服务器。
所以,我们还需要执行npm run dev来启动服务。当启动调试的时候,它会另外打开一个chrome浏览器窗口用于调试。
-
在 VS Code 中打开 React 组件文件(如
src/App.jsx),点击代码行号左侧添加断点(红色圆点)。 -
点击调试面板的启动调试按钮(绿色三角),VS Code 会自动打开 Chrome 并访问
http://localhost:5173。
这样就可以使用vscode进行调试了。
不过,有同学可能会问,这个浏览器好像没有 React DevTools 啊。
确实,因为这跑的是一个新的浏览器实例,没有之前的那些用户数据。
用户数据是保存在 userDataDir 里的,一个 userDataDir 对应一个浏览器实例。
不信我们试试看:
我指定一个 userDataDir,然后点击调试启动。
在启动的浏览器里把掘金收藏为书签:
然后进入刚才那个 userDataDir,进入 defaults 目录,看一下 Bookmarks 文件的内容:
就有刚才保存的书签了。
同理,各种 chrome 插件、浏览记录、cookies 等等,所有用户数据都是保存在 userDataDir 里。
chrome 一个 userDataDir 只能跑一个实例。
我们调试的时候,如果没有指定 userDataDir,默认是临时创建一个新的 userDataDir。
所以这时候自然就没有 React DevTools 等你之前安装的插件了。
如果想调试的时候还用这些插件,那可以把 userDataDir 设置为 false,就是这样就是用默认的 userDatDir 来跑:
"userDataDir": false
这时候需要你把之前跑的 chrome 关掉才能跑,因为一个 userDataDir 只能跑一个实例。否则会报错:
之后再点击调试,这次跑的浏览器就有你之前装的 React DevTools 了。
结合 click-to-react-component 打造超级爽的 React 调试体验
如果说业务开发中最重要的能力,那定位代码的能力肯定是其中之一。
业务项目一般代码都很多,你拿到一个需求之后,可能改起来不难,但是要定位在哪里改比较难。
特别是接手别人写的代码的时候。
大家都是怎么在不熟悉的项目里定位的代码呢?
很多人都是搜文案,搜 className。
这样没问题,但如果你用了 styled-component 之类的方案之后,className 都是动态生成的。
而且不少项目都做了国际化,你搜文案会搜到资源包里,而不是组件代码里。
那有什么好的办法可以快速定位代码么?
有,就是 click-to-react-component。
我们创建个项目后,引入:
npm install --save-dev click-to-react-component
在 main.tsx 引入下:
import { ClickToComponent } from 'click-to-react-component'
createRoot(document.getElementById('root')!).render(
// <StrictMode>
<Provider store={store}>
{import.meta.env.DEV && <ClickToComponent />}
<App />
</Provider>
// </StrictMode>
)
可以看到,现在按住 alt + 单击,就会直接打开它的对应的组件的源码。
如果按住 option + 右键单击,可以看到它的所有父级组件,然后选择一个组件打开:
这样在页面上看到了啥东西就可以直接打开它的组件代码来改,特别高效。