你还在用Console.log进行调试代码?这些真的好用不难!

原文自:alan.norbauer.com/articles/br...

罗列一些浏览器很有用但是不明显的debug技巧,假定大家都对浏览器的开发者工具有一些了解为前提(前端开发很难不了解😄)。

1、高级的条件断点

相较于我们在意想不到的地方进行有副作用的表达式进行debug,其实可以从像条件断点这样的基本功能中找到更多方法来帮助我们进行debug。

Logpoints / Tracepoints

例如,我们可以在断点处进行console.log。Logpoints是一种可以打日志的非暂定断点。Microsoct Edge已经内置了这个功能,Chrome也已经在v73版本加入了该功能,Firefox还没有。但是,我们可以使用条件断点在任何浏览器里面模拟他们。

如果你想计算当前行代码执行了多少次,你可以用console.count取代console.log

截止到2020年5月,所有主流的浏览器都支持了logpoint/tracepoint。

Watch Pane

你可以在watch pane中使用console.log。例如,你想在每次断点的时候看一下localstorage的一个数据快照,那么你可以在watch pane里面创建一个console.table(localStorage)

或者你想在DOM变化以后执行一个表达式,你可以设置一个DOM变化断点:

然后添加watch表达式,例如,记录DOM的快照:(window.doms = window.doms || []).push(document.documentElement.outerHTML)。现在,每当DOM子树发生变化,debugger会暂定执行,最新的DOM快照会追加到window.doms的最后。(没有办法创建没有暂定执行的DOM断点)

追踪调用栈

假设,你有两个方法,一个是展示loding的show方法,另一个是hide这个loading的方法。然后,你在代码的某处你调用了show方法,没有调用hide方法。那么你怎么找到这个未成对的show方法的调用来源呢?在show方法的条件断点里面用console.trace,运行代码,找到show方法的调用的堆栈的最后一条,点击调用即可定位到代码了。

更改程序行为

相较于在程序里添加有副作用的表达式,我们在浏览器就可以修改程序的执行逻辑。

例如,你可以重写getPerson这个方法的入参id,因为id=1会返回true,这个条件断点将会暂停debugger,为了避免这个情况,添加false到表达式里面就可以了。

快速进行性能统计

如果你想快速了解到某个代码执行耗时,你可以在条件断点里使用console的timing api,在代码开始的地方添加一个条件断点console.time('label'),在代码结束的地方同样打上一个条件断点console.timeEnd('label')。每次你执行这段代码,浏览器都会在log界面打印出运行时间。

函数方面

参数数量方面进行断点

当且仅当当前函数调用的时候使用了三个参数的时候进行暂停。当你有一些重载的函数十分有用。

参数未匹配方面进行断点

当且仅当当期函数调用的参数数量跟约定的不一致的时候,

使用时间

跳过页面加载

在页面加载5s内不要暂停:performance.now() > 5000,当你只想对页面加载以后的逻辑进行暂停的时候非常有用。

跳过N秒

接下来5s内遇到断点不要暂定执行,但之后的都需要暂停:window.baseline = window.baseline || Date.now(), (Date.now() - window.baseline) > 5000

通过这句来进行逻辑的重置:window.baseline = Date.now()

使用CSS

基于Css的值来进行暂停,例如,当前仅当页面body为红色背景色的时候进行暂停:window.getComputedStyle(document.body).backgroundColor === "rgb(255,0,0)"

偶数次执行

window.counter = window.counter || 0, window.counter % 2 === 0

采样中断

当前行执行10次进行1次的中断:Math.random() < 0.1

永不中断(Chrome only)

当你右键选择了"Never Pause Here",Chrome将会创建一个条件为false且永不通过的断点,这样将会使debugger在这行代码这里永不暂停。

当你想免除XHR的断点中某行的时候会非常有用,例如忽略我们已经抛出的异常场景。

自动示例ID

将类的每个实例自动设定一个唯一的ID,你可以在类的构造函数中这样做:(window.instances = window.instances || []).push(this)

然后可以在类的方法里面检索这个唯一的ID: window.instances.indexOf(instance)

编程式触发中断

通过一个全局布尔值来控制一个或某几个条件断点:

然后通过编程的方式修改这个布尔值

  • 手动改:window.enableBreakpoints = true;
  • 从其他断点中修改:
  • 从setTimeout中控制:setTimeout(() => (window.enableBreakpoints = true), 5000);
  • 等等

2、监控类的调用(Chrome Only)

你可以使用Chrome的monitor命令行方法很轻松的跟踪到所有调用该类的方法。举例说明,有一个类Dog

ts 复制代码
class Dog {  
    bark(count) {  
        /* ... */  
    }  
}

如果我们想知道所有Dog的实例的调用,在命令行里面粘贴这些代码:

ts 复制代码
var p = Dog.prototype;  
Object.getOwnPropertyNames(p).forEach((k) => monitor(p[k]));

当您想为任何类的任何实例(而不仅仅是Dog)编写一个这样做的函数时,这很有用。

3、调用并调试方法

当你想在调用方法之前在console里面进行调试,可以用debugger。例如

ts 复制代码
function fn() {
    /* ... */
}

在console中:

cmd 复制代码
debugger; fn(1)

然后"step into next function call"就可以debugfn的实现了。

当您不想手动找到fn的定义并添加断点时,或者当fn动态绑定到函数并且您不知道源在哪里时,这很有用。在Chrome中,您还可以选择在命令行上调用debug(fn),每次调用它时,调试器都会暂停fn内部的执行。

