在您阅读完这篇文章后,您就开始了解一个新项目。这不是一个古怪的东西 - 与你之前完成的项目相差不远。

当你花了 20 分钟左右的时间准备好了项目基础结构,正准备开始时,却遇到了这么一个错误。虽然它看起来并不太糟糕,但是当你花了 1 个小时在 Google 上搜索解决方案,以及改动代码不下 10 次来尝试着修复问题,结果却还是一样。你花了大量的时间在源码层面查找问题,即使实际一无所获,并且你已经产生了挫败感,但你还是在忍受着,这似乎并没有触及到你的底线。

Basic example of stack trace

在深入其他有可能解决问题的途径之前,退一步,清醒一下大脑,喝一杯水,然后思考一下这么几点:

  • 学习并不浪费时间,但是盲目地解决问题是浪费。
  • 这值得重新开始吗?
  • 也许你可以尝试不同的设置,来减少出错的可能性,比如像框架,这是人为问题出现的地方。所以,你的问题对于同样框架的用户来说,他们也很有可能会遇到。
  • 向别人求助。一个真诚的问题不是你弱小表现,反而在事实上,它可以增加团队的和谐程度。

现在,我们假设你完成了如上几点的思考和步骤,你想继续去修复问题,那么你应该采取什么策略呢?答案是我们只需要专注于一个特别的策略 —— 查看「堆栈跟踪信息」(stack trace) 来定位问题。寻找问题的根源,才是解决问题之本。请记住,如下这份简短的清单是基于我自己的经验,可能并不全面,但是,让我们用开放的心态来尝试一下新的策略。

  • 当你的代码遇到问题时,请 commit 你的代码(至少 commit 到你的本地仓库)。虽然这段代码并不处于完全正常运行的状态,但它起码比你最终弄得一团糟的局面要更好一些。沿途 commit 一些小改动,能够让你时刻追踪到你曾经做过了什么尝试或者改动。
  • 设置定时器(番茄钟或者普通定时器)。我建议不要超过 30 分钟。这不意味着你在这个时间段里面不需要把问题解决掉,但是你可以利用小憩来重新评估一下解决方案。
  • 知道在哪里看堆栈信息(取决于你的平台)。
  • 了解如何提取关键词和细节。
  • 知道如何进行查找。

接下来,我们深入讲解一下上面的最后三点。

在哪里看堆栈信息?

以下是不同场景下,可以显示堆栈跟踪信息的地方。

  • Node(后端) -> 终端控制台
  • React / Vue / vanilla JS (前端) -> 浏览器控制台

没有堆栈跟踪信息,我该怎么办?

一些情况下,并没有显示堆栈跟踪信息。为什么呢?这通常是因为代码中有逻辑错误(它并没有按照你所想的方式运行),且代码本身比较健壮。那么,你可以在你认为有问题的代码前后,打印调试信息到控制台,来尝试定位问题。

要找什么?

把堆栈跟踪信息想象成一堆盘子。把他们堆叠起来,并当他们开始摔下来的时候,你知道,最后放上去的盘子就是“罪魁祸首”。同样地,堆栈跟踪信息中顶部显示的就是最近的一些操作(也就很有可能是问题产生的地方)。

同样重要的是,要知道你所见的堆栈跟踪信息并非全部由错误组成,那是程序或者脚本运行至特定点所经由的历史信息。而恰恰因为这点,堆栈跟踪信息成为定位实际问题的绝佳工具。

错误 / 异常信息

如果问题的发生是涉及到一个包,那么包的作者很可能已经提供了良好的信息或者提示来帮助你定位问题。即便是语言层面产生的异常,也一般会有提示。查看错误 / 异常信息,是定位问题最有用的第一步。

文件名字

通常问题是存在于你自己编写的源代码中,而不是在其他人的软件包中(至少一般情况都这样),所以请查找有基本目录的文件。如果你有最近改动过的代码文件,那么应该优先检查那里。

代码行号

你很幸运,因为你在堆栈跟踪信息中不仅能看到问题产生所可能涉及到的文件名,还可以看到有关联的代码所处的行号,有时候还会包含列号。虽然列号不一定有用,但还是很感激那些决定将列号显示在堆栈跟踪信息中的人。

异常类型

有时候,抛出的异常的名称能给你有用的线索,可能你不会确切地知道它意味着什么。具体信息详见下一节。

如果你经常遇到问题,那么可以考虑将一些语言层面或者框架中常见的异常记忆起来。

我要深入到哪里?

有些堆栈跟踪信息非常多,你在不得已要阅读它们的时候就像在读一本小说,直至犯困。那么我们要深入阅读到哪里就可以了,而不是白白地在浪费时间?

我的经验是,在堆栈跟踪信息中的前五条尽可能找到你所需要的信息。如果你在详细地梳理过之后还没有任何收获,那么不管怎样,用尽一切方法,继续深入。

让我们伪造一个错误来看看其中的一些线索是否有效,你可以在客户端或者在服务端运行如下代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// demo.js

firstFunction = () => {
secondFunction();
}

secondFunction = () => {
thirdFunction();
}

thirdFunction = () => {
notDefined();
}

firstFunction();

最后终端会报出异常:
Comprehensive example of stack trace

要搜索什么

如果错误中提供了消息,那么这可能是搜索的最佳选择。

谷歌搜索使用技巧

  • 用引号包裹要搜索的精确的短语
  • 在这些引号中放置星号以进行模糊匹配
  • 使用减号来剔除包含指定关键字的结果
  • 如果你要查找教程,那么在你要搜索的短语前面加上 “how to”

Stack Overflow 使用技巧

  • 为你要查找的内容加上标签,用方括号包裹起来即可:[vue] trigger an event
  • 通常你需要查找带有纯绿色且标识为 “answers” 框的结果,这意味着某个人的回答已被接受为正确的答案。
  • 除了标记为答案的结果,你也要关注一下其他的答案,他们有可能更加前沿或者简练,又或者是更加符合你的风格。

对于所有搜索结果,使用右键指定用新 tab 页面来打开。为了更快地完成这个操作,可以使用你所在的平台终端的快捷键:

  • Windows 下使用 ctrl + 鼠标左键单击;
  • Mac 下使用 cmd + 触控板单击。

在将代码接受到最终产品之前,请务必了解代码实际执行的操作。

问题解决了吗?

有多少次我们遇到问题的时候,都是先搜索,然后去 Stack Overflow 网站搜索答案,然后看到我们之前已经浏览并赞同了那些答案?为了避免继续出现这种情况,我们应该把遇到的问题和解决方案记录下来,且更重要的是,起码完成一件以下事项:

  • 创建一个新的测试案例。
  • 写一些能够捕捉到指定错误并抛出自定义错误信息的错误处理函数。

例如:

1
if (valueIsNotCorrect) throw new Error(`D'oh, you did it again! Fix me by ...`);

把这些都记录下来,那就是两倍的胜利。

最后,Tiwitter 上的 Jordan Hall 发了这么一个有趣的内容:

1
2
3
4
window.onerror = error => {
// redirect to SO with error as query
window.location.href = `https://stackoverflow.com/search?q=[js]${error.message}`;
}