名称: 移动调试 描述: 在移动设备上远程访问JavaScript控制台和调试。用于调试手机/平板上的网页、在没有桌面开发者工具的情况下访问控制台错误、在真实设备上测试响应式设计或诊断移动特定问题。涵盖Eruda、vConsole、Chrome/Safari远程调试和云测试平台。
移动调试方法论
在移动设备上访问JavaScript控制台和调试网页的模式,无需传统桌面开发者工具。
快速开始:在任何页面注入控制台
Eruda书签工具(推荐)
在移动浏览器中添加此书签,然后在任何页面点击它:
javascript:(function(){var script=document.createElement('script');script.src='https://cdn.jsdelivr.net/npm/eruda';document.body.append(script);script.onload=function(){eruda.init();}})();
vConsole书签工具
javascript:(function(){var script=document.createElement('script');script.src='https://unpkg.com/vconsole@latest/dist/vconsole.min.js';document.body.append(script);script.onload=function(){new VConsole();}})();
页面内控制台工具
Eruda设置
Eruda在浮动面板中提供完整的开发者工具类体验。
<!-- CDN(仅开发) -->
<script src="https://cdn.jsdelivr.net/npm/eruda"></script>
<script>eruda.init();</script>
<!-- 条件加载(生产环境推荐) -->
<script>
(function() {
var src = 'https://cdn.jsdelivr.net/npm/eruda';
// 仅当?eruda=true或localStorage标志设置时加载
if (!/eruda=true/.test(window.location) &&
localStorage.getItem('active-eruda') !== 'true') return;
var script = document.createElement('script');
script.src = src;
script.onload = function() { eruda.init(); };
document.body.appendChild(script);
})();
</script>
// NPM安装
// npm install eruda --save-dev
import eruda from 'eruda';
// 使用选项初始化
eruda.init({
container: document.getElementById('eruda-container'),
tool: ['console', 'elements', 'network', 'resources', 'info'],
useShadowDom: true,
autoScale: true
});
// 添加自定义按钮
eruda.add({
name: '清除存储',
init($el) {
$el.html('<button>清除所有存储</button>');
$el.find('button').on('click', () => {
localStorage.clear();
sessionStorage.clear();
console.log('存储已清除');
});
}
});
// 完成后移除
eruda.destroy();
Eruda特性:
- 控制台(日志、错误、警告)
- 元素(DOM检查器)
- 网络(XHR/fetch请求)
- 资源(localStorage、cookies、sessionStorage)
- 源(页面源代码)
- 信息(页面/设备信息)
- 片段(保存的代码片段)
vConsole设置
更轻量级的替代方案,微信调试的官方工具。
<!-- CDN -->
<script src="https://unpkg.com/vconsole@latest/dist/vconsole.min.js"></script>
<script>
var vConsole = new VConsole();
</script>
// NPM
// npm install vconsole
import VConsole from 'vconsole';
// 使用选项初始化
const vConsole = new VConsole({
theme: 'dark',
onReady: function() {
console.log('vConsole已就绪');
},
log: {
maxLogNumber: 1000
}
});
// 动态配置
vConsole.setOption('log.maxLogNumber', 5000);
// 完成后销毁
vConsole.destroy();
vConsole特性:
- 日志面板(console.log、info、warn、error)
- 系统面板(设备信息)
- 网络面板(XHR、fetch)
- 元素面板(DOM树)
- 存储面板(cookies、localStorage)
对比:Eruda vs vConsole
| 特性 | Eruda | vConsole |
|---|---|---|
| 大小 | ~100KB | ~85KB |
| DOM编辑 | 是 | 仅查看 |
| 网络详情 | 完整 | 基本 |
| 插件系统 | 是 | 是 |
| 暗色主题 | 通过插件 | 内置 |
| 最适合 | 完整调试 | 快速日志记录 |
原生远程调试
Chrome开发者工具(Android)
# 1. 在Android上启用USB调试
# 设置 → 开发者选项 → USB调试 = 开启
# 2. 通过USB连接到计算机
# 3. 在计算机上打开Chrome,导航到:
# chrome://inspect#devices
# 4. 启用“发现USB设备”
# 5. 在Android设备上接受调试提示
# 6. 点击要调试的页面旁边的“检查”
本地主机的端口转发:
# 在chrome://inspect中,点击“端口转发”
# 添加:localhost:3000 → localhost:3000
# 现在Android Chrome可以在localhost:3000访问开发服务器
Safari网页检查器(iOS)
# 1. 在iPhone/iPad上:
# 设置 → Safari → 高级 → 网页检查器 = 开启
# 2. 在Mac上:
# Safari → 偏好设置 → 高级 → “显示开发菜单” = 开启
# 3. 通过USB连接设备(或启用Wi-Fi调试)
# 4. 在Mac上打开Safari:
# 开发 → [设备名称] → [要调试的页面]
# 无线调试(初始USB设置后):
# 开发 → [设备] → 通过网络连接
Firefox远程调试(Android)
# 1. 在Android Firefox上:
# 设置 → 高级 → 远程调试 = 开启
# 2. 在桌面Firefox上:
# 打开about:debugging
# 3. 通过USB连接Android
# 4. 在about:debugging中启用USB设备
# 5. 点击设备旁边的“连接”
iOS调试无需Mac
使用ios-webkit-debug-proxy
# 在Windows上安装(通过Scoop)
scoop bucket add extras
scoop install ios-webkit-debug-proxy
# 在Linux上安装
sudo apt-get install ios-webkit-debug-proxy
# 在Mac上安装
brew install ios-webkit-debug-proxy
# 运行代理
ios_webkit_debug_proxy -f chrome-devtools://devtools/bundled/inspector.html
# 连接到http://localhost:9221查看连接的设备
商业工具:Inspect.dev
Inspect.dev从Windows/Linux提供iOS调试,具有熟悉的开发者工具界面。
# 从https://inspect.dev/下载
# 1. 安装应用程序
# 2. 通过USB连接iOS设备
# 3. 在iOS上启用网页检查器
# 4. Inspect.dev自动检测页面
# 5. 点击打开开发者工具界面
云测试平台
LambdaTest(免费增值)
# LambdaTest提供带控制台访问的真实设备云
# 免费层:100分钟/月
import requests
# LambdaTest REST API用于自动化
LAMBDATEST_API = "https://api.lambdatest.com/automation/api/v1"
# 对于手动测试:
# 1. 访问https://www.lambdatest.com/
# 2. 选择设备/浏览器
# 3. 输入URL
# 4. 工具栏中提供开发者工具
# Selenium/Playwright集成用于自动控制台捕获
from playwright.sync_api import sync_playwright
def test_on_lambdatest():
with sync_playwright() as p:
# 连接到LambdaTest
browser = p.chromium.connect(
f"wss://cdp.lambdatest.com/playwright?capabilities="
f"{{\"browserName\":\"Chrome\",\"platform\":\"android\"}}"
)
page = browser.new_page()
# 捕获控制台日志
logs = []
page.on('console', lambda msg: logs.append(msg.text()))
page.goto('https://example.com')
browser.close()
return logs
BrowserStack
# BrowserStack:$29+/月,10,000+真实设备
from selenium import webdriver
from selenium.webdriver.common.desired_capabilities import DesiredCapabilities
def get_browserstack_driver():
"""创建带控制台日志记录的BrowserStack WebDriver。"""
capabilities = {
'browserName': 'chrome',
'device': 'Samsung Galaxy S21',
'realMobile': 'true',
'os_version': '11.0',
'browserstack.console': 'verbose', # 捕获控制台日志
'browserstack.networkLogs': 'true',
'browserstack.user': 'YOUR_USERNAME',
'browserstack.key': 'YOUR_KEY'
}
driver = webdriver.Remote(
command_executor='https://hub-cloud.browserstack.com/wd/hub',
desired_capabilities=capabilities
)
return driver
# 测试后,从BrowserStack仪表板或API检索日志
编程式控制台捕获
Playwright控制台捕获
const { chromium, devices } = require('playwright');
async function captureConsoleLogs(url) {
const browser = await chromium.launch();
// 模拟移动设备
const context = await browser.newContext({
...devices['iPhone 13']
});
const page = await context.newPage();
// 捕获所有控制台消息
const logs = [];
page.on('console', msg => {
logs.push({
type: msg.type(),
text: msg.text(),
location: msg.location(),
timestamp: new Date().toISOString()
});
});
// 捕获页面错误
const errors = [];
page.on('pageerror', error => {
errors.push({
message: error.message,
stack: error.stack,
timestamp: new Date().toISOString()
});
});
// 捕获失败请求
const failedRequests = [];
page.on('requestfailed', request => {
failedRequests.push({
url: request.url(),
failure: request.failure().errorText,
timestamp: new Date().toISOString()
});
});
await page.goto(url);
await page.waitForLoadState('networkidle');
await browser.close();
return { logs, errors, failedRequests };
}
// 用法
captureConsoleLogs('https://example.com')
.then(result => console.log(JSON.stringify(result, null, 2)));
Puppeteer控制台捕获
const puppeteer = require('puppeteer');
async function debugMobilePage(url) {
const browser = await puppeteer.launch();
const page = await browser.newPage();
// 设置移动视口
await page.setViewport({
width: 375,
height: 812,
isMobile: true,
hasTouch: true
});
// 移动用户代理
await page.setUserAgent(
'Mozilla/5.0 (iPhone; CPU iPhone OS 14_0 like Mac OS X) ' +
'AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.0 Mobile/15E148 Safari/604.1'
);
// 控制台捕获带完整详情
page.on('console', async msg => {
const args = await Promise.all(
msg.args().map(arg => arg.jsonValue().catch(() => arg.toString()))
);
console.log(`[${msg.type().toUpperCase()}]`, ...args);
// 获取源位置
const location = msg.location();
if (location.url) {
console.log(` 位于 ${location.url}:${location.lineNumber}`);
}
});
// 未处理的承诺拒绝
page.on('pageerror', err => {
console.error('[页面错误]', err.message);
});
await page.goto(url, { waitUntil: 'networkidle0' });
// 执行JavaScript并捕获结果
const result = await page.evaluate(() => {
// 检查常见移动问题
return {
viewportWidth: window.innerWidth,
devicePixelRatio: window.devicePixelRatio,
touchSupport: 'ontouchstart' in window,
errors: window.__capturedErrors || []
};
});
console.log('页面信息:', result);
await browser.close();
}
错误监控服务
Sentry集成
// npm install @sentry/browser
import * as Sentry from '@sentry/browser';
Sentry.init({
dsn: 'YOUR_SENTRY_DSN',
environment: 'production',
// 捕获console.error
integrations: [
new Sentry.BrowserTracing(),
new Sentry.Replay() // 用于调试的会话回放
],
// 采样率
tracesSampleRate: 0.1,
replaysSessionSampleRate: 0.1,
replaysOnErrorSampleRate: 1.0,
beforeSend(event) {
// 过滤或修改事件
return event;
}
});
// 手动错误捕获
try {
riskyOperation();
} catch (error) {
Sentry.captureException(error);
}
// 添加上下文
Sentry.setUser({ id: 'user123' });
Sentry.setTag('page', 'checkout');
LogRocket用于会话回放
// npm install logrocket
import LogRocket from 'logrocket';
LogRocket.init('your-app/your-project');
// 识别用户
LogRocket.identify('user123', {
name: 'Test User',
email: 'user@example.com'
});
// 控制台日志自动捕获
console.log('此内容出现在LogRocket中');
// 手动日志记录
LogRocket.log('自定义事件', { data: 'value' });
// 跟踪错误
LogRocket.captureException(new Error('出现错误'));
Android屏幕镜像与Scrcpy
# 安装scrcpy
# Windows: scoop install scrcpy
# Mac: brew install scrcpy
# Linux: apt install scrcpy
# 基本镜像
scrcpy
# 带特定选项
scrcpy --max-size 1024 --bit-rate 2M
# 无线连接(初始USB后)
adb tcpip 5555
adb connect <设备IP>:5555
scrcpy
# 录制会话
scrcpy --record session.mp4
# 镜像时关闭设备屏幕
scrcpy --turn-screen-off
移动调试工作流
┌─────────────────────────────────────────────────────────────────┐
│ 移动调试决策树 │
├─────────────────────────────────────────────────────────────────┤
│ │
│ 问:您是否有物理访问设备? │
│ │ │
│ ├─ 是:能否通过USB连接? │
│ │ │ │
│ │ ├─ Android:使用Chrome开发者工具远程 │
│ │ │ chrome://inspect#devices │
│ │ │ │
│ │ └─ iOS:有Mac吗? │
│ │ │ │
│ │ ├─ 是:使用Safari网页检查器 │
│ │ │ │
│ │ └─ 否:使用Inspect.dev或 │
│ │ ios-webkit-debug-proxy │
│ │ │
│ └─ 无USB:通过书签工具注入Eruda/vConsole │
│ │
│ 问:远程/生产调试? │
│ │ │
│ ├─ 添加条件Eruda加载 │
│ │ (?eruda=true参数) │
│ │ │
│ └─ 设置Sentry/LogRocket进行错误监控 │
│ │
│ 问:自动化测试? │
│ │ │
│ ├─ 使用移动模拟的Playwright/Puppeteer │
│ │ │
│ └─ 云平台(LambdaTest、BrowserStack) │
│ │
└─────────────────────────────────────────────────────────────────┘
常见移动调试问题
触摸事件未触发
// 检查触摸事件是否支持
eruda.init();
console.log('触摸支持:', 'ontouchstart' in window);
console.log('指针事件:', 'onpointerdown' in window);
// 调试触摸事件
document.addEventListener('touchstart', e => {
console.log('触摸开始', e.touches.length, '个触摸点');
}, { passive: true });
document.addEventListener('click', e => {
console.log('点击位于', e.clientX, e.clientY);
});
视口问题
// 记录视口信息
console.log('视口:', {
innerWidth: window.innerWidth,
innerHeight: window.innerHeight,
outerWidth: window.outerWidth,
outerHeight: window.outerHeight,
devicePixelRatio: window.devicePixelRatio,
orientation: screen.orientation?.type
});
// 检查meta视口
const viewport = document.querySelector('meta[name="viewport"]');
console.log('视口meta:', viewport?.content);
性能调试
// 检查性能计时
const perf = performance.getEntriesByType('navigation')[0];
console.log('页面加载计时:', {
dns: perf.domainLookupEnd - perf.domainLookupStart,
tcp: perf.connectEnd - perf.connectStart,
request: perf.responseStart - perf.requestStart,
response: perf.responseEnd - perf.responseStart,
domParsing: perf.domInteractive - perf.responseEnd,
domComplete: perf.domComplete - perf.domInteractive,
total: perf.loadEventEnd - perf.navigationStart
});
// 检查内存(仅Chrome)
if (performance.memory) {
console.log('内存:', {
usedJSHeapSize: (performance.memory.usedJSHeapSize / 1048576).toFixed(2) + ' MB',
totalJSHeapSize: (performance.memory.totalJSHeapSize / 1048576).toFixed(2) + ' MB'
});
}
平台对比
| 工具 | 成本 | 平台 | 设置难度 | 最适合 |
|---|---|---|---|---|
| Eruda | 免费 | 所有浏览器 | 简单(书签工具) | 快速调试 |
| vConsole | 免费 | 所有浏览器 | 简单 | 微信应用 |
| Chrome远程 | 免费 | 仅Android | 中等 | 完整开发者工具 |
| Safari检查器 | 免费 | 仅iOS | 简单(需要Mac) | 完整开发者工具 |
| Inspect.dev | 付费 | 从任何操作系统iOS | 简单 | 无需Mac的iOS |
| LambdaTest | 免费增值 | 所有 | 简单 | 云测试 |
| BrowserStack | 付费 | 所有 | 简单 | 真实设备 |
| Sentry | 免费增值 | 所有 | 中等 | 错误监控 |