调试是开发中不可或缺的技能。掌握调试工具和技巧,能大大提高问题定位和解决的效率。
🎯 Console 方法#
基础输出#
// 普通日志console.log('普通信息')
// 信息console.info('提示信息')
// 警告console.warn('警告信息')
// 错误console.error('错误信息')
// 调试(某些环境可能被过滤)console.debug('调试信息')格式化输出#
// 字符串替换console.log('用户 %s 的年龄是 %d', '张三', 25)// 用户 张三 的年龄是 25
// %s - 字符串// %d / %i - 整数// %f - 浮点数// %o - 对象// %O - 对象��可展开)// %c - CSS 样式
// 样式化输出console.log('%c重要信息', 'color: red; font-size: 20px; font-weight: bold;')
// 多种样式console.log( '%c成功 %c警告 %c错误', 'color: green', 'color: orange', 'color: red')对象和数组#
const user = { name: '张三', age: 25 }const users = [ { name: '张三', age: 25 }, { name: '李四', age: 30 },]
// 直接输出console.log(user)
// 以目录形式展示console.dir(user)
// 表格形式(数组/对象)console.table(users)console.table(users, ['name']) // 只显示 name 列分组#
// 分组console.group('用户信息')console.log('姓名: 张三')console.log('年龄: 25')console.groupEnd()
// 默认折叠的分组console.groupCollapsed('详细日志')console.log('详细信息1')console.log('详细信息2')console.groupEnd()
// 嵌套分组console.group('外层')console.log('外层信息')console.group('内层')console.log('内层信息')console.groupEnd()console.groupEnd()计时#
// 计时器console.time('操作耗时')// 执行一些操作for (let i = 0; i < 1000000; i++) {}console.timeEnd('操作耗时')// 操作耗时: 2.123ms
// 时间戳console.timeLog('操作耗时', '中间状态')// 操作耗时: 1.234ms 中间状态
// 时间戳(毫秒)console.timeStamp('标记点') // 在 Performance 面板显示计数#
function process(item) { console.count('process 被调用') // ...}
process('a') // process 被调用: 1process('b') // process 被调用: 2process('c') // process 被调用: 3
// 带标签的计数console.count('标签A') // 标签A: 1console.count('标签B') // 标签B: 1console.count('标签A') // 标签A: 2
// 重置计数console.countReset('标签A')console.count('标签A') // 标签A: 1断言#
// 条件为 false 时输出错误console.assert(1 === 1, '这不会显示')console.assert(1 === 2, '1 不等于 2')// Assertion failed: 1 不等于 2
// 用于参数验证function greet(name) { console.assert(typeof name === 'string', 'name 必须是字符串') console.log(`你好,${name}`)}调用栈#
function first() { second()}
function second() { third()}
function third() { console.trace('调用栈')}
first()// 输出:// third// second// first// (anonymous)清空控制台#
console.clear()断点调试#
debugger 语句#
function calculateTotal(items) { debugger // 代码执行到此处暂停
let total = 0 for (const item of items) { total += item.price * item.quantity } return total}条件断点#
在 DevTools 中右键行号设置:
// 设置条件:i === 50for (let i = 0; i < 100; i++) { process(i) // 只在 i === 50 时暂停}日志断点#
在 DevTools 中右键行号添加日志点,无需修改代码:
// 添加日志点:`当前值: ${value}`function process(value) { return value * 2}监视表达式#
在 Sources 面板的 Watch 区域添加:
// 监视以下表达式user.nameitems.lengthtotal > 100网络调试#
查看请求#
// 在 Network 面板查看fetch('/api/users') .then((response) => response.json()) .then((data) => console.log(data))模拟网络状况#
- Network 面板 → Throttling
- 选择 Slow 3G / Offline 等
复制请求#
- 右键请求 → Copy → Copy as fetch
- 可以直接在控制台粘贴重放
性能分析#
console.time#
console.time('数组操作')
const arr = []for (let i = 0; i < 100000; i++) { arr.push(i)}
console.timeEnd('数组操作')Performance 面板#
// 开始记录console.profile('性能分析')
// 执行代码heavyOperation()
// 结束记录console.profileEnd('性能分析')performance API#
// 性能标记performance.mark('start')
// 执行操作doSomething()
performance.mark('end')
// 测量performance.measure('操作耗时', 'start', 'end')
// 获取测量���果const measures = performance.getEntriesByName('操作耗时')console.log(measures[0].duration)
// 清除performance.clearMarks()performance.clearMeasures()内存分析#
// 查看内存使用console.log(performance.memory)// {// usedJSHeapSize: 10000000,// totalJSHeapSize: 35000000,// jsHeapSizeLimit: 2172649472// }
// Memory 面板:// - Heap snapshot:堆快照// - Allocation instrumentation:分配时间线// - Allocation sampling:分配采样调试技巧#
快速定位元素#
// 在 Elements 面板选中元素后$0 // 当前选中的元素$1 // 上一个选中的元素;($2, $3, $4) // 更早选中的元素
// 快捷选择器$('selector') // document.querySelector$$('selector') // document.querySelectorAll监控元素变化#
// 在 Elements 面板右键元素// - Break on subtree modifications// - Break on attribute modifications// - Break on node removal监控函数调用#
// 监控函数被调用function add(a, b) { return a + b}
monitor(add)add(1, 2)// function add called with arguments: 1, 2
unmonitor(add)监控事件#
// 监控元素的所有事件monitorEvents(document.body)
// 监控特定事件monitorEvents(document.body, 'click')monitorEvents(document.body, ['click', 'keydown'])
// 停止监控unmonitorEvents(document.body)获取事件监听器#
// 获取元素的所有事件监听器getEventListeners(document.body)// {click: Array(2), scroll: Array(1), ...}复制到剪贴板#
// 复制对象到剪贴板copy({ name: '张三', age: 25 })// 然后可以粘贴到其他地方查看对象结构#
// 在控制台输入对象名,按 Tab 查看属性// 或使用 keys/valueskeys(document.body)values(document.body)调试模式#
Source Maps#
// 在打包工具中启用 source mapmodule.exports = { devtool: 'source-map', // 开发环境 // devtool: 'hidden-source-map', // 生产环境}
// 然后可以在 DevTools 中调试原始代码条件日志#
// 开发环境日志const isDev = process.env.NODE_ENV === 'development'
const log = { info: (...args) => isDev && console.log(...args), warn: (...args) => isDev && console.warn(...args), error: (...args) => console.error(...args), // 错误总是显示}
log.info('开发信息') // 只在开发环境显示日志级别#
const LogLevel = { DEBUG: 0, INFO: 1, WARN: 2, ERROR: 3,}
class Logger { constructor(level = LogLevel.INFO) { this.level = level }
debug(...args) { if (this.level <= LogLevel.DEBUG) { console.debug('[DEBUG]', ...args) } }
info(...args) { if (this.level <= LogLevel.INFO) { console.info('[INFO]', ...args) } }
warn(...args) { if (this.level <= LogLevel.WARN) { console.warn('[WARN]', ...args) } }
error(...args) { console.error('[ERROR]', ...args) }}
const logger = new Logger(LogLevel.DEBUG)logger.debug('调试信息')logger.info('普通信息')常见问题调试#
���步问题#
// 使用 async/await 更容易调试async function fetchData() { console.log('开始请求') const response = await fetch('/api/data') console.log('响应状态:', response.status) const data = await response.json() console.log('数据:', data) return data}
// Promise 链中的调试fetch('/api/data') .then((response) => { console.log('响应:', response) return response.json() }) .then((data) => { console.log('数据:', data) return data }) .catch((error) => { console.error('错误:', error) })this 问题#
const obj = { name: '对象', method() { console.log('method this:', this)
setTimeout(function () { console.log('setTimeout this:', this) }, 100)
setTimeout(() => { console.log('箭头函数 this:', this) }, 100) },}
obj.method()作用域问题#
for (var i = 0; i < 3; i++) { setTimeout(() => { console.log('var i:', i) // 3, 3, 3 }, 100)}
for (let j = 0; j < 3; j++) { setTimeout(() => { console.log('let j:', j) // 0, 1, 2 }, 100)}总结#
| 方法 | 用途 |
|---|---|
| console.log | 普通日志 |
| console.table | 表格显示数组/对象 |
| console.time | 计时 |
| console.trace | 显示调用栈 |
| console.assert | 断言 |
| console.group | 分组 |
| debugger | 代码断点 |
| 工具 | 功能 |
|---|---|
| Elements | DOM 和样式调试 |
| Console | 日志和交互 |
| Sources | 代码和断点 |
| Network | 网络请求 |
| Performance | 性能分析 |
| Memory | 内存分析 |
核心要点:
- 善用 console 的各种方法
- 断点比 console.log 更高效
- 条件断点和日志断点很有用
- 使用 Source Maps 调试原始代码
- 开发环境启用详细日志,生产环境关闭