Console 调试技巧

head-img

前言

说到 console,很多人的第一反应肯定是 console.log,它既简单又直接!然而,在处理复杂问题时,这种方式往往效率低下。本文分享 20 个 console 的调试小技巧,帮您更快速地找出并解决问题。

1. 样式轰炸

1
2
3
4
5
6
7
8
// 这段代码使用了 console.log 的样式功能,通过传入特定的 CSS 样式字符串,
// 可以为输出的文本添加自定义样式,达到醒目的效果,这里创建了一个带有渐变背景、
// 白色文字、圆角和内边距的“VIP调试模式”样式,以及一个后续的灰色文字样式用于显示版本号。
console.log(
'%cVIP调试模式%c v2.0',
'background:linear-gradient(45deg,#ff6b6b,#4ecdc4);color:white;padding:4px 8px;border-radius:3px;',
'color:#666;margin-left:8px;'
);

2. 动态模板

1
2
3
4
5
// 定义一个包含用户信息的对象,然后利用模板字符串结合当前时间,
// 在控制台输出一条包含用户名称、等级以及执行操作信息的日志,方便跟踪用户行为及操作时间。
const user = { name: 'Alice', level: 'VIP' };
console.log(`[${new Date().toISOString()}] ${user.name} (${user.level}) 执行了支付操作`);
// 输出示例:[2023-12-25T09:30:00Z] Alice (VIP) 执行了支付操作

3. 树形结构解析

1
2
3
4
5
6
7
8
9
10
11
12
13
// 以一种结构化、易读的方式在控制台输出用户数据树,使用特定样式突出显示树的标题,
// 数据树包含用户 ID、个人资料(姓名、地址,地址又细分城市和区)以及订单列表等信息,方便查看数据层级关系。
console.log('%c▸ 用户数据树', 'color:#48bb78;font-weight:bold', {
id: 1001,
profile: {
name: 'Bob',
address: {
city: '上海',
district: '浦东'
}
},
orders: ['#2023-1001', '#2023-1002']
});

4. 表格魔法升级

1
2
3
4
5
6
7
8
9
10
11
12
// 对于加密货币数据数组,先对每个数据项进行处理,添加一个表示价格趋势的字段(根据 24 小时价格变化判断上升或下降),
// 然后使用 console.table 将处理后的数据以表格形式输出,使数据对比更加直观清晰。
const cryptoData = [
{ coin: 'BTC', price: 42000, change24h: '+5.2%', volatility: 'high' },
{ coin: 'ETH', price: 2300, change24h: '-1.8%', volatility: 'medium' },
{ coin: 'SOL', price: 98, change24h: '+12.6%', volatility: 'high' }
];

console.table(cryptoData.map(item => ({
...item,
'价格趋势': item.change24h.startsWith('+')? '↑' : '↓'
})));

5. 生命周期标记

1
2
3
4
5
6
7
8
// 使用 console.groupCollapsed 和 console.groupEnd 来创建一个可折叠的分组,
// 用于标记 API 请求流程的不同阶段,如请求发起、响应接收、数据处理等,每个阶段用不同颜色突出显示关键信息,
// 方便在控制台快速了解整个请求生命周期的状态。
console.groupCollapsed('%c🔄 API请求流程追踪', 'color:#4299e1');
console.log('%c▶️ 请求发起', 'color:#48bb78', { url: '/api/users', method: 'GET' });
console.log('%c⏳ 响应接收', 'color:#ed8936', { status: 200, time: '320ms' });
console.log('%c✅ 数据处理', 'color:#48bb78', { processedItems: 15 });
console.groupEnd();

6. 调用栈快照

1
2
3
4
5
6
7
// 在函数内部使用 console.trace,当执行到这一行时,会输出完整的调用栈信息,
// 显示函数是如何被调用的,经过了哪些路径,对于排查函数调用逻辑和定位问题根源非常有用。
function processOrder() {
console.trace('订单处理流程追踪');
// 其他处理逻辑
}
// 输出完整的调用栈信息

7. 内存监控

1
2
3
4
5
6
// 首先获取初始的 JavaScript 堆内存使用量,执行一些内存密集型操作后,
// 通过计算前后内存使用量的差值并转换为 KB 单位,输出内存使用量的变化情况,以便监控内存消耗是否合理。
const initialMemory = performance.memory.usedJSHeapSize;
// 执行内存密集型操作
console.log('内存使用量变化:',
(performance.memory.usedJSHeapSize - initialMemory) / 1024 + ' KB');

8. 复合性能测试

1
2
3
4
5
6
7
8
9
10
// 使用 console.time 和 console.timeEnd 来测量一段包含多个异步操作(如 Promise.all 中的多个异步任务)的复合操作的执行时间,
// 同时使用 console.timeLog 在操作的关键阶段记录时间点,方便了解各个阶段的耗时情况,精准定位性能瓶颈。
console.time('复合操作');
await Promise.all([
console.timeLog('复合操作', '阶段1完成'),
fetchData(),
console.timeLog('复合操作', '阶段2完成'),
processData()
]);
console.timeEnd('复合操作');

9. 条件断言

1
2
3
4
5
6
7
8
// 使用 console.assert 进行条件断言,如果条件不满足(这里是用户年龄大于 18 岁),
// 则按照指定的红色醒目的样式输出错误信息,包含期望年龄和实际年龄,用于快速验证数据的合法性。
console.assert(
user.age > 18,
'%c年龄验证失败',
'color:red;font-weight:bold',
{ requiredAge: 18, actualAge: user.age }
);

