Skip to content

调试技巧

调试是开发中不可或缺的技能。掌握调试工具和技巧,能大大提高问题定位和解决的效率。

🎯 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 被调用: 1
process('b') // process 被调用: 2
process('c') // process 被调用: 3
// 带标签的计数
console.count('标签A') // 标签A: 1
console.count('标签B') // 标签B: 1
console.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 === 50
for (let i = 0; i < 100; i++) {
process(i) // 只在 i === 50 时暂停
}

日志断点#

在 DevTools 中右键行号添加日志点,无需修改代码:

// 添加日志点:`当前值: ${value}`
function process(value) {
return value * 2
}

监视表达式#

在 Sources 面板的 Watch 区域添加:

// 监视以下表达式
user.name
items.length
total > 100

网络调试#

查看请求#

// 在 Network 面板查看
fetch('/api/users')
.then((response) => response.json())
.then((data) => console.log(data))

模拟网络状况#

复制请求#

性能分析#

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/values
keys(document.body)
values(document.body)

调试模式#

Source Maps#

webpack.config.js
// 在打包工具中启用 source map
module.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代码断点
工具功能
ElementsDOM 和样式调试
Console日志和交互
Sources代码和断点
Network网络请求
Performance性能分析
Memory内存分析

核心要点