前言
这是我参与「第五届青训营」伴学笔记创作活动的第 10 天,相信大家对Bug 一词并不会陌生,也是我们不需要看到的,那如何去避免或者找到这个Bug并排除是比较关键的,那接下来就向大家介绍一下关于Web调试技术
前端必须知道的开发调试知识
Bug 与 Debug
Bug 的产生
First bug第一个bug 要追溯到1947年9月美国的葛丽丝·霍普(Grace Hopper)发现了第一个电脑上的bug。当在Mark II计算机上工作时,整个团队都搞不清楚为什么电脑不能正常运作了。经过大家的深度挖掘,发现原来是一只飞蛾意外飞入了一台电脑内部而引起的故障(如图所示)。这个团队把错误解除了,并在日记本中记录下了这一事件。也因此,人们逐渐开始用“Bug”(原意为“虫子”)来称呼计算机中的隐错。
前端 Debug 的特点
- 多平台
- 浏览器、Hybrid、NodeJs、小程序、桌面应用等
- 多环境
- 本地开发环境、线上环境
- 多工具
- Chrome devTools、Charles、Spy-Debugger、Whistle、vConsole
- 多技巧
- Console、BreakPoint、sourceMap、代理等
Chrome DevTools
Elements | Console | Source | Performance | Network
动态修改元素和样式
- 点击
.cls开启动态修改元素的class - 输入字符串可以动态的给元素添加类名
- 勾选/取消类名可以动态的查看类名生效效果
- 点击具体的样式值(字号、颜色、宽度高度等)可以进行编辑,浏览器内容区域实时预览
Computed下点击样式里的箭头可以跳转到styles面板中的css规则
可以用以下2种方式强制激活伪类
- 选中具有伪类的元素,点击
:hov - DOM 树右键菜单,选择
Force State
Console
以下可以选择等级,对日志进行分类查看
- console.log
- console.warn
- console.error
- console.debug
- console.info
以下有特殊的展示数据的效果
- console.table 具像化的展示 JSON 和 数组数据
- console.dir 通过类似文件树的方式展示对象的属性
- 占位符
给日志添加样式,可以突出重要的信息
%s: 字符串占位符;%o: 对象占位符, %c: 样式占位符;%d: 数字占位符
工作中建议还是用
console.info会好一些,因为console.log对对象需要console.log(JSON.parse(JSON.stringify(obj)))去输出对象。
还有一些日志,用于性能测试
console.time('测试名')
... do something
console.timeEnd(`测试名`)
export function rotate1(arr: number[], k: number): number[] {
const length = arr.length;
if (!k || length === 0) {
return arr;
}
const step = Math.abs(k % length); // 取绝对值
for (let i = 0; i < step; i++) {
const n = arr.pop();
if (n != null) {
arr.unshift(n);
}
}
return arr;
}
export function rotate2(arr: number[], k: number): number[] {
const length = arr.length;
if (!k || length === 0) {
return arr;
}
const step = Math.abs(k % length); // 取绝对值
const part1 = arr.slice(-step);
const part2 = arr.slice(0, length - step);
const part3 = part1.concat(part2);
return part3;
}
// 性能测试
const arr1 = [];
for (let i = 0; i < 10 * 10000; i++) {
arr1.push(i);
}
console.time("rotate1");
rotate1(arr1, 9 * 10000); // 1.659s
console.timeEnd("rotate1");
const arr2 = [];
for (let i = 0; i < 10 * 10000; i++) {
arr2.push(i);
}
console.time("rotate2");
rotate2(arr2, 9 * 10000); // 0.766ms
console.timeEnd("rotate2");
以上就是对两种旋转K步算法的性能测试
Sorce Tab
- 区域1: 页面资源文件目录树
- 区域2: 代码预览区域
- 区域3: Debug 工具栏从左到右依次为
- 暂停(继续)
- 单步跳过
- 进入函数
- 跳出函数
- 单步执行激活 (关闭) 所有断点
- 代码执行异常处自动
Break Point 与 Watch
- 使用关键字
debugger或 代码预览区域的行号可以设置断点 - 执行到断点处时代码暂停执行
- 展开
Breakpoints列表可以查看断点列表,勾选/取消可以激活/禁用对应断点 - 暂停状态下,鼠标
hover变量可以查看变量的值 - 在调试器
Watch右侧点击 +可以添加对变量的监控,查看该变量的值
Scope 与 Call Stack
- 展开 Scope 可以查看作用域列表(包含闭包) 闭包可以参考developer.mozilla.org/en-US/docs/…
- 展开 Call Stack 可以查看当前javaScript 代码的调用栈 关于调用栈可以参考developer.mozilla.org/en-US/docs/…
闭包
closure一个具有执行作用域的函数,当函数嵌套时,在子函数引用父函数内的变量时,就形成了执行作用域。
Call Stack调用栈,当脚本调用一个函数时,解释器将它添加到调用堆栈中,然后开始执行该函数。 由该函数调用的任何函数都被添加到调用堆栈中,并在到达其调用的地方运行。当\当前函数完成时,解释器将其从堆栈中取出,并在上一个代码清单中停止的地方继续执行。如果堆栈占用的空间大于分配的空间,则抛出“堆栈溢出”错误。
压缩后的代码如何调试? Source Map
前端代码天生具有
“开源”属性,出于安全考虑,上线之前 JavaScript 代码通常会被压缩,压缩后的代码只有一行,变量使用 'a'、'b' 等替换,整体变得不可阅读。那么压缩后的代码如何调试呢?
mappings 字段存储了源文件和Source Map 的映射
- 英文,表示源码及压缩代码的位置关联
- 逗号,分隔一行代码中的内容
- 分号,代表换行。
映射见 www.murzwin.com/base64vlg.h…
source Map 标准docs.google.com/document/d/…
开发在打包时,同时映射文件一份,然后通过在源文件下方附上映射文件的地址,然后
devtool通过diff映射文件和本地源文件,进行静态标记行数和列数。当浏览器报错或者警告的时候附上映射文件的地址,应该是这个思路
devtool: "source-map"在webpack.config.js添加这个配置就可开启文件映射
以下就是webpack源代码
source-map.js and source-map.js.map
(self["webpackChunk"] = self["webpackChunk"] || []).push([[0],[
/* 0 */
/*!*********************************************************************!*\
!*** ../../node_modules/coffee-loader/dist/cjs.js!./example.coffee ***!
*********************************************************************/
/*! unknown exports (runtime-defined) */
/*! runtime requirements: */
/***/ (() => {
// Taken from http://coffeescript.org/
// Objects:
var math, race;
math = {
root: Math.sqrt,
square: square,
cube: function(x) {
return x * square(x);
}
};
// Splats:
race = function(winner, ...runners) {
return print(winner, runners);
};
/***/ })
],
/******/ __webpack_require__ => { // webpackRuntimeModules
/******/ var __webpack_exec__ = (moduleId) => (__webpack_require__(__webpack_require__.s = moduleId))
/******/ var __webpack_exports__ = (__webpack_exec__(0));
/******/ }
]);
//# sourceMappingURL=bundle-source-map.js.map
思考题:既然Source Map 可以映射源码,那压缩后的代码带上 Source Map 上线不就又不安全了吗?
NetWork
- 区域1: 控制面板
- 区域2: 过滤面板
- 区域3:概览区域
- 区域4: Request Table 面板
- 区域5: 总结面板
- 区域6: 请求详情面板金技术社区
当你发送请求或者接收请求,就可查看当前链接的请求头、响应头是否携带信息和发送信息
ETag,Last-Modified用于检测304的原因等等
Application
Application 面板展示与本地存储Application 相关的信息
- Local Storage
- Session Storage
- IndexedDB
- Web SQL
- Cookie
点击左侧 Application 下的Stroage 面板中的 Clear Site Data可以清楚网页的本地存储数据
运用
Window.sessionStorage.getItem()/setItem()/window.localStorage.getItem()/setItem()去读和写里面的值
Performance
- 区域1: 控制面板
- 区域2: 概览面板
FPS: 每秒板数CPU: 处理各个任务花费的时间NET: 各个请求花费时间
- 区域3: 线程面板
Frames: 顿线程Main: 主线程,负责执行Javascript,解析HTML/CSS,完成绘制Raster:Raster线程,负责完成某个layer或者某些块(tile)的给制.
- 区域4:统计面板
Performance 运用 示例
页面卡顿 ==> 查看 FPS 指标 ==> 寻找性能瓶颈 ==> 优化代码
Lighthouse
通过
lighthouse展示的数据表
核心 Web 指标
Largest Contentful Paint (LCP) : 最大内容绘制,测量加载性能。为了提供良好的用户体验,LCP 应在页面首次开始加载后的2.5 秒内发生。
First Input Delay (FID) : 首次输入延迟测量交互性。为了提供良好的用户体验,页面的 FID 应为100 毫秒或更短
Cumulative Layout Shift (CLS) : 累积布局偏移,测量视觉稳定性。为了提供良好的用户体验,页面的 CLS 应保持在 0.1.或更少
移动端 H5.调试
真机调试
IOS
- 使用
Lightning数据线将iPhone与Mac相连 iPhone开启Web检查器 (设置 ->Safari-> 高级 -> 开启Web检查器iPhone使用Safari浏览器打开要调试的页面Mac打开Safari浏览器调试 (菜单栏一>开发 ->iPhone设备名 -> 选择调试页面 5.在弹出的Safari Developer Tools中调试
没有
iPhone设备可以在Mac AppStore安装Xcode使用其内置的IOS模拟器
Android
- 使用 USB 数据线将手机与电脑相连
- 手机进入开发者模式,勾选 USB 调试,并允许调试
- 电脑打开 Chrome 浏览器,在地址栏输入: chrome://inspect/#devices 并勾选 Discover USB devices 选项
- 手机允许远程调试,并访问调试页面
- 电脑点击 inspect 按钮
- 进入调试界面
直接使用手机扫码查看,体验更佳
VConsole
- 日志(Logs):
console.log|info|error... - 网络(Network):
XMLHttpRequest,Fetch,sendBeacon - 节点(Element):
HTML节点树 - 存储(Storage):
cookies,LocalStorage,SessionStorage - 手动执行
JS命令行 - 自定义插件
像这种界面就是通过将各个方法封装,然后当触发点击事件时,就调用这个封装的方法,其中里面需要获取相应的API
使用代理工具调试
原理:
- 电脑作为代理服务器
- 手机通过 HTTP 代理连接到电脑
- 手机上的请求都经过代理服务器
以 Charles 为例
- 安装 Charles
- 查看电脑 IP 和 端口
- 将 IP、端口号填入手机 HTTP 代理
- Charles 允许授权
- 使用
SwitchHosts! 软件给 Mac 电脑配 Hosts - 手机访问开发环境页面
默认情况下,Charles 无法抓取到 HTTPS 的请求,需要安装证书。这个证书就是数字证书是一个将公开的加密密钥和一个组织绑定的数据文件。一个数字证书包含一个组织的信息,如公共名称(例如 mozilla.org),组织单元(例如 Mozilla Corporation)以及位置(例如 Mountain View)。数字证书通常由certificate authority (en-US)签署,以证明其真实性。
常用代理工具
Charles: 适合查看、控制网络请求,分析数据
Fiddler: 与 Charles 类似, 适合 windows 平台
spy-debugger: 远程调试手机页面,抓包
Whistle: 基于 Node 实现的跨平台 Web 调试代理工具
whistle这个还是可以配合node.js真是强强联合
NodejS 调试
Inspector Protocol + Chrome Devtool
这些调试工具基本上都是差不多的,学会一个,其他稍微熟悉一下就可以上手了
Inspector Protocol + VS Code
- VS Code 点击运行
- 添加配置
- 启动调试
- 添加断点
- 查看变量、堆栈
这个我还是经常使用的,因为我个人是
VSCode的忠实爱好者,非常的好用,不管是debug,还是插件扩展,还有主题切换,链接Docker和gitbhub
常用开发调试技巧
线上即时修改 Overrides
- 打开 Sources 面板下的的Overrides
- 点击 Select folders forOverrides。选择一个本地的空文件夹目录。
- 允许授权
- 在 page 中修改代码,修改完成后 command +s 保存
- 打开
devTools,点击右上角的三个小点-> More tools ->Changes,就能看到所有修改了
利用代理解决开发阶段的跨域问题
用代理服务器解决是一个常见的方案,运用
node.js里的API 就可实现代理,也可以使用Nginx去进行反向代理,会更高效
启用本地 source map
线上不存在 Source Map 时可以使用 Map Local 网络映射功能来访问本地的
Source Map 文件。
使用代理工具 Mock 数据
- 右键选中要
mock数据的接口 选择save response,保存文件到本地。 - 本地打开保存的文件,编辑想
mock的数据并保存。 - 右键选中第一步的接口,选择
Map Local,Local Path选择第二步的文件。
小黄鸭调试大法
传说中程序大师随身携带一只小黄鸭,在调试代码的时候会在桌上放上这只小黄鸭,然后详细地向鸭子解释每行代码,然后很快就将问题定位修复了。
《程序员修炼之道》
小黄鸭调试大法是真有用,当你找不到
Bug捏几下小黄鸭,就马上会有思路
总结
不管是
PC端还是移动端掌握其中一种devtool就可适用大部分的调试器,当然一个好用的调试工具可以让你快速找到bug,但是个人希望的还是从自身出发,严谨逻辑,代码书写整洁,赋有注释,优秀的代码习惯是减少Bug最好的方式和方法