10. 环境感知日志

1
2
3
4
5
6
7
8
9
10
// 根据当前 Node.js 环境变量(NODE_ENV)来决定是否输出日志,
// 在开发环境下,将 console.log 绑定并正常输出日志,而在生产环境下,
// 提供一个空函数替代,实现自动静默,避免在生产环境输出不必要的调试信息。
const debug = {
log: process.env.NODE_ENV === 'development'
? console.log.bind(console)
: () => {}
};

debug.log('开发环境专属日志'); // 生产环境自动静默

11. 数据快照对比

1
2
3
4
5
// 先将状态数据序列化为字符串保存起来,在状态修改后,通过自定义的 diff 函数(假设已定义)对比修改前后的数据,
// 并在控制台输出状态变更差异,有助于跟踪数据的变化情况,排查因数据变更引发的问题。
const before = JSON.stringify(state);
// 状态修改操作
console.log('状态变更差异:', diff(JSON.parse(before), state));

12. 日志分类过滤

1
2
3
4
5
6
7
8
9
10
// 创建一个日志记录器对象,包含不同分类的日志方法,如网络、安全、支付等,
// 每个方法在调用 console.log 时添加对应的分类前缀,方便在控制台根据分类筛选和查看日志,
// 这里演示了记录支付请求发起的日志,包含金额信息。
const logger = {
network: (...args) => console.log('[NET]',...args),
security: (...args) => console.log('[SEC]',...args),
payment: (...args) => console.log('[PAY]',...args)
};

logger.payment('支付请求发起', { amount: 99.9 });

13. 交互式调试

1
2
3
4
5
6
// 在控制台输出一个包含加密数据和检查函数的对象,用户点击该输出信息时,
// 可以通过调用检查函数(这里是解密数据函数 decryptData)来查看详细信息,实现交互式调试体验。
console.log('点击查看详情:', {
data: encryptedData,
inspect: () => decryptData(encryptedData)
});

14. 日志持久化

1
2
3
4
5
6
7
8
// 保存原始的 console.log 方法,并重写 console.log,
// 在新的 console.log 中,先执行原始的输出操作,再将日志信息发送到日志服务器(假设 sendToLogServer 函数已实现),
// 确保日志不仅在控制台可见,还能持久化存储以供后续分析。
const originalLog = console.log.bind(console);
console.log = (...args) => {
originalLog(...args);
sendToLogServer(args); // 发送到日志服务器
};

15. 性能热力图

1
2
3
4
5
6
7
// 使用 console.timeStamp 在关键的性能节点(如渲染开始、DOM 更新)记录时间戳,
// 之后可以在浏览器的 Performance 面板查看这些时间戳,以可视化的方式分析页面性能瓶颈,优化渲染流程。
console.timeStamp('渲染开始');
// 渲染逻辑
console.timeStamp('DOM更新');
// 更新逻辑
// 在浏览器Performance面板查看时间戳

16. 内存泄漏检测

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 创建一个数组来存储组件实例的引用,在组件构造函数中,每创建一个实例就将其添加到数组中,
// 并通过 console.count 记录实例创建的次数,同时定期检查数组中实例的数量,
// 如果数量持续增加,可能存在内存泄漏问题,方便及时排查。
let instances = [];

class MyComponent {
constructor() {
instances.push(this);
console.count('实例创建');
}
}

// 定期检查实例数量
setInterval(() => console.log('存活实例:', instances.length), 5000);

17. 源码映射调试

1
2
3
4
// 在 webpack 配置中启用 source-map,这使得在调试时能够将打包后的代码映射回原始的源代码,
// 方便在浏览器调试工具或 IDE 中直接查看和调试原始代码,而不是混淆后的代码,提高调试效率。
// 配合webpack配置
devtool: 'source-map',

18. 调试器集成

1
2
3
4
5
6
7
8
// 在函数开头使用 console.log 输出进入函数的信息,然后使用 debugger 语句,
// 当代码执行到这里时,会暂停执行,等待调试器(如浏览器开发者工具或 IDE 调试功能)连接,
// 方便深入调试函数内部复杂的运算逻辑。
function complexCalculation() {
console.log('进入计算函数');
debugger; // 配合IDE断点使用
// 复杂运算
}

19. 日志分级控制

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// 定义一个日志级别对象,包含调试、信息、警告、错误等级别,
// 然后创建一个 Logger 类,在构造函数中接收一个日志级别参数,
// 类中的 debug 方法根据当前设置的级别决定是否输出调试信息,实现灵活的日志分级管理。
const LOG_LEVEL = {
DEBUG: 0,
INFO: 1,
WARN: 2,
ERROR: 3
};

class Logger {
constructor(level = LOG_LEVEL.INFO) {
this.level = level;
}

debug(...args) {
if (this.level <= LOG_LEVEL.DEBUG) console.log('[DEBUG]',...args);
}
}

20. 敏感信息过滤

1
2
3
4
5
6
7
8
9
10
11
// 定义一个安全日志函数,它接收数据对象,先深度克隆数据,
// 然后删除可能包含敏感信息的字段(如密码、令牌),最后返回过滤后的安全数据,
// 并在控制台输出,确保敏感信息不会意外泄露。
const safeLog = (data) => {
const filtered = _.cloneDeep(data);
delete filtered.password;
delete filtered.token;
return filtered;
};

console.log('用户数据:', safeLog(userData));

注意:所有调试代码应在开发环境使用,生产环境需通过构建工具自动移除。