掌握前端开发必备技巧:Chrome开发者工具Sources面板深度解析
最近发现有很多同学在调试代码的时候依旧是一个 log 打天下,着实是有些辛苦。本篇文章,咱们来介绍一下 chrome 开发者工具里一个相当好用、对测试相当友好但是被很多人忽略的面板:源代码(Source) 面板,让我们减轻负担,提前下班。
汉化切换
先简单提一下怎么把控制台切换成中文。打开控制台后点击右上角小齿轮进入设置,然后就可以找到语言选项,选择中文后重启控制台即可。
这个应该是最近几个大版本才更新出来的,所以如果你没有也不用难过,下面还是会用英文界面进行介绍,OK,话不多说我们现在开始。
左侧面板
首先我们打开 Souces 面板,可以看到是左中右布局,在左侧可以看到有几个选项卡:
在 Page 和 Content scripts 面板里可以看到当前页面正在使用的代码,平时用的不多(毕竟大多数代码都被压缩过了看不懂),真正实用的是后面两个:
咱们一个一个来说,首先是这个 Overrides(覆盖) 面板,它可以把本地的文件覆盖到你的网站上。emmmm,有啥用呢?还记得有时候我们直接在浏览器里调试样式,然后回头改了个代码,浏览器直接一个自动刷新,好了,辛辛苦苦改到一半的样式没了。
现在我们就可以用 Overrides 来解决这个问题:它会把你编辑的内容保存到本地,然后用这个本地文件覆盖线上加载的内容。
具体做法如下:先在 Overrides 面板里选择保存到本地哪个文件夹里,允许后就可以来到 Element 面板里找到我们要编辑的样式文件,点一下之后我们会跳会到 Sources 里,然后右键这个文件,选择保存到覆盖文件即可(Save for Overrides),然后我们的准备工作就做完了,具体操作见下面 gif:
注意上面 gif 里,我们保存了覆盖文件之后,Network 面板会有一个黄色感叹号,同时 Element 里也可以看到对应的样式文件链接前多了个图标,悬停上去就可以看到链接到的文件地址。
好了,现在我们就可以编辑了,随便改样式,之后点击刷新,我们会发现之前编辑的样式并没有消失:
这个操作除了我们平时调试样式,还很适合在非开发机上操作。例如在客户机器上,这么搞一下之后实操+争吵后确定了客户想要的样式,就可以直接把保存到本地的样式拷贝拿回去用。
代码片段
左侧第二个实用功能是代码片段(Snippets):
这简直就是一个浏览器自带的 IDE。有很多人觉得它不实用,因为里边的代码跑一遍之后再执行就会报错说变量已定义:
只能跑一遍的代码要他有什么用?其实不然,只要把 var 换成 let 就可以解决问题了:
或者你的代码里很多 var,直接在外边包一个自执行函数也可以:
其实除了测试自己的代码,我们还可以搭配 chrome 插件 console-importer 解锁更强力的操作:在浏览器里使用第三方依赖!
例如下面这段代码,就可以直接在浏览器里搞出一个 lodash 的迷你 playground:
if (!('_' in window)) $i('lodash')
else {
const result = _.map([1, 2, 3, 4, 5], i => i + 1)
console.log(result)
}
很简单对吧,看一下全局有没有 lodash,没有的话就安装。具体效果可以看这个 gif:
我大多数有点忘的 lodash api 都会来这里试一下(lodash 官方文档里的交互用例载入太慢了 ),除了 lodash 之外还有相当多的第三方包都可以用,比如 jq,react,vue 等等,大家*发挥~
断点调试
页面中间的编辑器窗口就是我们调试的主场了,但是在调试之前我们还有一个问题要解决。
现在目标大多数前端框架都会有一个编译打包的过程,所以虽然我们可以通过左侧的 Page
面板找到页面使用的所有代码,但是我们会发现里边包含的内容基本都看不懂。
我知道怎么打断点,但是你得先告诉我代码在哪啊。所以首先,咱们先讲一下 如何定位问题代码,如果你已经很熟悉的话大体浏览一下即可:
顺带说一句,如果你使用 webpack 打包的话,打包前的网站源码实际上就放在 Page
的 webpack://
目录下。
如何进入断点调试
首先是最常用的办法,从报错信息入手,现在主流的前端框架都带 source-map,会自动校准调用栈。所以我们可以看一下异常的调用栈,找到其中我们眼熟的文件,点进来就是我们熟悉的内容了,然后就是左侧打红点并重新执行下问题操作,代码会在执行到你的红点处暂停。
但是报错信息里的调用栈就那几个,如果我们在报错信息里没有看到眼熟的文件名,都是一些 xxxdevtool 之类的文件,这时候可以使用第二种办法,点击右侧按钮条中最后一个按钮,并选中 Pause on caught exceptions(报错时暂停)。然后我们就可以在 Call Stack
面板里看到比报错信息里更详细的的调用栈。
又或者,你在这里也没找到具体的报错位置,或者你的代码根本没有报错,只是行为异常。这样我们就只能掏出最终武器(console.log
或者 debugger
)。
使用 debugger 关键字可以直接让代码在 debugger 处暂停,而 console.log 在控制台中显示出的日志右侧也会标记出文件位置,我们可以点击跳进 Souces 面板并手动打断点:
对于大多数人来说,接触到的第一种调试方法基本就是 debugger 了,但是我在日常使用中直接打 debugger 反而最少,因为问题有可能不是在你电脑上复现的、或者没办法很方便的接触到源码、又或者复现问题需要点很多操作,回去改下代码页面就刷新了。想复现又要重新做一遍复现流程,就很烦。
所以说,下文中我们会更倾向于:不修改代码,尽量使用 devtool 本身提供的能力进行调试。
条件断点和 log point
首先,请出我们的测试用例,很简单,闭包创建了一个点击回调,每次点击按钮时就会修改值,你也可以复制到 html 文件里一起试一试:
<script>
const createClick = function () {
let val = 0;
return () => {
const valBox = document.getElementById('val');
valBox.innerText = ++val;
}
}
const onClick = createClick()
</script>
<body>
<span id="val">0</span>
<button onclick="onClick()">点此 +1</button>
</body>
现在我们来介绍第一个常用的断点变种:条件断点 。
顾名思义,就是给这个断点的触发加上一个条件。这对于调试那些会重复执行超多次的代码很有帮助。
放置条件断点的方式也很简单:在要添加断点的位置右键选择 Add Conditional Breakpoint 或者 右键已有的断点,选择 Edit breakpoint,将断点类型设置为 Conditional Breakpoint,然后输入任何 js 支持的条件表达式即可,表达式里支持的变量就是断点所在作用域能访问到的所有变量。
除了条件断点之外,我们还可以创建另一个叫做 log point(日志点?)的东西。这个特性是 chrome 73 里添加进来的,可以让我们少打 console.log
,具体用法和条件断点类似:
这个功能特别实用,几乎所有可以写在 console.log 里的代码都可以写在这里,并且从上面这个 gif 中也可以看到,可以使用从全局、闭包、到当前作用域的所有可访问变量。直接干死 console.log 了属于是。
右侧面板
讲完了左边中间。咱们再来讲一下右边的这些面板,你可能经常看见,但是基本没怎么碰过:
首先需要说明一点,这里的大部分功能都是 断点调试 的一部分,也就是说,很多都要进入断点后才可以使用。接下来我们一个一个来看:
1、Threads
首先是最上面的 Threads
(线程)。你可能会问,啊?浏览器里的 js 不是单线程么?
是的没错,但是现在我们有了 Web Workers 和 Service Worker。所以前端也是会同时存在多线程的。而 Threads
就会标识出当前启动的线程:
2、Watch
还记得我们刚才介绍的 log point 么,Watch 字段就是断点调试中内置的 log。我们可以在这里新建表达式,进入断点后这里就会自动计算出表达式的值。
进入断点前:
进入断点后:
你可能会好奇为什么进入断点前的 val 会显示为 [object HTMLSpanElement]
,这个是 chrome 开发者工具的一个小特性(褒义),它会自动将设置了 id 的 dom 元素放在全局下供我们快速访问,而我们的测试用例里就设置了一个 id 为 val
的 span 元素。
你也可以在控制台里直接输入元素 id 来打印出对应的元素,例如上面的 val。
3、BreakPoints
这个面板里显示了所有存在的断点,我们可以在这里启用 / 停用指定断点:
4、Scope(常用)
这个面板显示了 当前断点所在的作用域,使用频率很高:
大体上可以分为以下四种:
-
Local
本地作用域:代码当前{}
内存在的变量,this
就可以在这里找到。 -
Closure
闭包作用域:代码当前所处闭包里的变量。注意名称后用括号注明了这是哪个闭包里的内容,因为闭包可以嵌套,所以可能出现多个Closure
作用域。 -
Script
代码标签作用域:当前<script />
标签里存在的变量,这个用的不多。 -
Global
全局作用域:这个就不用解释了吧。
因为这个面板里可以找到 this
和闭包内容,所以在调试一些作用域问题的时候简直是神技,非常好使。
这个 Closure
在官中里被翻译成了 关闭
,老机翻了。
5、Call Stack
调用栈,这个就不多说了。进断点之后可以看到当前的调用次序,点击能跳到对应的代码。
6、XHR/fetch Breakpoints
从这里往下的面板都是一些特殊断点的触发器了。而这个面板就是用来设置当发送包含对应 url 的请求时进入断点:一般都是输入一个 url 片段。如果不输入直接回车的话就是对所有请求启用断点。
但是由于我们一般都会使用一些第三方库来封装请求,所以这个功能一般都会搭配上面的 Call Stack
面板来定位实际发送请求的业务代码。
7、Dom Breakpoints
顾名思义,就是 dom 变更的时候进入断点。但是很多同学不知道怎么建 dom 断点,因为右上角没有看到小加号:
实际上建 dom 断点不是在这里,而是在 Element 面板里,我们右键一个 dom 元素,在菜单下面就可以看到 Break on
,其中可以选择 subtree modifications
(子树变更时触发)、attribute modifications
(元素属性变更时触发)、node removal
(元素移除时触发):
这个断点在查找一些底层、组件库问题或者性能调优的时候非常好使。特别是在找那些不知道啥时候被添加上的玄幻样式。
8、Global Listener
显示所有绑定到 window 的事件监听器,也可以找到对应的绑定代码:
在调试时,我们可以通过解绑事件来定位问题是哪个事件引发的:
有一点需要提一下,这个面板 只显示绑定到 window 上的监听器。如果你想查看 docuemnt 或者具体元素上的元素,需要到 Element 里选择对应元素,然后在右侧的 Event Listener
里查看,内容都是一样的,这里不再赘述:
有一说一,这个面板在调试代码的时候用的比较少,写爬虫的时候用的倒挺多。
9、Event Listener Breakpoints
这个没啥好说的,就是给你列了一下所有的事件,你勾选哪个,对应事件触发时就会进入断点。
因为这里列的挺全,所以我一般都是忘记了某种事件叫啥的时候才会来这里查一下。这个写爬虫也挺常用。
10、CSP Violation Breakpoints
用于在违反内容安全策略的时候进入断点,我没用过,就不当复读机了,有兴趣的可以去官方介绍看一下:
What's New In DevTools (Chrome 89) - Chrome Developers
总结
本文介绍了开发者工具里的 Sources 面板,包含了不少很实用却被被许多开发者忽略的功能,特别是代码片段、条件断点以及右侧的 Watch 表达式、Scope 作用域、Call Stack 调用栈面板。希望本文能让大家重新认识它,提高自己的开发效率,毕竟没人不喜欢早点下班!
大家有兴趣的话以后也可以分享一下其他实用的开发者功能,如果有帮助的话欢迎点赞~
参考
- 使用工作区编辑文件 - Microsoft Edge Development | Microsoft Docs
- Console Importer - Chrome 网上应用店 (google.com)