mobile-eruda-pro
Version:
移动端调试工具,封装了eruda和eruda-vue,支持微信小程序调试,实时日志,优化点击触发机制,支持URL参数控制,多次点击显示隐藏,全局控制console和alert
382 lines (330 loc) • 14.8 kB
JavaScript
// 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();
};
})();