const VCONSOLE_QUERY_KEY = 'vconsole' const VCONSOLE_STORAGE_KEY = '__ENABLE_VCONSOLE__' const VCONSOLE_SCRIPT_URL = 'https://cdn.jsdelivr.net/npm/vconsole@3.15.1/dist/vconsole.min.js' let vConsoleBootstrapped = false /** * 仅在 H5 环境按需启用 vConsole,方便 iOS 真机排查分享页问题。 * 开启方式: * 1. 链接追加 ?vconsole=1 或 &vconsole=1 * 2. 开启后会写入 localStorage,后续刷新页面仍保持开启 * 3. 链接追加 ?vconsole=0 可关闭 */ export function initH5DebugPanel() { if (typeof window === 'undefined' || vConsoleBootstrapped) { return } const debugFlag = resolveDebugFlag() if (!debugFlag) { return } vConsoleBootstrapped = true bindGlobalDebugEvents() loadVConsoleScript() } function resolveDebugFlag() { const queryValue = getQueryParamValue(VCONSOLE_QUERY_KEY) if (queryValue === '1') { safeSetStorage(VCONSOLE_STORAGE_KEY, '1') return true } if (queryValue === '0') { safeRemoveStorage(VCONSOLE_STORAGE_KEY) return false } return safeGetStorage(VCONSOLE_STORAGE_KEY) === '1' } function bindGlobalDebugEvents() { window.addEventListener('error', (event) => { const error = event.error console.error('[h5-debug] window error:', { message: event.message, source: event.filename, line: event.lineno, column: event.colno, stack: error && error.stack ? error.stack : '', }) }) window.addEventListener('unhandledrejection', (event) => { console.error('[h5-debug] unhandled rejection:', event.reason) }) } function loadVConsoleScript() { if (window.VConsole) { createVConsoleInstance() return } const existingScript = document.querySelector(`script[data-debug-tool="${VCONSOLE_QUERY_KEY}"]`) if (existingScript) { existingScript.addEventListener('load', createVConsoleInstance, { once: true }) return } const script = document.createElement('script') script.src = VCONSOLE_SCRIPT_URL script.async = true script.setAttribute('data-debug-tool', VCONSOLE_QUERY_KEY) script.onload = createVConsoleInstance script.onerror = () => { console.error('[h5-debug] vConsole 加载失败:', VCONSOLE_SCRIPT_URL) } document.head.appendChild(script) } function createVConsoleInstance() { if (!window.VConsole || window.__h5VConsoleInstance) { return } window.__h5VConsoleInstance = new window.VConsole() console.log('[h5-debug] vConsole 已启用') } function getQueryParamValue(key) { const searchValue = readQueryValue(window.location.search, key) if (searchValue) { return searchValue } const hashQuery = String(window.location.hash || '').split('?')[1] || '' return readQueryValue(hashQuery, key) } function readQueryValue(queryText, key) { if (typeof queryText !== 'string' || !queryText) { return '' } const normalizedQuery = queryText.replace(/^[?#&]/, '') if (!normalizedQuery) { return '' } const pairs = normalizedQuery.split('&') for (const pair of pairs) { if (!pair) { continue } const splitIndex = pair.indexOf('=') const rawKey = splitIndex >= 0 ? pair.slice(0, splitIndex) : pair if (safeDecode(rawKey) !== key) { continue } const rawValue = splitIndex >= 0 ? pair.slice(splitIndex + 1) : '' return safeDecode(rawValue).trim() } return '' } function safeDecode(value) { try { return decodeURIComponent(String(value || '').replace(/\+/g, ' ')) } catch (error) { return String(value || '') } } function safeGetStorage(key) { try { return window.localStorage.getItem(key) } catch (error) { return '' } } function safeSetStorage(key, value) { try { window.localStorage.setItem(key, value) } catch (error) { // 忽略存储异常,避免影响页面正常使用。 } } function safeRemoveStorage(key) { try { window.localStorage.removeItem(key) } catch (error) { // 忽略存储异常,避免影响页面正常使用。 } }