mobile-debugging

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Mobile 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

FeatureErudavConsole
Size~100KB~85KB
DOM EditingYesView only
Network DetailsFullBasic
Plugin SystemYesYes
Dark ThemeVia pluginBuilt-in
Best ForFull debuggingQuick logging
功能特性ErudavConsole
体积~100KB~85KB
DOM编辑支持仅查看
网络详情完整基础
插件系统支持支持
深色主题通过插件内置
最佳适用场景完整调试快速日志查看

Native remote debugging

原生远程调试

Chrome DevTools (Android)

Chrome DevTools(Android)

bash
undefined
bash
undefined

1. 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

**本地主机端口转发:**
```bash

In 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访问你的开发服务器

undefined
undefined

Safari Web Inspector (iOS)

Safari Web Inspector(iOS)

bash
undefined
bash
undefined

1. 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

开发 → [设备] → 通过网络连接

undefined
undefined

Firefox Remote Debugging (Android)

Firefox远程调试(Android)

bash
undefined
bash
undefined

1. 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. 点击设备旁的“连接”

undefined
undefined

iOS debugging without Mac

无Mac环境下的iOS调试

Using ios-webkit-debug-proxy

使用ios-webkit-debug-proxy

bash
undefined
bash
undefined

Install 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

undefined
undefined

Commercial: Inspect.dev

商业方案:Inspect.dev

Inspect.dev provides iOS debugging from Windows/Linux with a familiar DevTools interface.
bash
undefined
Inspect.dev支持从Windows/Linux环境调试iOS设备,提供熟悉的DevTools界面。
bash
undefined

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界面

undefined
undefined

Cloud testing platforms

云测试平台

LambdaTest (freemium)

LambdaTest(免费增值)

python
undefined
python
undefined

LambdaTest 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用于自动化

For manual testing:

手动测试:

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 logs
undefined
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
undefined

BrowserStack

BrowserStack

python
undefined
python
undefined

BrowserStack: $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 driver
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

After test, retrieve logs from BrowserStack dashboard or API

测试完成后,从BrowserStack控制台或API获取日志

undefined
undefined

Programmatic 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
undefined
bash
undefined

Install 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
undefined
scrcpy --turn-screen-off
undefined

Mobile 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

平台对比

ToolCostPlatformsSetup DifficultyBest For
ErudaFreeAll browsersEasy (bookmarklet)Quick debugging
vConsoleFreeAll browsersEasyWeChat apps
Chrome RemoteFreeAndroid onlyMediumFull DevTools
Safari InspectorFreeiOS onlyEasy (Mac required)Full DevTools
Inspect.devPaidiOS from any OSEasyiOS without Mac
LambdaTestFreemiumAllEasyCloud testing
BrowserStackPaidAllEasyReal devices
SentryFreemiumAllMediumError monitoring
工具成本支持平台配置难度最佳适用场景
Eruda免费所有浏览器简单(书签方式)快速调试
vConsole免费所有浏览器简单微信应用
Chrome远程调试免费仅Android中等完整DevTools调试
Safari检查器免费仅iOS简单(需Mac)完整DevTools调试
Inspect.dev付费从任意OS调试iOS简单无Mac环境下调试iOS
LambdaTest免费增值所有平台简单云测试
BrowserStack付费所有平台简单真实设备测试
Sentry免费增值所有平台中等错误监控