这是我参与「第四届青训营 」笔记创作活动的的第8天
Bug 与 Debug
Bug 臭虫, Debug 杀虫。
前端 Debug 的特点:
- 多平台
- 浏览器、 Hybird 、 NodeJs 、小程序、桌面应用等
- 多环境
- 本地开发环境、线上环境
- 多工具
- Chrome devTools 、 Charles 、 Spy-Debugger 、 Whistle 、 vConsole 、……
- 多技巧
- Console 、 BreakPoint 、 sourceMap 、代理等
Chrome DevTools
打开 DevTools :
- 网页右键,然后点击检查。
- 先按 Fn , 然后按 F12。
Elements 代表网页的元素和样式; Console 控制台,用于展示日志的界面; Sources 展示项目的源代码; Network 站点请求的资源; Application 展示与本地存储相关的信息; Performance 网页性能相关; Lighthouse 查看网页性能情况。
Elements 调试技巧
- 点击 .cls 开启动态修改元素的 class
- 输入字符串可以动态的给元素添加类名
- 勾选/取消类名可以动态的查看类名生效效果
- 点击具体的样式值(字号、颜色、宽度等)可以进行编辑,浏览器内容区域实时浏览
- computed 下点击样式里的箭头可以跳转到 styles 面板中的 css 规则
如下图箭头点开, font-family 有三个样式,跟 font-family 同行的为起作用的样式值:
可以用以下两种方式强制激活伪类:
- 选中具有伪类的元素,点击 :hov (在 .cls 旁边)。
- DOM 树(选中元素)右键菜单,选择 Force State 。
Console 调试技巧
- 可以选择等级,对日志进行分类查看
console.log('Welcome to bytedance!');
console.warn('Welcome to bytedance!');
console.error('Welcome to bytedance!');
console.debug('Welcome to bytedance!');
console.info('Welcome to bytedance!');
console.log('%s %o %c%s', 'hello', {name: 'tom', age: 18}, 'font-size: 24px; color: red', 'Welcome to bytedance!');
注意:console.debug('Welcome to bytedance!'); 应该打印蓝色字体的 Welcome to bytedance! 在红叉叉下一行,但此处无输出 (浏览器不同、浏览器版本不同可能导致的效果也不同)。
占位符:给日志添加样式可以突出重要的信息。
- %s : 字符串占位符
- %o : 对象占位符
- %c :样式占位符
- %d : 数字占位符
不同类型的数据打印出来的字体颜色可能不一样:
字符串为黑色,数字类型为蓝色。
console.table 可以具象化的展示 JSON 和数组数据:
const object = [
{name: 'Tom', sex: 'm', age: 12},
{name: 'Helen', sex: 'f', age: 13},
{name: 'Peter', sex: 'm', age: 14},
];
console.table(object);
console.dir 通过类似文件树的方式展示对象的属性:
<div class="dd" id="demo">
<div class="aa"></div>
<div class="cc"></div>
<div class="bb"></div>
</div>
const d = document.getElementById('demo');
console.dir(d);
Sources 调试技巧
Watch 观测值; Breakpoints 执行代码的断点; Scope 代表的是作用域; Call Stack 代表的是调用栈; XHR/fetch Breakpoints 所有请求的断点。
查看变量的值
方式一:鼠标移入某个你想要观测的变量,它对应的值就会显示在上面。
方式二:输入想要观测的变量名。
方式三:看作用域。
进入断点调试界面
代码执行到断点进入暂停状态。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<input value="" class="num" id="numA" oninput="handleChangeNumberA()">
+
<input value="" class="num" id="numB" oninput="handleChangeNumberB()">
=
<span id="result"></span>
<button id="run">计算</button>
<script src="./index.js"></script>
</body>
</html>
index.js :(方式一:在想要调试(暂停)的地方加入 debugger)
let numberA = ""
let numberB = ""
function handleChangeNumberA() {
numberA = document.getElementById('numA').value;
}
function handleChangeNumberB() {
numberB = document.getElementById('numB').value;
}
document.getElementById('run').onclick = function() {
// 方式一:在想要调试(暂停)的地方加入 debugger
debugger;
document.getElementById('result').innerHTML = numberA + numberB;
}
方式二:“√”点掉或者点添加断点时的数字,都可以取消断点
Breakpoints 与 Watch 小结:
- 使用关键字 debugger 或代码预览区域的行号可以设置断点
- 执行到断点处时代码暂停执行
- 展开 Breakpoints 列表可以查看断点列表,勾选/取消 可以激活/禁用 对应断点
- 暂停状态下,鼠标 hover 变量可以查看变量的值
- 在调试器 Watch 右侧点击“+”可以添加对变量的监控,查看该变量的值。
Scope 与 Call Stack 小结:
- 展开 Scope 可以查看作用域列表(包含闭包)
- 展开 Call Stack 可以查看当前 JavaScript 代码的调用栈
- 关于调用栈可以参考:developer.mozilla.org/zh-CN/docs/…
压缩后的代码如何调试?
前端代码天生具有“开源”属性,出于安全考虑, JavaScript 代码通常会被压缩,压缩后的代码只有一行,变量使用 ‘a’、‘b’等替换,整体变得不可阅读。那么压缩后的代码如何进行调试呢?
webpack.config.js 文件配置开启 source-map 调试:在 module.exports 中添加 devtool: 'source-map'
version 代表 source-map 标准的版本; file 映射哪个文件; mappings 映射规则,经过 base64 转换后的结果,代表着映射代码的行数列数等; sources 源文件的地址;sourcesContent 源文件的内容。
mappings 字段存储了源文件和 Source Map 的映射:
- 英文,表示源码及压缩代码的位置关联
- 逗号,分隔一行代码中的内容
- 分号,代表换行
映射见:www.murzwin.com/base64vlq.h…
Source Map 标准:
两个问题:
- Source Map 文件肯定比源文件要大且可以映射源码,那带 Source Map 上线的话压缩后的代码又不安全了。
- 不带 Source Map 文件上线,要是代码出了问题,代码又变得不可调试了。
上线之前把 Source Map 文件上传到另一个平台(比如监控平台),上传完后删除 Source Map 文件 ,不带 Source Map 的产物部署上线,错误监控程序把错误收集起来,拿着这些错误对照平台上的 Source Map 文件,然后去展示对应的源码位置。
Network 调试技巧
Waterfall 可以看出那些请求是并行的,那些请求时串行的。
模拟弱网环境。
通过 Request Table 面板找到对应的接口,看后端返回的结果符不符合预期就可以判断是前端还是后端的问题(实现前后端联调)。
Application 调试技巧
- Local Storage
- Session Storage
- IndexedDB
- Web SQL
- Cookie
Performance 调试技巧
控制面板第一个按钮是录制按钮。
在统计面板中, script 表示执行脚本耗时; rendering 表示渲染耗时。
设置 CPU 和网络的性能。
graph TD
页面卡顿 --> 查看FPS指标 --> 寻找性能瓶颈 --> 优化代码
FPS 代表着每一帧的渲染情况。
More tools 可以设置更多的性能。
以目前技术来说,正常情况下浏览器刷新的频率为 60 fps ,低于60都是掉帧会卡顿。
看不出代码有什么问题,继续往下找(点击 app.js:95 就会跳转到 Sources 调试对应文件的对应行并在代码预览区域左侧显示对应的耗时)
图中有端代码
if(!app.optimize) 这表示选中不优化的代码,与其对应的 else 为优化后的代码。(给我的感觉:能不能优化看你能不能区分各种相似功能方法的优缺点)
Lighthouse 调试技巧
核心 Web 指标
- Largest Contentful Paint (LCP) :最大内容绘制,测量加载性能。为了提供良好的用户体验, LCP 应在页面首次开始加载后的2.5秒内发生。
- First Input Delay (FID) :首次输入延迟,测量交互性。为了提供良好的用户体验,页面的 FID 应为100毫秒或更短。
- Cumulative Layout Shift (CLS) :累积布局偏移,测量视觉稳定性。为了提供良好的用户体验,页面的 CLS 应保持在0.1或更少。
开始性能分析
Performance 性能评分(核心 Web 指标包含在性能评分里面); Accessibility 访问无故障; Best Practice 最佳实践; SEO 搜索引擎优化; PWA (Progressive Web App)。
移动端 H5 调试
真机调试
直接使用手机扫码查看,体验更佳。
iOS :
- 使用 Lightning 数据线将 iPhone 与 Mac 相连;
- iPhone 开启 Web 检查器(设置 -> Safari -> 高级 -> 开启 Web 检查器);
- iPhone 使用 Safari 浏览器打开要调试的页面;
- Mac 打开 Safari 浏览器调试(菜单栏 -> 开发 -> iPhone 设备名 -> 选择调试页面);
- 在弹出的 Safari Developer Tools 中调试。
没有 iPhone 设备可以在 Mac App Store 安装 Xcode 使用其内置的 iOS 模拟器。
Android :
- 使用 USB 数据线将手机与电脑相连;
- 手机进入开发者模式,勾选 USB 调试并允许调试;
- 电脑打开 Chrome 浏览器,在地址栏输入: chrome://inspect/#devices 并勾选 Discover USB devices 选项;
- 手机允许远程调试并访问调试页面;
- 电脑点击 inspect 按钮;
- 进入调试页面。
第二种移动端常用的调试方案: VConsole
代理调试
原理:
- 电脑作为代理服务器
- 手机通过 HTTP 代理连接到电脑
- 手机上的请求都经过代理服务器
以 Charles 为例:
- 安装 Charles ;
- 查看电脑 IP 和端口;
- 将 IP 、端口填入手机 HTTP 代理;
- Charles 允许授权;
- 使用 switchhosts 软件给 Mac 电脑配 Hosts ;
- 手机访问开发环境页面。
默认情况下, Charles 无法抓取到 HTTPS 的请求,需要安装证书。
常用代理工具:
- Charles :适合查看、控制网络请求,分析数据
- Fiddler :与 Charles 类似,适合 Windows 平台
- spy-debugger :远程调试手机页面,抓包
- Whistle :基于 Node 实现的跨平台 Web 调试代理工具
Nodejs 调试
可以直接使用官方的调试方法。
- 执行命令: node --inspect=8888 index.js ;
- chrome 浏览器访问服务(上面会返回 nodejs 监听的端口);
- 点击绿色 node 图标打开 node 调试面板(在 chrome://inspect/#devices 中配置 network target);
- 在 node 调试面板中使用断点调试。
在 VSCode 界面:
运行 -> 启动调试 -> 添加断点 -> 查看变量、堆栈
常用开发调试技巧
线上即时修改 Overrides
- 打开 Sources 面板下的 Overrides ;
- 点击 Select folders for Overrides 。选择一个本地的空文件夹目录;
- 运行授权;
- 在 page 中修改代码,修改完成后 command + s 保存(Windows : Ctrl + s);
- 打开 devTools ,点击右上角的三个点 -> More tools -> Changes ,就能看到所有修改了。
在浏览器的 Elements 上修改的元素和样式被刷新后是不会保存的,经过上述操作会被保存到文件。
利用代理服务器解决开发阶段的跨域问题
同源策略是浏览器的安全策略不是HTTP的。
启用本地 Source Map
线上不存在 Source Map 时可以使用 Map Local 网络映射功能来访问本地的 Source Map 文件。
使用代理工具 Mock 数据
后端未准备好接口,前端需要数据去渲染时,可以自己去 Mock 数据。
- 右键选择要 mock 数据的接口,选择 save response ,保存文件到本地;
- 本地打开保存的文件,编辑想 mock 的数据并保存;
- 右键选中第一步的接口,选择 Map Local , Local Path 选择第二步的文件。
Path 为保存的那个文件的相对路径。
小黄鸭调试大法
传说中程序大师随身携带一只小黄鸭,在调试代码的时候会在桌上放上这只小黄鸭,然后详细地向鸭子解释每行代码,然后很快就将问题定位修复了。——《程序员修炼之道》