超越Console.log() - 提升你的调试技能

182 阅读7分钟

Using the browser developer tools like a boss

你可能已经建立了一种编码模式,利用了浏览器控制台提供的一些关键工具。但你最近有没有深入研究过?有一些强大的工具可供你使用,它们可能只是彻底改变你的工作方式。

舒适区

作为开发者,我们喜欢找到一种能让我们感到高效的工作方式。然而,陷阱是我们对某种工作流程感到舒适,并认为没有更好的方法。我们已经内化了一种方法,不需要考虑我们做什么。

一个常见的开发者工作流程是在编辑器中写代码,保存它,然后切换到浏览器,加载产品,看是否一切正常。然后,我们使用浏览器中的开发者工具来调整CSS,也许还可以测试产品对大小调整和移动模拟的反应。我们通过在我们需要了解的地方添加console.log() 语句来调试我们的脚本--在行号和调试之间保持紧密的联系。

这样做的结果是有点乱。如果你在网上冲浪时一直打开你的浏览器工具,你会在代码中看到很多不应该最终出现在最终产品中的信息。这不仅使无意中打开它们的人感到困惑,而且也会使新的开发者望而生畏。

我们只使用了我们所掌握的一小部分工具,我们可能会错过更好地工作和更少的努力的机会。这没关系,但让我们看看我们如何能做得更好。

我们将看看免费提供的开发者工具的几个功能,你可能会使用但不知道。具体来说,这些功能在Chromium浏览器的开发者工具Visual Studio Code中的一些功能。让我们先来了解一下一个多年来的最爱--控制台。

提升我们的控制台游戏水平

在我们的开发生涯的早期,我们有条件在代码的任何地方添加一个console.log(thing) ,以了解正在发生的事情。通常这就足够了,但有时你还是得不到正确的信息,或者信息是以一些难以理解的原始格式出现的。

