前端三种调试工具,3种常用VsCode断点调试方法,核心调试方法 (debugger 调试)调试:指的是代码在某个平台运
调试:指的是代码在某个平台运行,把运行时的状态通过某种方式暴露出来,传递给开发工具做 UI 的展示和交互,辅助开发者排查问题、梳理流程、了解代码运行状态等。
frontend(前端)、backend(后端)、调试协议、信道,这是调试工具的四要素。
三种调试工具原理
Chrome DevTools 调试
用 Chrome DevTools 调试网页,可以查看元素,网络请求,断点运行 JS,用 Performance 工具分析性能等
Chrome DevTools 分为两部分,backend(后端) 和 frontend(前端):
- backend 和 Chrome 集成,负责把 Chrome 的网页运行时状态通过调试协议暴露出来。
- frontend 是独立的,负责对接调试协议,做 UI 的展示和交互。
两者之间的调试协议叫做 Chrome DevTools Protocol,简称 CDP。
传输协议数据的方式叫做信道(message channel)
Chrome DevTools 由(frontend、backend、调试协议(CDP)、信道
) 4 个组成部分
图中的,backend 可以是 Chromium,也可以是 Node.js 或者 V8,这些 JS 的运行时都支持 Chrome DevTools Protocol。
VSCode Debugger 原理
VSCode Debugger 的原理和 Chrome DevTools 差不多,也是分为 frontend、backend、调试协议这几部分,只不过它多了一层适配器协议。
中间多了一层适配器协议 Debug Adapter Protocol,这是为什么呢?
因为 VSCode 不是 JS 专用编辑器呀,它可能用来调试 Python 代码、Node 代码等等,自然不能和某一种语言的调试协议深度耦合,所以多了一个适配器层。
VSCode Debugger 的 UI 的部分算是 frontend,而调试的目标语言算是 backend 部分,中间也是通过 WebSocket 传递调试协议。
Vue/React DevTools
Vue DevTools 或者 React DevTools 都是以 Chrome 插件(Chrome Extension)的形式存在的
Vue DevTools 也是分为 backend 和 frontend 的
Chrome 插件中可以访问网页的 DOM 的部分叫做 Content Script,随页面启动而生效,可以写一些操作 DOM 的逻辑。还有一部分是后台运行的,叫做 Background,浏览器启动就生效了,生命周期比较长,可以做一些常驻的逻辑。
如果是扩展 DevTools 的 Chrome 插件,那还有一部分 DevTools Page,是在 DevTools 里显示的页面
DevTools Page 部分渲染出的界面是这样的:
VsCode断点调试方法
异常断点
代码抛了异常,你想知道在哪抛的,这时候就可以用异常断点。
比如下面一段代码:
function add(a, b) {
throw Error('add');
return a + b;
}
console.log(add(1, 2));
add 函数里抛了个异常,你想在异常处断住,这时候就可以加个异常断点:
勾选异常断点选项
可以在没有被处理的错误或者 Promise 的 reject 处断住。
上面那个 Caught Exception 是在被 catch 处理的异常出断住。
对应中文调试界面
运行过程和结果:
条件断点
有的时候我们只想在满足一定条件的时候才断住,这时候就可以用条件断点
在代码左边打断点的地方右键单击,就可以选择条件断点:
添加一个表达式,比如我只想在 a 等于 3 的时候断住:
下面打印了两次,但是只断住符合条件的一次
LogPoint
LogPoint 是在控制台打印日志,和console·log()差不多,但是console·log()打印实际上是浏览器保存了我们输出对象的信息数据引用,会造成代码污染和内存泄漏。
logpoint不会造成代码污染和内存泄漏,它就是个断点的设置,不在代码里。
添加一个 LogPoint:
输入打印的表达式:
跑一下 node 调试,你会发现打印了日志,但没有断住:
核心调试方法 (Debugger 调试)
用 debugger 调试
,调试复杂源码
为什么弃用console.log
很多同学用的是 console.log
调试,把想看的变量值打印在控制台。
console.log
打印弊端:
console.log
打印时,当打印的对象值,也是对象时,就无法展开打印的对象了,而是打印一个 [Object] [Array] 这种字符串,根本没法看;更致命的是打印的太长会超过缓冲区的大小,terminal 里会显示不全。
有的同学会问,打印一个具体的属性值可以用console.log
了吧,如果要打印一个对象值的话,为啥不考虑用LogPoint
,他和console.log
的优劣上面说过了,明显是LOgPoint
更加完美。
代码执行到这里就会打印:
而且用 console.log
的话调试完之后,作为合格的程序员 console.log
也是删掉的。
Debugger 调试
最重要的是 Debugger 调试是可以看到调用栈和作用域的!
调用栈:指代码的执行路线
比如这个 App 的函数组件,你可以看到渲染这个函数组件会经历 workLoop、beginWork、renderWithHooks 这些流程:
你可以点开调用栈的每一帧看下都执行了啥逻辑,用到啥数据。比如可以看到这个函数组件的 fiber 节点:
再就是作用域,点击每一个栈帧就可以看到每个函数的作用域中的变量:
经典操作
在排查问题的时候,用 Debugger 的话可以加一个异常断点,代码跑到抛异常的地方就会断住
可以看到调用栈来理清出错前都走了哪些代码,可以通过作用域来看到每一个变量的值。
有了这些东西,排查错误不就很轻松了么!
Performance性能(辅助调试)
Debugger 调试
可以看到一条代码的执行路径,但是代码的执行路径往往比较曲折。在浏览器工具中,有一个Performance 工具,可以看到代码执行的全貌。
SourceMap(调试源码)
我们执行的都是编译打包后的代码,基本是不可读的,调试这种代码也没啥意义,而 sourcemap 可以让我们直接调试最初的源码。
比如 vue,关联了 sourcemap 之后,我们能直接调试 ts 源码:
nest.js 也是:
总结
我们调试、打断点,根本原因就是对代码逻辑、语法等等不够熟悉,做调试就是为了熟悉每一行代码。
读懂了一行,然后读懂一个函数,最后读懂一个小功能的实现流程
转载自:https://juejin.cn/post/7246778044022259767