作为前端工程师,无论是开发还是线上环境,浏览器或是 node
,移动端或者 PC
端,经常会遇到一些 bug
,那么如何快速定位和解决问题呢,笔者准备了一份前端调试指南供大家参考。
「文章大纲」:
调试本身可以分为两个过程,「定位问题」 和 「解决问题」。而更重要的显然是如何快速的定位问题。本文将集中讨论如何 「快速发现」 并 「调试问题」,至于如何解决问题,那就是开发本身的事情,无法一概而论了。
Chrome 的 DevTools 是最常用的调试工具,下面主要介绍下 「Elements」、「Console」 和 「Source」 三个面板的使用。
Elements 面板会显示目前网页中的 DOM、CSS 状态,且可以修改页面上的 DOM 和 CSS,即时看到结果,省去了在编辑器修改、储存、浏览器查看结果的流程。
Elements 主要可以分为 DOM 结构以及元素(Element)内容两个子面板,下面主要介绍一下 Elements DOM
开启 Elements 面板时,标记的元素后方总会有个 == $0
image-20220116212438893
选中一个元素后再到 Console 面板输入$0
,会发现刚刚选中的元素出现在 Console 中,如果再多点几个元素,还可以用$1
、$2
、$3
、$4
(到此为止)来拿到前几次选到的元素。
另外在 Console 中对元素按下右键,选择 Reveal in Elements Panel
可以跳到该元素在 Elements 面板中的位置,对 Elements 面板的元素按下右键则有 Scroll to view
可以把视野滚到能看见元素的地方。
想要在 Console 面板中用 JavaScript 操作元素时,$0
就非常方便,另外也可以搭配 console.dir($0)
来观察元素的各个属性,如果在 Console 直接输入 $0
或是 console.log($0)
只会显示元素自身。
image-20220116212412476
有时候一些 dom 节点会嵌套很深,导致我们很难利用 Element 面板 html 代码来找到对应的节点。inspect(dom元素)
可以让我们快速跳转到对应的 dom 节点的 html 代码上。
eg:在 console 输入inspect($('#app'))
,回车后便可以跳转到#app 节点的 html,进行审查元素
Console 面板作为 shell 提示窗口用来和页面文档以及 DevTools 进行交互
前端说起调试,最常用的肯定就是console.log
方法,但是 console 是一个对象,上面还有很方便的方法。
console.table()
可用于打印 obj/arr 成表格
image-20220104154752095
console.trace()
可用于 debugger 堆栈调试,方便查看代码的执行逻辑,也可以帮助我们看一些库的源码
image-20220104155335803
console.count
会印出这个标签被执行了几次,预设值是default
,可以用在快速的计数。
console.countReset
与 count
配套,用来重置,可用在计算单次行为的触发的计数。
console.group() / console.groupEnd();
为了在一大堆混乱的讯息中一眼看到自己的 log, 通常会这样做
console.log("----start-----");
console.log(object);
console.log("---end---");
虽然 ---
是很显眼没错,但其实有更好的做法,用console.group
可以自订 Message group 的标签也可以多层嵌套,并用 console.groupEnd
来关闭 Group
console.log("iteration");
for (var firstLevel = 0; firstLevel < 2; firstLevel++) {
console.group("First level: ", firstLevel);
for (var secondLevel = 0; secondLevel < 2; secondLevel++) {
console.group("Second level: ", secondLevel);
for (var thirdLevel = 0; thirdLevel < 2; thirdLevel++) {
console.log("This is third level number: ", thirdLevel);
}
console.groupEnd();
}
console.groupEnd();
}
$(select)
拿到的是 NodeList(伪数组),而 $$(select)
拿到的则是一个纯正的数组,方便我们在控制台上调试 API,只有在 devtools 下打印才能使用
image-20220104155823831
Sources
面板可以查看浏览器页面中的源文件(html/js/img/css
等),点击面板下方的{}
大括号可以将代码转成可读格式,同时可给js
文件添加上断点。Sources
下的Snippets
可以添加文件片段,可在浏览器中运行
「debugger 语句」
在代码中加上debugger
语句,是仅次于console.log
的常用调试方式,在需要的地方进行添加断点
chrome devtool breakpoint 下面列举一些平常使用较多的断点方式
普通断点:在想断住的那一行左侧单击一下就可以添加一个断点,运行到该处就会断住。
image-20220104161116371
条件断点:右键单击代码所在的行左侧,会出现一个下拉框,可以添加一个条件断点。输入条件表达式,当运行到这一行代码并且表达式的值为真时就会断住。
image-20220104161315275
image-20220104161458483
Event Listeners 打断点:在 Chrome Devtools 的 Elements 面板上找到你想排查的 dom 节点,右侧面板 Event Listeners 中会有当前阶节点,可以当前节点打断点调试。
image-20220104161916972
异常断点:在 Debugger 面板勾选 Uncaught Exceptions 和 Caught Exceptions 可以添加异常断点,在抛出异常未被捕获或者被捕获时断住。用来调试一些发生异常的代码时很有用。
Event Listener 断点:在 Chrome Devtools 的 Sources 面板还可以添加 Event Listener 的断点,指定当发生什么事件时断住,可以用来调试事件相关代码。比如拖拽事件、媒体事件断点
debug
相当于在该 function 的第一行插入debugger
:function a() {
console.log(1);
}
// 在Console中输入
debug(a);
a();
// 相当于
function a() {
debugger;
console.log(1);
}
// 使用 undebug 解除
“BlackBox Script”可以在调试中忽略某些脚本(此处的 BlackBox 为动词),在 Call Stack 堆栈中会将该脚本隐藏,单步调试时也不会步入脚本中的任何函数。如果确认第三方库没有 bug,就可以 BlackBox 整个第三方库的 js 脚本,在调试中跳过这些代码的执行。
三种添加 BlackBox 的方法:
第一步:打开 Filesystem add folder to workspace,把包含 sourceMap 的目录添加进去
image-20220104171549241
第二步:打开指定的混淆代码
image-20220104171821907
第三步:右键 -> 选择【Add source map】
第四步:拷贝 Filesystem 中的 sourceMap 地址。
chrome devTools 提供了 local overrides 能力,首先,打开 sources 下的 overrides 面板;
然后,点击【select folder for overrides】选择修改后的文件存储地址;
image-20220104172628695
再然后,点击顶部的授权,确认同意;
最后,找到入口文件,然后右键选择 Save for overrides
(一定要是原件,formatted 后的版本不行),
image-20220104173000023
然后找到保存的文件进行修改,重新刷新页面后,修改后的代码就可以被执行了。
下面以调试 webpack 源码为例:
node --inspect --inspect-brk node_modules/webpack/bin/webpack.js --env.production --config webpack-common.js
执行 bin 中相应「启动文件」webpack.js 打开 chrome 的开发者工具页面,如果看到 node 的「绿色图标」,点击就可进入调试。
img
whistle 配合 VConsole 或者 eruda,可以在任何环境下开启调试模式,在 whistle 中 规则中配置相应域名下进行调试,以 m.zhuanzhuan.com 域名为例
m.zhuanzhuan.com jsAppend://{eruda.js} jsAppend://{erudaInit.js}
下载 eruda.js,把 eruda.js 和 erudaInit.js 配置在 values 中
image-20220104164430460
image-20220104164328168
这样就会在移动端开启调试 eruda 模式了。
微信调试网页,正常来说你可以使用微信开发者工具[1] 进行调试。它本质上和 chrome://inspect
方法类似,只是它为线上的微信包提供了 debug 模式,并将操作简单化。具体的使用方法可以参考官方文档:https://x5.tencent.com/tbs/guide/debug/season1.html
如果你需要调试的 Android 手机版本 >= 4.4,则推荐使用 chrome://inspect
的方式进行调试,它能为 WebView 带来原生的开发者工具,可以方便的对代码进行断点调试。该方法需要满足以下三个条件才能使用:
//开启 webview 的 debug 模式
webview.setWebContentsDebuggingEnabled(true);
当满足以上要求之后,访问 chrome://inspect
,页面将显示您的设备上已启用调试的 WebView 列表。要开始调试,请点击您想要调试的 WebView 下方的 inspect。像使用远程浏览器标签一样使用开发者工具。
如果你手机上安装的是 DEBUG 版应用,那么推荐使用 Safari 来调试,它能为 WebView 带来原生的开发者工具,可以方便的对代码进行断点调试。该方法需要满足以下三个条件才能使用:
由于 iOS 有签名校验机制,真机正式包不允许 Safari Debug,所以安装在真机上的包必须是测试签名打的包。需要联系客户端将我们 iOS 设备的 ID 写入到可信任设备列表中,然后使用 iTunes 安装客户端提供的测试包即可。当满足以上要求后,就可以在 Safari -> 开发中看到自己的设备以及 WebView 中网页,点击后即可开启对应页面的 Inspector,可以用来进行断点调试。
当系统版本或者未开启 debug 模式导致上面的方法不可用时,可以考虑使用 weinre[2]。weinre 通过在页面中插入一段脚本,将页面的所有行为发送到服务上。首先我们需要安装并启动服务:
npm install -g weinre
weinre --httpPort 8000
访问 http://localhost:8000
按照页面提示将 debug 脚本插入到页面中。访问页面后就会发现 winere 页面中出现了对应的请求记录,点击该记录即可跳到如下页面。可以看到这个就是一个网页版的开发者工具,可以方便的查看网络请求,控制台执行代码以及样式修改等。
方式 | 优点 | 优点 | 推荐场景 |
---|---|---|---|
移动端网络代理+whistle 本地代理 | 移动端网络代理+whistle 本地代理 | 1、无法断点调试 | 1、推荐开发环境使用 |
whistle 外部工具注入(vConsole.js 或 Eruda.js) | 1、方便 | 2、无法断点调试 | 1、推荐任何环境调试 |
Android 真机调试 | 1、最接近真实环境,可以断点调试 | 1、条件苛刻麻烦; 2、仅限 Android; 3、不够方便 | 1、实在找不到问题的保底手段 |
iOS Safari 真机调试 | 1、最接近真实环境,可以断点调试 | 1、条件苛刻麻烦; 2、仅限 ios; 3、不够方便 | 1、实在找不到问题的保底手段 |
微信开发者工具调试 | 1、可以 pc 一样方便的断点调试 | 1、仅限微信; 2、要提前将自己的账号加入到开发者账号中 | 1、任何需要使用到微信的场景 |
weinre(web inspector remote) | 1、在 whistle 内有继承,比较方便; 2、方便调试样式,选中即可得 | 1、无法断点调试 | 1、任何需要调试样式的场景 |
当出现异常时,按照这个基本逻辑排查,一般可以快速定位问题。
可以快速确定页面不符合预期的原因
undefined/null/NaN
等值之后,会引发后续的异常。要先从检查数据入手。网络请求是不稳定因素之一,可能会带来难以预料的复杂情况,出现问题的时候检查网络请求和数据的优先级很高。
检查开发者工具 Network/网络面板,查看需要获取数据的接口是否成功获取到数据。
取不到数据的原因有两类,一类是责任在前端,一类是后端。主要通过请求提交的内容是否合法,接口返回内容是否符合预期两个方面判断。
查看的关键点:
合法请求没有得到预期返回,就找后端解决,请求与预期不符就是代码写错了,到错误地方查看代码。
如果是控制台有错误信息的,利用 sourcemap 可以快速定位到问题出在哪一行。如果没有报错信息,就需要凭借当前页面的状态自己判断出问题的区域,按照代码执行的顺序排查。这一步可以利用的手段比较多,情况也更复杂,需要具体分析。
查看代码运行状态:
通过断点、日志等手段判断程序有没有按照自己想要的顺序执行,简单来说就是排查。
检查运行过程中每一步的数据变化,是否与预期的相同。
Safari
,Whistle
,vConsole
查看异常日志,从而迅速定位类库位置,从而找寻替换或是兼容方案。Try Catch
吧。Babel
来编译 ES6
,但是额外的第三方类库如果有不兼容的语法,低版本的移动设备就会异常。所以,先用上文讲述的调试方法,确定异常,然后去增加 polyfill 来兼容吧写到这里整篇文章的调试方法就结束了。也许有很多不到位的地方,专业用词不严谨的地方,希望读者和我一起交流。非常乐意我的调试总结给予前端人受用。
[1]微信开发者工具: https://mp.weixin.qq.com/debug/wxadoc/dev/devtools/download.html
[2]weinre: http://people.apache.org/~pmuellr/weinre/
Copyright© 2013-2020
All Rights Reserved 京ICP备2023019179号-8