调试是项目开发中非常重要的环节。掌握一些调试技巧,不仅能帮助我们定位到问题,还能提升我们的开发效率。本文从两个场景来介绍调试技巧:
代码报错。
逻辑出错。
大部分情况下,能读懂错误信息与错误堆栈的信息,就知道是什么导致的错误。报错信息可以分为如下四类:
引用类型错误。引用不存在的变量时会报这个错。如:
console.log(a)
会报错: Uncaught ReferenceError: a is not defined
。因为 a
被引用时,还没有被定义。
语法错误。出现语法错误时会报这个错。如:
let c == 5
会报错: Uncaught SyntaxError: Unexpected token '=='
。
类型错误。值的类型与预期不一致时会报这个错。如:
let data = await fetch('/api/list')
data.list
如果 data 是 undefined
。会报错:Uncaught TypeError: Cannot read property 'list' of undefined
。预期是对象,实际是 undefined
。
再看下面的代码:
doSth() function doSth(cb) { cb() }
会报:Uncaught TypeError: cb is not a function
。预期是函数,实际不是。
范围错误。当设置的数值超出相应的范围会报这个错。如:
new Array(-1)。
会报:Uncaught RangeError: Invalid array length
。
有的报错,根据报错信息,无法定位到具体的代码。此时,可以用二分法来快速定位到报错的具体代码。具体做法如下:
如,在 React 开发中,常会碰到这样的错:Uncaught Invariant Violation: Minified React error #31;...
。这是因为非法的 JSX 的节点导致的。二分法可以快速的找出有问题的 JSX。
可以在报错的前一行断点。可以查看当前作用域和上级作用域中,各个变量值。看是哪个导致的下一行的报错。
上图中右侧的 Scope Tab
中的内容是作用域中变量的信息, Call Stack
是调用堆栈信息。
如果只有在某些情况下,才会报错,可以打条件断点。我习惯在代码里写条件断点:
if (a > 5) {
debugger
}
逻辑出错,常常是因为错误的条件判断,错误的循环退出条件和没控制好异步导致的。
找出逻辑出错的方式:
有时候,错误的逻辑会导致的预期之外的 DOM 变化。或 接口的调用。这种情况下,可以通过设置 DOM 断点 来查找问题。DOM 断点 支持 3 种触发条件:
Subtree Modified。子元素发生变化时触发。
Attribute Modified。属性发生改变时触发。
Node Removal。元素被删除时触发。
举个例子,要看元素为什么被错误的移除了,设置 DOM 断点,触发条件是 node removal
。设置 DOM 断点 方式,右击目标元素,点击下图所示的菜单:
有时候,错误的逻辑会导致的预期之外的接口调用。这种情况下,可以通过设置 XHR/fetch 断点 来查找问题。XHR/fetch 断点 的触发条件支持筛选接口地址。在开发者工具的 Source Tab,可以打 XHR/fetch 断点。如下图所示:
我们给函数写覆盖率很高的单元测试来确保函数逻辑的正确性。但受限与团队的理念,写测试的不小的工作量,项目工期等原因,写测试的毕竟是少数。
用 console.assert()
来写些断言,是测试的低成本替代方案。只有在 console.assert()
的第一个参数是 false
时,向控制台输出第二个参数的内容。通过 console.assert()
输出的报错信息,来知道哪个函数出错了。例如:
function sum(a, b) {
// 代码实现
}
console.log(sum(1, 2) !== 3, `sum 函数逻辑错误`)
有的元素,在开发中工具去审查它的时候,它就消失了。可以通过断点会冻结 DOM 的特性来调试这元素。在控制台中,输入下面的代码:
setTimeout(() => {debugger}, 2000)
然后,让元素显示。2秒后,会进断点,此时再去审查元素,因为 DOM 的更新被断点冻结住了,元素就不会消失。