要验证我们编写的代码是否符合预期,我们需要在浏览器页面中进行调试。

# 浏览器(Chrome)调试

浏览器中调试功能又多又方便,但我们更主要是在 Chrome 下进行调试,先看看 Chrome 提供的开发者工具:

Elements、Console、Sources、Network 这种前面也介绍过了,剩下的功能在性能调试的时候会用的多一些:

  • Performance:查看页面在浏览器运行时的性能表现,如 CPU\GPU 执行时间与内存占用等
  • Memory:可进行内存查看和分析
  • Application:会列出所有的资源,包括 Database 和 LocalStore 等,可以对存储的内容编辑和删除
  • Security:查看网站的安全性,有效证书等
  • Audits/Lighthouse:会针对目前网页提出若干条优化的建议,包括网络加载性能、界面性能等

# 打断点

我们来看看在浏览器中打断点的一些方式。

(1) 代码断点。

常用的浏览器代码断点,在 Sources 面板 Javascript 文件行号处设置断点,如图我们下了个断点:
image

这里我们在请求发起的位置打了个断点,每次我们在搜索输入框输入的时候,都会发送请求,触发 debug 模式。

这里除了常规断点外, 还有个条件断点(右键 -> conditional breakpoint), 在设置的条件为true时才会断电, 在循环中需要断点时比较有用。断点后可以查看堆栈、变量。

(2) 事件断点。

元素上事件断点:某一事件/某类事件,从Elements > Event Listeners中进行。

(3) DOM 断点。

一般是 DOM 结构改变时触发。有时候我们需要监听某个 DOM 被修改情况,而不关心是哪行代码做的修改(也可能有多处都会对其做修改),可以直接在 DOM 上设置断点。

在元素审查的Elements标签页中在某个元素上右键菜单里可以设置三种不同情况的断点:

  • 子节点修改
  • 自身属性修改
  • 自身节点被删除

(4) XHR 断点。

右侧调试区有一个XHR Breakpoints,点击+ 并输入 URL 包含的字符串,即可监听该 URL 的 Ajax 请求,输入内容就相当于 URL 的过滤器。
一旦 XHR 调用触发时就会在request.send()的地方中断。

(5) 全局事件断点。

对事件发生时断点,不会限定在元素上,只要是事件发生,并且有handler就断点。
还可以对resizeAjaxsetTimeout/setInterval断点。

关于断点调试,除了几种工具和方式的了解以外,更多的需要在实际工作中进行实践才能掌握。用得好的话,谷歌源码也照样能趴出来里面的实现。

# 调试样式

样式的调试需要有个前提,就是对一些样式属性有很好的认识和理解,尤其涉及盒子模型、display和定位等。样式的规则除了一些基本的逻辑能遵循(可参考前面 1.2.1 章节内容),更多的则是丰富的经验,多写、多练。

在此基础上,一般样式的调试逻辑大概会是这样:

  • 样式是否生效
  • 文件、相关样式代码是否加载
  • 是否被其他样式覆盖(优先级问题)

image

如上图,一般我们会在选中对应的元素后,从上往下来找到生效(或不生效)的样式,同时也可以定位到对应的源文件。

这里面如果是本地环境调试的话,在 source map 的支持下,我们甚至可以直接在浏览器中修改源文件,保存生效。

# 调试 Javascript 脚本

我们可以通过添加alert()consoledebugger相关代码的方式,进行调试日志输出和断点。

(1) alert。

alert()会在窗口中显示一个警告对话框,是特别古老的调试方式,以前有些浏览器不支持控制台调试的时候可以这么操作。

由于alert()会阻塞进程,并且输出内容只支持局限的参数类型,因此现在几乎没什么人会使用了。

(2) console。

可以使用console来打日志。在生产环境的代码通常是编译后压缩后的代码,浏览器断点调试很不方便,或者是用户的反馈无法进行现场定位,此时通过日志获取需要的信息显得尤为重要。

console常用的几个方法有:

  • console.log():打印字符串,以及对象、变量什么的都可以
  • console.info():打印以感叹号字符开始的信息,使用方法和log相同
  • console.error():打印一条错误信息

养成在代码中通过console打日志的习惯,在遇到问题的时候能更加高效地定位和解决。

(3) debugger。

debugger被调用时, 执行暂停在debugger语句的位置,就像在脚本源代码中的断点一样。

断点的好处在于,我们可以直接看到代码的执行调用关系,也可以在断点位置查看上下文的变量情况,可以直观地看到问题在哪。

一般 Javascript 的调试逻辑,未按预期执行会有这样的原因:代码未执行到理想的位置。

这时候我们要思考这样的问题:

(1) 为事件触发执行 -> 检测事件是否被触发。

(2) 在函数中执行 -> 检测函数是否被调用。

(3) 在判断语句中执行 -> 检测判断是否正确。

我们可以在这些地方进行输出,或者打下断点:

(1) 事件触发的地方。

(2) 函数调用过程。

(3) 判断语句(if等)。

以上是类似的推导逻辑,我们在写代码时,会有一个预期的执行顺序和期望,如果说不生效,则我们可以:

  • 从前往后执行步骤,看在哪一步分了岔路
  • 从后往前打下断点,看在哪一步走丢了

其实最重要的是思路需要清晰,如果说你连自己要做的功能,逻辑还没理清楚的话,编写的代码质量不会高,同时调试性能也会随着下降。