mobile-debugging
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseMobile debugging methodology
移动端调试方法论
Patterns for accessing JavaScript console and debugging web pages on mobile devices without traditional desktop DevTools.
无需传统桌面DevTools即可在移动端设备上访问JavaScript控制台并调试网页的方案。
Quick-start: Inject console on any page
快速上手:在任意页面注入控制台
Eruda bookmarklet (recommended)
Eruda书签(推荐)
Add this as a bookmark on your mobile browser, then tap it on any page:
javascript
javascript:(function(){var script=document.createElement('script');script.src='https://cdn.jsdelivr.net/npm/eruda';document.body.append(script);script.onload=function(){eruda.init();}})();在移动端浏览器中将以下代码添加为书签,然后在任意页面点击该书签:
javascript
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 bookmarklet
vConsole书签
javascript
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();}})();javascript
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();}})();In-page console tools
页面内控制台工具
Eruda setup
Eruda配置
Eruda provides a full DevTools-like experience in a floating panel.
html
<!-- CDN (development only) -->
<script src="https://cdn.jsdelivr.net/npm/eruda"></script>
<script>eruda.init();</script>
<!-- Conditional loading (recommended for production) -->
<script>
(function() {
var src = 'https://cdn.jsdelivr.net/npm/eruda';
// Only load when ?eruda=true or localStorage flag set
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>javascript
// NPM installation
// npm install eruda --save-dev
import eruda from 'eruda';
// Initialize with options
eruda.init({
container: document.getElementById('eruda-container'),
tool: ['console', 'elements', 'network', 'resources', 'info'],
useShadowDom: true,
autoScale: true
});
// Add custom buttons
eruda.add({
name: 'Clear Storage',
init($el) {
$el.html('<button>Clear All Storage</button>');
$el.find('button').on('click', () => {
localStorage.clear();
sessionStorage.clear();
console.log('Storage cleared');
});
}
});
// Remove when done
eruda.destroy();Eruda features:
- Console (logs, errors, warnings)
- Elements (DOM inspector)
- Network (XHR/fetch requests)
- Resources (localStorage, cookies, sessionStorage)
- Sources (page source code)
- Info (page/device information)
- Snippets (saved code snippets)
Eruda在浮动面板中提供类DevTools的完整调试体验。
html
<!-- CDN(仅开发环境使用) -->
<script src="https://cdn.jsdelivr.net/npm/eruda"></script>
<script>eruda.init();</script>
<!-- 条件加载(生产环境推荐) -->
<script>
(function() {
var src = 'https://cdn.jsdelivr.net/npm/eruda';
// 仅当URL包含?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>javascript
// 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: 'Clear Storage',
init($el) {
$el.html('<button>Clear All Storage</button>');
$el.find('button').on('click', () => {
localStorage.clear();
sessionStorage.clear();
console.log('Storage cleared');
});
}
});
// 使用完成后销毁
eruda.destroy();Eruda功能特性:
- 控制台(日志、错误、警告)
- 元素(DOM检查器)
- 网络(XHR/fetch请求)
- 资源(localStorage、Cookie、sessionStorage)
- 源代码(页面源码)
- 信息(页面/设备信息)
- 代码片段(保存的代码片段)
vConsole setup
vConsole配置
Lighter weight alternative, official tool for WeChat debugging.
html
<!-- CDN -->
<script src="https://unpkg.com/vconsole@latest/dist/vconsole.min.js"></script>
<script>
var vConsole = new VConsole();
</script>javascript
// NPM
// npm install vconsole
import VConsole from 'vconsole';
// Initialize with options
const vConsole = new VConsole({
theme: 'dark',
onReady: function() {
console.log('vConsole is ready');
},
log: {
maxLogNumber: 1000
}
});
// Dynamic configuration
vConsole.setOption('log.maxLogNumber', 5000);
// Destroy when done
vConsole.destroy();vConsole features:
- Log panel (console.log, info, warn, error)
- System panel (device info)
- Network panel (XHR, fetch)
- Element panel (DOM tree)
- Storage panel (cookies, localStorage)
轻量级替代方案,微信调试官方工具。
html
<!-- CDN -->
<script src="https://unpkg.com/vconsole@latest/dist/vconsole.min.js"></script>
<script>
var vConsole = new VConsole();
</script>javascript
// NPM安装
// npm install vconsole
import VConsole from 'vconsole';
// 带选项初始化
const vConsole = new VConsole({
theme: 'dark',
onReady: function() {
console.log('vConsole is ready');
},
log: {
maxLogNumber: 1000
}
});
// 动态配置
vConsole.setOption('log.maxLogNumber', 5000);
// 使用完成后销毁
vConsole.destroy();vConsole功能特性:
- 日志面板(console.log、info、warn、error)
- 系统面板(设备信息)
- 网络面板(XHR、fetch)
- 元素面板(DOM树)
- 存储面板(Cookie、localStorage)
Comparison: Eruda vs vConsole
对比:Eruda vs vConsole
| Feature | Eruda | vConsole |
|---|---|---|
| Size | ~100KB | ~85KB |
| DOM Editing | Yes | View only |
| Network Details | Full | Basic |
| Plugin System | Yes | Yes |
| Dark Theme | Via plugin | Built-in |
| Best For | Full debugging | Quick logging |
| 功能特性 | Eruda | vConsole |
|---|---|---|
| 体积 | ~100KB | ~85KB |
| DOM编辑 | 支持 | 仅查看 |
| 网络详情 | 完整 | 基础 |
| 插件系统 | 支持 | 支持 |
| 深色主题 | 通过插件 | 内置 |
| 最佳适用场景 | 完整调试 | 快速日志查看 |
Native remote debugging
原生远程调试
Chrome DevTools (Android)
Chrome DevTools(Android)
bash
undefinedbash
undefined1. Enable USB debugging on Android
1. 在Android设备上启用USB调试
Settings → Developer Options → USB Debugging = ON
设置 → 开发者选项 → USB调试 = 开启
2. Connect via USB to computer
2. 通过USB连接到电脑
3. Open Chrome on computer, navigate to:
3. 在电脑上打开Chrome,访问:
chrome://inspect#devices
chrome://inspect#devices
4. Enable "Discover USB devices"
4. 启用“发现USB设备”
5. Accept debugging prompt on Android device
5. 在Android设备上接受调试授权提示
6. Click "Inspect" next to the page you want to debug
6. 点击要调试页面旁的“Inspect”
**Port forwarding for localhost:**
```bash
**本地主机端口转发:**
```bashIn chrome://inspect, click "Port forwarding"
在chrome://inspect中,点击“端口转发”
Add: localhost:3000 → localhost:3000
添加:localhost:3000 → localhost:3000
Now Android Chrome can access your dev server at localhost:3000
现在Android Chrome可以通过localhost:3000访问你的开发服务器
undefinedundefinedSafari Web Inspector (iOS)
Safari Web Inspector(iOS)
bash
undefinedbash
undefined1. On iPhone/iPad:
1. 在iPhone/iPad上:
Settings → Safari → Advanced → Web Inspector = ON
设置 → Safari浏览器 → 高级 → Web检查器 = 开启
2. On Mac:
2. 在Mac上:
Safari → Preferences → Advanced → "Show Develop menu" = ON
Safari → 偏好设置 → 高级 → “显示开发菜单” = 开启
3. Connect device via USB (or enable Wi-Fi debugging)
3. 通过USB连接设备(或启用Wi-Fi调试)
4. Open Safari on Mac:
4. 在Mac上打开Safari:
Develop → [Device Name] → [Page to debug]
开发 → [设备名称] → [要调试的页面]
Wireless debugging (after initial USB setup):
无线调试(首次USB设置后):
Develop → [Device] → Connect via Network
开发 → [设备] → 通过网络连接
undefinedundefinedFirefox Remote Debugging (Android)
Firefox远程调试(Android)
bash
undefinedbash
undefined1. On Android Firefox:
1. 在Android Firefox上:
Settings → Advanced → Remote debugging = ON
设置 → 高级 → 远程调试 = 开启
2. On Desktop Firefox:
2. 在桌面Firefox上:
Open about:debugging
打开about:debugging
3. Connect Android via USB
3. 通过USB连接Android设备
4. Enable USB devices in about:debugging
4. 在about:debugging中启用USB设备
5. Click "Connect" next to your device
5. 点击设备旁的“连接”
undefinedundefinediOS debugging without Mac
无Mac环境下的iOS调试
Using ios-webkit-debug-proxy
使用ios-webkit-debug-proxy
bash
undefinedbash
undefinedInstall on Windows (via Scoop)
Windows安装(通过Scoop)
scoop bucket add extras
scoop install ios-webkit-debug-proxy
scoop bucket add extras
scoop install ios-webkit-debug-proxy
Install on Linux
Linux安装
sudo apt-get install ios-webkit-debug-proxy
sudo apt-get install ios-webkit-debug-proxy
Install on Mac
Mac安装
brew install ios-webkit-debug-proxy
brew install ios-webkit-debug-proxy
Run the proxy
运行代理
ios_webkit_debug_proxy -f chrome-devtools://devtools/bundled/inspector.html
ios_webkit_debug_proxy -f chrome-devtools://devtools/bundled/inspector.html
Connect to http://localhost:9221 to see connected devices
undefinedundefinedCommercial: Inspect.dev
商业方案:Inspect.dev
Inspect.dev provides iOS debugging from Windows/Linux with a familiar DevTools interface.
bash
undefinedInspect.dev支持从Windows/Linux环境调试iOS设备,提供熟悉的DevTools界面。
bash
undefinedDownload from https://inspect.dev/
1. Install application
1. 安装应用
2. Connect iOS device via USB
2. 通过USB连接iOS设备
3. Enable Web Inspector on iOS
3. 在iOS上启用Web检查器
4. Inspect.dev auto-detects pages
4. Inspect.dev自动检测页面
5. Click to open DevTools interface
5. 点击打开DevTools界面
undefinedundefinedCloud testing platforms
云测试平台
LambdaTest (freemium)
LambdaTest(免费增值)
python
undefinedpython
undefinedLambdaTest provides real device cloud with console access
LambdaTest提供带控制台访问的真实设备云
Free tier: 100 minutes/month
免费额度:每月100分钟
import requests
import requests
LambdaTest REST API for automation
LambdaTest REST API用于自动化
LAMBDATEST_API = "https://api.lambdatest.com/automation/api/v1"
LAMBDATEST_API = "https://api.lambdatest.com/automation/api/v1"
For manual testing:
手动测试:
1. Go to https://www.lambdatest.com/
2. Select device/browser
2. 选择设备/浏览器
3. Enter URL
3. 输入URL
4. DevTools available in toolbar
4. 工具栏中可打开DevTools
Selenium/Playwright integration for automated console capture
Selenium/Playwright集成用于自动捕获控制台日志
from playwright.sync_api import sync_playwright
def test_on_lambdatest():
with sync_playwright() as p:
# Connect to LambdaTest
browser = p.chromium.connect(
f"wss://cdp.lambdatest.com/playwright?capabilities="
f"{{"browserName":"Chrome","platform":"android"}}"
)
page = browser.new_page()
# Capture console logs
logs = []
page.on('console', lambda msg: logs.append(msg.text()))
page.goto('https://example.com')
browser.close()
return logsundefinedfrom 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 logsundefinedBrowserStack
BrowserStack
python
undefinedpython
undefinedBrowserStack: $29/month+, 10,000+ real devices
BrowserStack:每月29美元起,支持10000+真实设备
from selenium import webdriver
from selenium.webdriver.common.desired_capabilities import DesiredCapabilities
def get_browserstack_driver():
"""Create BrowserStack WebDriver with console logging."""
capabilities = {
'browserName': 'chrome',
'device': 'Samsung Galaxy S21',
'realMobile': 'true',
'os_version': '11.0',
'browserstack.console': 'verbose', # Capture console logs
'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 driverfrom 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 driverAfter test, retrieve logs from BrowserStack dashboard or API
测试完成后,从BrowserStack控制台或API获取日志
undefinedundefinedProgrammatic console capture
程序化控制台捕获
Playwright console capture
Playwright控制台捕获
javascript
const { chromium, devices } = require('playwright');
async function captureConsoleLogs(url) {
const browser = await chromium.launch();
// Emulate mobile device
const context = await browser.newContext({
...devices['iPhone 13']
});
const page = await context.newPage();
// Capture all console messages
const logs = [];
page.on('console', msg => {
logs.push({
type: msg.type(),
text: msg.text(),
location: msg.location(),
timestamp: new Date().toISOString()
});
});
// Capture page errors
const errors = [];
page.on('pageerror', error => {
errors.push({
message: error.message,
stack: error.stack,
timestamp: new Date().toISOString()
});
});
// Capture failed requests
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 };
}
// Usage
captureConsoleLogs('https://example.com')
.then(result => console.log(JSON.stringify(result, null, 2)));javascript
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 console capture
Puppeteer控制台捕获
javascript
const puppeteer = require('puppeteer');
async function debugMobilePage(url) {
const browser = await puppeteer.launch();
const page = await browser.newPage();
// Set mobile viewport
await page.setViewport({
width: 375,
height: 812,
isMobile: true,
hasTouch: true
});
// Mobile user agent
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'
);
// Console capture with full details
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);
// Get source location
const location = msg.location();
if (location.url) {
console.log(` at ${location.url}:${location.lineNumber}`);
}
});
// Unhandled promise rejections
page.on('pageerror', err => {
console.error('[PAGE ERROR]', err.message);
});
await page.goto(url, { waitUntil: 'networkidle0' });
// Execute JavaScript and capture result
const result = await page.evaluate(() => {
// Check for common mobile issues
return {
viewportWidth: window.innerWidth,
devicePixelRatio: window.devicePixelRatio,
touchSupport: 'ontouchstart' in window,
errors: window.__capturedErrors || []
};
});
console.log('Page info:', result);
await browser.close();
}javascript
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(` at ${location.url}:${location.lineNumber}`);
}
});
// 未处理的Promise拒绝
page.on('pageerror', err => {
console.error('[PAGE 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();
}Error monitoring services
错误监控服务
Sentry integration
Sentry集成
javascript
// npm install @sentry/browser
import * as Sentry from '@sentry/browser';
Sentry.init({
dsn: 'YOUR_SENTRY_DSN',
environment: 'production',
// Capture console.error
integrations: [
new Sentry.BrowserTracing(),
new Sentry.Replay() // Session replay for debugging
],
// Sample rates
tracesSampleRate: 0.1,
replaysSessionSampleRate: 0.1,
replaysOnErrorSampleRate: 1.0,
beforeSend(event) {
// Filter or modify events
return event;
}
});
// Manual error capture
try {
riskyOperation();
} catch (error) {
Sentry.captureException(error);
}
// Add context
Sentry.setUser({ id: 'user123' });
Sentry.setTag('page', 'checkout');javascript
// 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 for session replay
LogRocket会话重放
javascript
// npm install logrocket
import LogRocket from 'logrocket';
LogRocket.init('your-app/your-project');
// Identify user
LogRocket.identify('user123', {
name: 'Test User',
email: 'user@example.com'
});
// Console logs automatically captured
console.log('This appears in LogRocket');
// Manual logging
LogRocket.log('Custom event', { data: 'value' });
// Track errors
LogRocket.captureException(new Error('Something went wrong'));javascript
// 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('This appears in LogRocket');
// 手动日志记录
LogRocket.log('Custom event', { data: 'value' });
// 追踪错误
LogRocket.captureException(new Error('Something went wrong'));Android screen mirroring with Scrcpy
使用Scrcpy进行Android屏幕镜像
bash
undefinedbash
undefinedInstall scrcpy
安装scrcpy
Windows: scoop install scrcpy
Windows: scoop install scrcpy
Mac: brew install scrcpy
Mac: brew install scrcpy
Linux: apt install scrcpy
Linux: apt install scrcpy
Basic mirroring
基础镜像
scrcpy
scrcpy
With specific options
带特定选项的镜像
scrcpy --max-size 1024 --bit-rate 2M
scrcpy --max-size 1024 --bit-rate 2M
Wireless connection (after initial USB)
无线连接(首次USB连接后)
adb tcpip 5555
adb connect <device-ip>:5555
scrcpy
adb tcpip 5555
adb connect <device-ip>:5555
scrcpy
Record session
录制会话
scrcpy --record session.mp4
scrcpy --record session.mp4
Turn off device screen while mirroring
镜像时关闭设备屏幕
scrcpy --turn-screen-off
undefinedscrcpy --turn-screen-off
undefinedMobile debugging workflow
移动端调试工作流
┌─────────────────────────────────────────────────────────────────┐
│ MOBILE DEBUGGING DECISION TREE │
├─────────────────────────────────────────────────────────────────┤
│ │
│ Q: Do you have physical access to the device? │
│ │ │
│ ├─ YES: Can you connect via USB? │
│ │ │ │
│ │ ├─ Android: Use Chrome DevTools Remote │
│ │ │ chrome://inspect#devices │
│ │ │ │
│ │ └─ iOS: Have a Mac? │
│ │ │ │
│ │ ├─ YES: Use Safari Web Inspector │
│ │ │ │
│ │ └─ NO: Use Inspect.dev or │
│ │ ios-webkit-debug-proxy │
│ │ │
│ └─ NO USB: Inject Eruda/vConsole via bookmarklet │
│ │
│ Q: Remote/production debugging? │
│ │ │
│ ├─ Add conditional Eruda loading │
│ │ (?eruda=true parameter) │
│ │ │
│ └─ Set up Sentry/LogRocket for error monitoring │
│ │
│ Q: Automated testing? │
│ │ │
│ ├─ Playwright/Puppeteer with mobile emulation │
│ │ │
│ └─ Cloud platforms (LambdaTest, BrowserStack) │
│ │
└─────────────────────────────────────────────────────────────────┘┌─────────────────────────────────────────────────────────────────┐
│ 移动端调试决策树 │
├─────────────────────────────────────────────────────────────────┤
│ │
│ 问题:你是否能物理接触到设备? │
│ │ │
│ ├─ 是:能否通过USB连接? │
│ │ │ │
│ │ ├─ Android:使用Chrome DevTools远程调试 │
│ │ │ chrome://inspect#devices │
│ │ │ │
│ │ └─ iOS:是否有Mac设备? │
│ │ │ │
│ │ ├─ 是:使用Safari Web Inspector │
│ │ │ │
│ │ └─ 否:使用Inspect.dev或 │
│ │ ios-webkit-debug-proxy │
│ │ │
│ └─ 无USB:通过书签注入Eruda/vConsole │
│ │
│ 问题:是否为远程/生产环境调试? │
│ │ │
│ ├─ 添加条件式Eruda加载 │
│ │ (?eruda=true参数) │
│ │ │
│ └─ 配置Sentry/LogRocket进行错误监控 │
│ │
│ 问题:是否为自动化测试? │
│ │ │
│ ├─ 使用带移动端模拟的Playwright/Puppeteer │
│ │ │
│ └─ 使用云平台(LambdaTest、BrowserStack) │
│ │
└─────────────────────────────────────────────────────────────────┘Common mobile debugging issues
常见移动端调试问题
Touch events not firing
触摸事件未触发
javascript
// Check if touch events are supported
eruda.init();
console.log('Touch support:', 'ontouchstart' in window);
console.log('Pointer events:', 'onpointerdown' in window);
// Debug touch events
document.addEventListener('touchstart', e => {
console.log('touchstart', e.touches.length, 'touches');
}, { passive: true });
document.addEventListener('click', e => {
console.log('click at', e.clientX, e.clientY);
});javascript
// 检查是否支持触摸事件
eruda.init();
console.log('触摸支持:', 'ontouchstart' in window);
console.log('指针事件:', 'onpointerdown' in window);
// 调试触摸事件
document.addEventListener('touchstart', e => {
console.log('touchstart', e.touches.length, '个触摸点');
}, { passive: true });
document.addEventListener('click', e => {
console.log('点击位置:', e.clientX, e.clientY);
});Viewport issues
视口问题
javascript
// Log viewport information
console.log('Viewport:', {
innerWidth: window.innerWidth,
innerHeight: window.innerHeight,
outerWidth: window.outerWidth,
outerHeight: window.outerHeight,
devicePixelRatio: window.devicePixelRatio,
orientation: screen.orientation?.type
});
// Check meta viewport
const viewport = document.querySelector('meta[name="viewport"]');
console.log('Viewport meta:', viewport?.content);javascript
// 打印视口信息
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);Performance debugging
性能调试
javascript
// Check performance timing
const perf = performance.getEntriesByType('navigation')[0];
console.log('Page load timing:', {
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
});
// Check memory (Chrome only)
if (performance.memory) {
console.log('Memory:', {
usedJSHeapSize: (performance.memory.usedJSHeapSize / 1048576).toFixed(2) + ' MB',
totalJSHeapSize: (performance.memory.totalJSHeapSize / 1048576).toFixed(2) + ' MB'
});
}javascript
// 检查性能计时
const perf = performance.getEntriesByType('navigation')[0];
console.log('页面加载计时:', {
dns解析: perf.domainLookupEnd - perf.domainLookupStart,
tcp连接: perf.connectEnd - perf.connectStart,
请求发送: perf.responseStart - perf.requestStart,
响应接收: perf.responseEnd - perf.responseStart,
DOM解析: perf.domInteractive - perf.responseEnd,
DOM完成: perf.domComplete - perf.domInteractive,
总耗时: perf.loadEventEnd - perf.navigationStart
});
// 检查内存(仅Chrome支持)
if (performance.memory) {
console.log('内存信息:', {
已使用JS堆内存: (performance.memory.usedJSHeapSize / 1048576).toFixed(2) + ' MB',
总JS堆内存: (performance.memory.totalJSHeapSize / 1048576).toFixed(2) + ' MB'
});
}Platform comparison
平台对比
| Tool | Cost | Platforms | Setup Difficulty | Best For |
|---|---|---|---|---|
| Eruda | Free | All browsers | Easy (bookmarklet) | Quick debugging |
| vConsole | Free | All browsers | Easy | WeChat apps |
| Chrome Remote | Free | Android only | Medium | Full DevTools |
| Safari Inspector | Free | iOS only | Easy (Mac required) | Full DevTools |
| Inspect.dev | Paid | iOS from any OS | Easy | iOS without Mac |
| LambdaTest | Freemium | All | Easy | Cloud testing |
| BrowserStack | Paid | All | Easy | Real devices |
| Sentry | Freemium | All | Medium | Error monitoring |
| 工具 | 成本 | 支持平台 | 配置难度 | 最佳适用场景 |
|---|---|---|---|---|
| Eruda | 免费 | 所有浏览器 | 简单(书签方式) | 快速调试 |
| vConsole | 免费 | 所有浏览器 | 简单 | 微信应用 |
| Chrome远程调试 | 免费 | 仅Android | 中等 | 完整DevTools调试 |
| Safari检查器 | 免费 | 仅iOS | 简单(需Mac) | 完整DevTools调试 |
| Inspect.dev | 付费 | 从任意OS调试iOS | 简单 | 无Mac环境下调试iOS |
| LambdaTest | 免费增值 | 所有平台 | 简单 | 云测试 |
| BrowserStack | 付费 | 所有平台 | 简单 | 真实设备测试 |
| Sentry | 免费增值 | 所有平台 | 中等 | 错误监控 |