4、当URL变化时暂停执行

在一个单页应用中当URL发生变化的时候暂停执行(例如,当页面路由发生变化):

ts 复制代码
const dbg = () => {
    debugger
}

history.pushState = dbg;
history.replaceState = dbg;
window.onhashchange = dbg;
window.onpopstate = dbg;

创建一个暂停执行而不中断导航的dbg版本是留给读者的练习。

此外,请注意,当代码直接调用window.location.replace/assign时,这不起作用,因为页面将在分配后立即卸载,因此没有什么可调试的。如果你仍然想查看这些重定向的来源(并在重定向时调试你的状态),你可以在Chrome中调试相关方法:

ts 复制代码
debug(window.location.replace)
debug(window.location.assign)

5、调试属性的读取

如果你有一个对象并且想知道对象里面的属性什么时候被读取了,使用对象的getter并调用debugger。例如,将{configOption: true}改成{get configOption(){ debugger; return true }}(要么在源码改,要么在条件断点中改)。

当你将一些配置选项传递给某个东西,并且你想看看它们是如何使用的时,这很有用。

6、使用copy(Chrome & Firefox Only)

你可以通过console中直接执行copy命令将浏览器里面的信息拷贝到剪切板中,这样你可以在任何地方查看这部分内容。你可能感兴趣的内容有:

  • 当前dom的快照:copy(document.documentElement.outerHTML)
  • 资源的元数据:copy(performance.getEntriesByType("resource"))
  • 大的JSON blob流,格式化后:copy(JSON.parse(blob))
  • localStorage的转存:copy(localStorage)
  • 等等

7、调试HTML/CSS

console确实能帮我们解决关于HTML和CSS的相关问题。

无JS影响的检查DOM

在DOM检查器中时,按ctrl+\(Chrome/Windows)可随时暂停JS执行。这允许您检查DOM的快照,而不必担心JS会更改DOM或事件(例如mouseover)会导致DOM从您的下面更改。

检查不好定位到的元素

假如你想检查一个只在某些条件下才会展现的元素,此时你需要将鼠标移动到该元素上的时候才能进行检查,但是有些时候你想放上去的时候,它就消失了,就像下面这样:

想要检车这样的元素,你可以将这段代码拷贝到console中:setTimeout(function() { debugger; }, 5000)。这给了你5s来触发这个UI,一旦5s结束,js的执行将会暂停并且你的元素也不会消失,你可以自由地移动你的鼠标并且检查这个元素了。

一旦JS执行暂停,你可以检查元素,编辑样式,console中执行命令等。当你需要检查将鼠标放在某处进行hover或者focus才能展示的元素时,这确实十分有用。

记录DOM快照

记录当前DOM的快照

ts 复制代码
copy(document.documentElement.outerHTML);

每一秒记录一次DOM的快照

ts 复制代码
doms = [];
setInterval(() => {
    const domStr = document.documentElement.outerHTML;
    doms.push(domStr)
}, 1000);

或者只是转存到console

ts 复制代码
setInterval(() => {
    const domStr = document.documentElement.outerHTML;
    console.log("snapshotting DOM: ", domStr)
}, 1000)

监控选中的元素

ts 复制代码
(function(){
    let last = document.activeElement
    setInterval(() => {
        if(document.activeElement !== last) {
            last = document.activeElement
            console.log("Focus changed to: ", last)
        }
    }, 100)
})()

查找粗体元素

ts 复制代码
const isBold = (e) => {
    let w = window.getComputedStyle(e).fontWeight;
    return w === 'bold' || w === "700"
}
Array.from(document.querySelectorAll('*')).filter(isBold)

或者是当前选中元素的子元素进行查找

ts 复制代码
Array.from($0.querySelectorAll('*')).filter(isBold)

引用当前选定元素

$0在console中意为当前选中元素,在Chrome和Edge中你可以通过$1访问上一次选中的元素,$2为上上次选中的元素,以此类推。

在Chrome中你还可以获取当前选中元素的监听事件:getEventListeners($0),例如

监听元素的事件(Chrome Only)

调试选中元素的所有事件:monitorEvents($0)

调试选中元素的特定事件:monitorEvents($0, ["control", "key"])

8、总结

文章中还是提到了很多有效地调试能力,让我印象深刻的有:无断点的logpoint,快速统计代码性能,打印dom快照,监控不好定位的元素等,这些功能需要多使用才能孰能生巧,也是时候放弃在代码里添加有副作用的代码进行调试的办法了。那么,不知道你心水哪些功能呢?

相关推荐
小白学习日记1 小时前
【复习】HTML常用标签<table>
前端·html
丁总学Java1 小时前
微信小程序-npm支持-如何使用npm包
前端·微信小程序·npm·node.js
yanlele2 小时前
前瞻 - 盘点 ES2025 已经定稿的语法规范
前端·javascript·代码规范
懒羊羊大王呀2 小时前
CSS——属性值计算
前端·css
DOKE2 小时前
VSCode终端:提升命令行使用体验
前端
xgq2 小时前
使用File System Access API 直接读写本地文件
前端·javascript·面试
用户3157476081352 小时前
前端之路-了解原型和原型链
前端
永远不打烊2 小时前
librtmp 原生API做直播推流
前端
北极小狐2 小时前
浏览器事件处理机制:从硬件中断到事件驱动
前端
无咎.lsy2 小时前
vue之vuex的使用及举例
前端·javascript·vue.js