你可以通过抓取GitHub上的这个演示页面,并在浏览器中打开你的开发工具,看到下面所有的技巧。(或者查看[CodePen上的实时演示](onCodePen)。

这里的第一个技巧是为你记录的变量添加大括号。这不仅可以打印出它们的值,还可以打印出变量的名称。这让我们更容易在日志中追踪到什么值来自哪里。

let x = 2;
console.log(x) // 2
console.log({x}) // {x: 2}

格式化日志

你也可以在你的日志信息中使用以百分比符号开头的指定器。这允许你以不同的格式记录某些值。

  • %s: 以字符串形式记录日志
  • %i 或 : 日志为整数%d
  • %f: 日志为浮点值
  • %o: 日志为可扩展的DOM元素
  • %O日志作为可扩展的JavaScript对象

你可以混合和匹配这些。第一个字符串中的替换顺序就是后面的参数顺序。

console.log('%ix %s developer', 10, 'console');
// 10x console developer

你甚至可以用这个进行转换,以防你得到的信息不是你喜欢的格式。

console.log('%i', 12.34455241234324234); // 12

%c 指定器允许你使用CSS来样式你的日志信息,以防你真的想突出。

console.log('%cPay attention to me','color:firebrick;font-size:40px')

日志是很好的,但你可以用更多的选项来使事情更明显,避免自己写功能。

分组日志

你可以使用console.group() ,将你的日志分组,以显示为可扩展和可折叠的组。

const label = 'The Millenium Falcon Crew';
console.group(label);
console.log('Leia');
console.log('Han');
console.log('Chewie');
console.log('Ben');
console.groupEnd(label);

你可以对组进行嵌套,你也可以使用console.groupCollapsed() ,默认不展开它们。

const extendedlabel = 'The Millenium Falcon Crew extended';
const meat = 'Humanoids';
const metal = 'Droids';
console.group(extendedlabel);
console.groupCollapsed(meat);
console.log('Leia');
console.log('Han');
console.log('Chewie');
console.log('Ben');
console.groupEnd(meat);
console.group(metal);
console.log('R2D2');
console.log('C3PO');
console.groupEnd(metal);
console.groupEnd(extendedlabel);

允许日志过滤

你也可以使用console.info(),console.error()console.warn() 来代替console.log() 。这允许你使用控制台侧边栏或级别选择器来过滤你在控制台看到的信息。这样,你就可以在来自第三方脚本和项目中其他脚本的信息中更容易找到自己的信息。

其他有用的控制台方法

在你的调试过程中,你可能已经创建了一些变量来计算某个方法被调用的频率或功能的执行频率。你可以通过使用console.count()console.countReset() 方法做同样的事情。你可以随心所欲地创建它们,并按标签进行区分。

console.count('Chocula'); // Chocula: 1
console.count(); // default: 1
console.count('Chocula'); // Chocula: 2
console.countReset('Chocula'); 
console.count(); // default: 2 
console.count(); // default: 3
console.count('Chocula'); // Chocula: 1

你也可以用console.time() 方法来衡量你的脚本的某一部分需要多长时间。

console.time('go');
for(let i = 0; i < 200000; i+=1) {
  let x = Math.random()*2000;
}
console.timeEnd('go'); // go: 11.7861328125 ms

根据你想记录的数据,使用正确的方法也是有意义的。使用console.dir() ,不仅可以显示内容,还可以显示你发送的数据类型。例如,如果你想要一个节点的XML表示,你可以使用console.dirxml() 。而console.table() 对于将JSON数据显示为一个可排序的表格是非常好的。

用实时表达式取代日志记录

使用console.log() 来监视那些变化很大的东西是很诱人的。这不仅很慢,而且也很难跟上。虽然控制台自动将类似的值作为实例分组是很有用的,但它仍然是一个很大的动作,你经常发现自己只想检查值,而不是向后滚动查看某个变化发生的地方。

这就是实时表达式的作用。你可以通过激活开发者工具中的眼睛按钮来添加实时表达式。然后你会被要求输入任何有效的JavaScript表达式,它的值会显示在控制台的上方。

通过激活眼睛按钮并输入document.activeElement 来尝试一下。这个表达式应该显示为body ,直到你激活或聚焦于页面中的任何其他元素。

每个表达式旁边都有一个x 按钮,用来停止监测数值。实时表达式是持久的,并不与某个域或网站相连。这就是为什么在你完成调试后删除它们以避免错误是有意义的。

实时表达式是很好的小帮手,可以在不使用控制台的情况下监视你的脚本的数据变化。这在你记录非常繁忙的数值时特别有用--比如鼠标位置。这可能就是你的方法:

let x = 0;
let y = 0;
document.addEventListener('mousemove', e => {
  x = e.x;
  y = e.y;
  console.log({x}, {y});
});

其结果是一个非常繁忙的日志,而且很容易错过重要的信息。使用实时表达式,你不需要掌握快速移动的日志,这很可能也是一种更快的调试体验。

自己试试吧:抓取有日志记录的移动鼠标无日志记录的移动鼠标的演示代码,并尝试在浏览器中运行这两个例子。

使用控制台来操作当前文档

开发者工具中的控制台不仅仅是一个显示日志的方法。它是一个REPL,允许你编写和执行 JavaScript,并使用自动完成功能了解当前文档的可用方法和属性。只要进入开发者工具中的控制台,输入doc ,然后点击tab,它就会自动转换为document 。如果你加上一个句号,你会看到所有可用的方法和属性。这是学习可用方法和属性的一种吸引人的简单方法,它可以让你在短时间内写出大量的代码。

除此之外,控制台也是与当前文档交互和访问其中部分内容的绝佳方式。有大量的方便方法和快捷方式可供你作为Console的实用工具。其中一些是:

  • $_ 存储最后一条命令的结果。因此,如果你要输入 ,然后按回车键,输入 ,你就会得到4。2+2 $_
  • $0 到 是最后一个被检查的元素的堆栈,其中 总是最新的一个。$4 $0
  • $() 和 是 和 的缩写。$$() document.querySelector() document.querySelectorAll()
  • $x() 允许你通过XPATH选择DOM元素。
  • copy() 复制你给它的任何东西到剪贴板。
  • clear() 清除控制台。
  • getEventListeners(node) 列出一个节点的所有事件监听者。
  • monitorEvents(node, events) 监视并记录节点上发生的事件。
  • monitor(method) 每当一个方法被调用时,创建一个日志项。

这些方法中有些是非常强大的,是我们过去可能自己写的一系列console.log() 语句的东西。

下面是一些你可以使用这些方法的方法:

monitorEvents(window, ['resize', 'scroll']);

monitorEvents($0, 'key');

这记录了每次窗口滚动或被调整大小的情况。第二个例子很有意思,它记录了对当前选定元素的任何按键操作:

console.table($$('a'),['href','text'])

这就检索了文档中的所有链接(因为$$('a')document.querySelectorAll('a') 的简称),并将它们显示为一个可排序的表格。作为table 方法的第二个参数的数组定义了该表的列。 否则,链接的每个属性都会变成一列,这就很难浏览了。最有趣的是,这个表不仅是可排序的,而且你可以复制和粘贴它--例如,粘贴到Excel。

与其写复杂的JavaScript来过滤这些结果,你可以使用CSS选择器的力量。例如,如果你想有一个表格,列出文档中所有非内联图像的srcalt 信息,你可以使用下面的方法:

console.table($$('img:not([src^=data])'), ['src','alt'])

然而,这其中最有趣的是编写在页面的上下文中运行的脚本。

例如,当你使用Markdown生成HTML时,大多数页面生成器会在标题上创建自动ID,以允许深度链接到文档的这一部分。一个# New Stuff 的标题会变成<h1 id="new-stuff">New stuff</h1> 。我需要批量创建大量指向这些深层链接的短网址,但又不想手工创建它们。

所以我开始为控制台写一个脚本,让它帮我做这件事:

let out = '';
$$('#main [id]').filter(
    elm => {return elm.nodeName.startsWith('H')}
).forEach(elm => {
   out += `${elm.innerText}
${document.location.href}#${elm.id}
` 
});
copy(out);

结果是一个文本块,其中有每个标题的文本内容,后面是指向它的完整URL。

这也显示了$$ 这个快捷方式的一个有趣的额外功能。document.querySelectorAll('#main [id]').filter() 会导致一个错误,因为返回的值不是一个Array ,而是一个NodeList 。你需要用[...document.querySelectoAll('#main [id]').filter()]Array.from(document.querySelectoAll('#main [id]').filter()) 将其转换为一个Array ,这对于从jQuery到JavaScript的人来说,已经有一段时间是一个烦恼了。$$ 这个方便的方法可以直接使用所有的Array方法。

一般来说,你有很大的权力从控制台访问和改变浏览器中渲染的页面的任何东西。而且你还有一个额外的好处,那就是使用开发者工具的Elements标签来获得正确的路径。激活每个节点旁边的... 菜单,从上下文菜单的复制菜单中选择你需要的东西。

从控制台转移到源程序

一旦你找到了一些操纵网站的有趣方法,你很快就会发现控制台作为一个脚本环境的局限性。在Console上编码可能有点困难,因为你是在一个单行环境中工作。点击回车键就会立即执行你所输入的内容,而你经常会不小心这样做。不过有一个技巧。你可以用Shift+Enter来编写多行脚本。

总的来说,控制台提供了一个很好的测试环境,但编辑体验却很差。幸运的是,在Sources面板上也有一个完整的编辑器。在那里,你可以检查当前页面的代码,并编写更复杂的脚本来与之互动。