UNPKG

mobile-eruda-pro

Version:

移动端调试工具,封装了eruda和eruda-vue,支持微信小程序调试,实时日志,优化点击触发机制,支持URL参数控制,多次点击显示隐藏,全局控制console和alert

382 lines (330 loc) 14.8 kB
// WebSocket监控插件 (function() { 'use strict'; // WebSocket连接记录 var wsConnections = []; var wsMessages = []; var maxMessages = 1000; // 最大消息记录数 // 原始WebSocket构造函数 var OriginalWebSocket = window.WebSocket; // 创建WebSocket代理 function createWebSocketProxy() { function WebSocketProxy(url, protocols) { var ws = new OriginalWebSocket(url, protocols); var connectionId = Date.now() + '_' + Math.random().toString(36).substr(2, 9); // 记录连接信息 var connection = { id: connectionId, url: url, protocols: protocols, readyState: ws.readyState, createdAt: new Date(), connectedAt: null, closedAt: null, error: null, messageCount: { sent: 0, received: 0 } }; wsConnections.push(connection); // 代理所有WebSocket属性和方法 var proxy = Object.create(WebSocket.prototype); // 代理属性 Object.defineProperty(proxy, 'url', { get: () => ws.url }); Object.defineProperty(proxy, 'readyState', { get: () => ws.readyState }); Object.defineProperty(proxy, 'bufferedAmount', { get: () => ws.bufferedAmount }); Object.defineProperty(proxy, 'extensions', { get: () => ws.extensions }); Object.defineProperty(proxy, 'protocol', { get: () => ws.protocol }); Object.defineProperty(proxy, 'binaryType', { get: () => ws.binaryType, set: (value) => ws.binaryType = value }); // 代理事件处理器 ['onopen', 'onclose', 'onerror', 'onmessage'].forEach(function(event) { Object.defineProperty(proxy, event, { get: () => ws[event], set: (handler) => ws[event] = handler }); }); // 代理方法 proxy.send = function(data) { connection.messageCount.sent++; // 记录发送的消息 var message = { id: Date.now() + '_' + Math.random().toString(36).substr(2, 9), connectionId: connectionId, type: 'send', data: data, timestamp: new Date(), size: getDataSize(data) }; wsMessages.push(message); // 限制消息数量 if (wsMessages.length > maxMessages) { wsMessages.shift(); } updatePlugin(); return ws.send(data); }; proxy.close = function(code, reason) { connection.closedAt = new Date(); updatePlugin(); return ws.close(code, reason); }; proxy.addEventListener = function(type, listener, options) { return ws.addEventListener(type, listener, options); }; proxy.removeEventListener = function(type, listener, options) { return ws.removeEventListener(type, listener, options); }; proxy.dispatchEvent = function(event) { return ws.dispatchEvent(event); }; // 监听WebSocket事件 ws.addEventListener('open', function(event) { connection.connectedAt = new Date(); connection.readyState = ws.readyState; updatePlugin(); }); ws.addEventListener('message', function(event) { connection.messageCount.received++; // 记录接收的消息 var message = { id: Date.now() + '_' + Math.random().toString(36).substr(2, 9), connectionId: connectionId, type: 'receive', data: event.data, timestamp: new Date(), size: getDataSize(event.data) }; wsMessages.push(message); // 限制消息数量 if (wsMessages.length > maxMessages) { wsMessages.shift(); } updatePlugin(); }); ws.addEventListener('close', function(event) { connection.closedAt = new Date(); connection.readyState = ws.readyState; updatePlugin(); }); ws.addEventListener('error', function(event) { connection.error = { timestamp: new Date(), message: event.message || 'WebSocket error' }; updatePlugin(); }); return proxy; } // 复制静态属性 WebSocketProxy.CONNECTING = OriginalWebSocket.CONNECTING; WebSocketProxy.OPEN = OriginalWebSocket.OPEN; WebSocketProxy.CLOSING = OriginalWebSocket.CLOSING; WebSocketProxy.CLOSED = OriginalWebSocket.CLOSED; return WebSocketProxy; } // 获取数据大小 function getDataSize(data) { if (typeof data === 'string') { return new Blob([data]).size; } else if (data instanceof ArrayBuffer) { return data.byteLength; } else if (data instanceof Blob) { return data.size; } return 0; } // 格式化时间 function formatTime(date) { return date.toLocaleTimeString() + '.' + date.getMilliseconds().toString().padStart(3, '0'); } // 格式化数据大小 function formatSize(bytes) { if (bytes === 0) return '0 B'; var k = 1024; var sizes = ['B', 'KB', 'MB', 'GB']; var i = Math.floor(Math.log(bytes) / Math.log(k)); return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i]; } // 获取连接状态文本 function getReadyStateText(readyState) { switch (readyState) { case 0: return 'CONNECTING'; case 1: return 'OPEN'; case 2: return 'CLOSING'; case 3: return 'CLOSED'; default: return 'UNKNOWN'; } } // 插件更新函数 var updatePlugin = function() {}; // 替换全局WebSocket window.WebSocket = createWebSocketProxy(); // 导出插件 window.ErudaWebSocketPlugin = function(eruda) { var Tool = eruda.Tool; var util = eruda.util; var $ = util.$; class WebSocketTool extends Tool { constructor() { super(); this.name = 'websocket'; this._style = util.evalCss(` .eruda-websocket { padding: 10px; font-family: 'Courier New', monospace; font-size: 12px; height: 100%; overflow-y: auto; background: #fff; } .eruda-websocket.eruda-dark-theme { background: #2d3748; color: #e2e8f0; } .eruda-ws-connection { border: 1px solid #e2e8f0; margin-bottom: 10px; border-radius: 4px; background: #f7fafc; } .eruda-dark-theme .eruda-ws-connection { border-color: #4a5568; background: #1a202c; } .eruda-ws-header { padding: 8px 12px; background: #edf2f7; border-bottom: 1px solid #e2e8f0; font-weight: bold; cursor: pointer; } .eruda-dark-theme .eruda-ws-header { background: #2d3748; border-color: #4a5568; } .eruda-ws-messages { max-height: 200px; overflow-y: auto; } .eruda-ws-message { padding: 4px 12px; border-bottom: 1px solid #f1f5f9; font-size: 11px; } .eruda-dark-theme .eruda-ws-message { border-color: #4a5568; } .eruda-ws-message.send { background: #f0fff4; } .eruda-dark-theme .eruda-ws-message.send { background: #1a2e1a; } .eruda-ws-message.receive { background: #f0f9ff; } .eruda-dark-theme .eruda-ws-message.receive { background: #1a1e2e; } .eruda-ws-time { color: #64748b; margin-right: 8px; } .eruda-ws-type { font-weight: bold; margin-right: 8px; } .eruda-ws-type.send { color: #059669; } .eruda-ws-type.receive { color: #0284c7; } .eruda-ws-data { word-break: break-all; margin-top: 2px; } .eruda-ws-status { display: inline-block; padding: 2px 6px; border-radius: 3px; font-size: 10px; font-weight: bold; text-transform: uppercase; } .eruda-ws-status.connecting { background: #fbbf24; color: #92400e; } .eruda-ws-status.open { background: #34d399; color: #065f46; } .eruda-ws-status.closing { background: #f87171; color: #991b1b; } .eruda-ws-status.closed { background: #9ca3af; color: #374151; } .eruda-ws-clear { position: absolute; top: 10px; right: 10px; background: #ef4444; color: white; border: none; padding: 4px 8px; border-radius: 3px; cursor: pointer; font-size: 11px; } `); } init($el) { super.init($el); this._$el = $el; // 设置更新函数 updatePlugin = () => this.render(); this.render(); } render() { if (!this._$el) return; var html = '<button class="eruda-ws-clear" onclick="ErudaWebSocketPlugin.clear()">清空</button>'; if (wsConnections.length === 0) { html += '<div style="text-align: center; padding: 50px; color: #64748b;">暂无WebSocket连接</div>'; } else { wsConnections.forEach(function(conn) { var messages = wsMessages.filter(function(msg) { return msg.connectionId === conn.id; }); html += '<div class="eruda-ws-connection">'; html += '<div class="eruda-ws-header">'; html += '<div>' + conn.url + '</div>'; html += '<div style="font-size: 11px; margin-top: 4px;">'; html += '<span class="eruda-ws-status ' + getReadyStateText(conn.readyState).toLowerCase() + '">' + getReadyStateText(conn.readyState) + '</span>'; html += ' 发送: ' + conn.messageCount.sent + ' 接收: ' + conn.messageCount.received; if (conn.connectedAt) { html += ' 连接时间: ' + formatTime(conn.connectedAt); } html += '</div>'; html += '</div>'; if (messages.length > 0) { html += '<div class="eruda-ws-messages">'; messages.slice(-20).forEach(function(msg) { html += '<div class="eruda-ws-message ' + msg.type + '">'; html += '<span class="eruda-ws-time">' + formatTime(msg.timestamp) + '</span>'; html += '<span class="eruda-ws-type ' + msg.type + '">' + (msg.type === 'send' ? '发送' : '接收') + '</span>'; html += '<span style="color: #64748b;">(' + formatSize(msg.size) + ')</span>'; html += '<div class="eruda-ws-data">' + (typeof msg.data === 'string' ? msg.data : '[Binary Data]') + '</div>'; html += '</div>'; }); html += '</div>'; } html += '</div>'; }); } this._$el.html(html); } destroy() { super.destroy(); util.evalCss.remove(this._style); // 恢复原始WebSocket window.WebSocket = OriginalWebSocket; } } return new WebSocketTool(); }; // 清空记录的方法 window.ErudaWebSocketPlugin.clear = function() { wsConnections.length = 0; wsMessages.length = 0; updatePlugin(); }; })();