feat: 精简日志

This commit is contained in:
zdl
2025-11-26 15:34:11 +08:00
parent 64f8914951
commit 9df725b748
4 changed files with 83 additions and 252 deletions

View File

@@ -15,6 +15,10 @@
import { useEffect, useRef } from 'react'; import { useEffect, useRef } from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
// ⚡ 模块级变量:防止 React StrictMode 双重初始化
let widgetInitialized = false;
let idleCallbackId = null;
const BytedeskWidget = ({ const BytedeskWidget = ({
config, config,
autoLoad = true, autoLoad = true,
@@ -27,13 +31,24 @@ const BytedeskWidget = ({
useEffect(() => { useEffect(() => {
// 如果不自动加载或配置未设置,跳过 // 如果不自动加载或配置未设置,跳过
if (!autoLoad || !config) { if (!autoLoad || !config) {
if (!config) {
console.warn('[Bytedesk] 配置未设置,客服组件未加载');
}
return; return;
} }
console.log('[Bytedesk] 开始加载客服Widget...', config); // ⚡ 防止重复初始化React StrictMode 会双重调用 useEffect
if (widgetInitialized) {
return;
}
// ⚡ 使用 requestIdleCallback 延迟加载,不阻塞首屏
const loadWidget = () => {
// 再次检查,防止竞态条件
if (widgetInitialized) return;
widgetInitialized = true;
// 检查脚本是否已存在
if (document.getElementById('bytedesk-web-script')) {
return;
}
// 加载Bytedesk Widget脚本 // 加载Bytedesk Widget脚本
const script = document.createElement('script'); const script = document.createElement('script');
@@ -42,28 +57,20 @@ const BytedeskWidget = ({
script.id = 'bytedesk-web-script'; script.id = 'bytedesk-web-script';
script.onload = () => { script.onload = () => {
console.log('[Bytedesk] Widget脚本加载成功');
try { try {
if (window.BytedeskWeb) { if (window.BytedeskWeb) {
console.log('[Bytedesk] 初始化Widget');
const bytedesk = new window.BytedeskWeb(config); const bytedesk = new window.BytedeskWeb(config);
bytedesk.init(); bytedesk.init();
widgetRef.current = bytedesk; widgetRef.current = bytedesk;
console.log('[Bytedesk] Widget初始化成功');
// ⚡ 屏蔽 STOMP WebSocket 错误日志(不影响功能) // ⚡ 屏蔽 STOMP WebSocket 错误日志(不影响功能)
// Bytedesk SDK 内部的 /stomp WebSocket 连接失败不影响核心客服功能
// SDK 会自动降级使用 HTTP 轮询
const originalConsoleError = console.error; const originalConsoleError = console.error;
console.error = function(...args) { console.error = function(...args) {
const errorMsg = args.join(' '); const errorMsg = args.join(' ');
// 忽略 /stomp 和 STOMP 相关错误
if (errorMsg.includes('/stomp') || if (errorMsg.includes('/stomp') ||
errorMsg.includes('stomp onWebSocketError') || errorMsg.includes('stomp onWebSocketError') ||
(errorMsg.includes('WebSocket connection to') && errorMsg.includes('/stomp'))) { (errorMsg.includes('WebSocket connection to') && errorMsg.includes('/stomp'))) {
return; // 不输出日志 return;
} }
originalConsoleError.apply(console, args); originalConsoleError.apply(console, args);
}; };
@@ -75,7 +82,7 @@ const BytedeskWidget = ({
throw new Error('BytedeskWeb对象未定义'); throw new Error('BytedeskWeb对象未定义');
} }
} catch (error) { } catch (error) {
console.error('[Bytedesk] Widget初始化失败:', error); console.error('[Bytedesk] 初始化失败:', error);
if (onError) { if (onError) {
onError(error); onError(error);
} }
@@ -83,54 +90,39 @@ const BytedeskWidget = ({
}; };
script.onerror = (error) => { script.onerror = (error) => {
console.error('[Bytedesk] Widget脚本加载失败:', error); console.error('[Bytedesk] 脚本加载失败:', error);
widgetInitialized = false; // 允许重试
if (onError) { if (onError) {
onError(error); onError(error);
} }
}; };
// 添加脚本到页面
document.body.appendChild(script); document.body.appendChild(script);
scriptRef.current = script; scriptRef.current = script;
};
// 清理函数 - 增强错误处理,防止 React 18 StrictMode 双重清理报错 // ⚡ 使用 requestIdleCallback 在浏览器空闲时加载
if ('requestIdleCallback' in window) {
idleCallbackId = requestIdleCallback(loadWidget, { timeout: 3000 });
} else {
// 降级:使用 setTimeout
idleCallbackId = setTimeout(loadWidget, 100);
}
// 清理函数
return () => { return () => {
console.log('[Bytedesk] 清理Widget'); // 取消待执行的 idle callback
if (idleCallbackId) {
// 移除脚本 if ('cancelIdleCallback' in window) {
try { cancelIdleCallback(idleCallbackId);
if (scriptRef.current && scriptRef.current.parentNode) { } else {
scriptRef.current.parentNode.removeChild(scriptRef.current); clearTimeout(idleCallbackId);
} }
scriptRef.current = null; idleCallbackId = null;
} catch (error) {
console.warn('[Bytedesk] 移除脚本失败(可能已被移除):', error.message);
} }
// 移除Widget DOM元素 // ⚠️ 不重置 widgetInitialized保持单例
try { // 不清理 DOM因为客服 Widget 应该持久存在
const widgetElements = document.querySelectorAll('[class*="bytedesk"], [id*="bytedesk"]');
widgetElements.forEach(el => {
try {
if (el && el.parentNode && el.parentNode.contains(el)) {
el.parentNode.removeChild(el);
}
} catch (err) {
// 忽略单个元素移除失败(可能已被移除)
}
});
} catch (error) {
console.warn('[Bytedesk] 清理Widget DOM元素失败:', error.message);
}
// 清理全局对象
try {
if (window.BytedeskWeb) {
delete window.BytedeskWeb;
}
} catch (error) {
console.warn('[Bytedesk] 清理全局对象失败:', error.message);
}
}; };
}, [config, autoLoad, onLoad, onError]); }, [config, autoLoad, onLoad, onError]);

View File

@@ -40,82 +40,18 @@ if (process.env.REACT_APP_ENABLE_DEBUG === 'true') {
function registerServiceWorker() { function registerServiceWorker() {
// ⚠️ Mock 模式下跳过 Service Worker 注册(避免与 MSW 冲突) // ⚠️ Mock 模式下跳过 Service Worker 注册(避免与 MSW 冲突)
if (process.env.REACT_APP_ENABLE_MOCK === 'true') { if (process.env.REACT_APP_ENABLE_MOCK === 'true') {
console.log(
'%c[App] Mock 模式已启用,跳过通知 Service Worker 注册(避免与 MSW 冲突)',
'color: #FF9800; font-weight: bold;'
);
return; return;
} }
// 仅在支持 Service Worker 的浏览器中注册 // 仅在支持 Service Worker 的浏览器中注册
if ('serviceWorker' in navigator) { if ('serviceWorker' in navigator) {
// 在页面加载完成后注册
window.addEventListener('load', () => { window.addEventListener('load', () => {
navigator.serviceWorker navigator.serviceWorker
.register('/service-worker.js') .register('/service-worker.js')
.then((registration) => {
console.log('[App] ✅ Service Worker 注册成功');
console.log('[App] Scope:', registration.scope);
// 检查当前激活状态
if (navigator.serviceWorker.controller) {
console.log('[App] ✅ Service Worker 已激活并控制页面');
} else {
console.log('[App] ⏳ Service Worker 已注册,等待激活...');
console.log('[App] 💡 刷新页面以激活 Service Worker');
// 监听 controller 变化Service Worker 激活后触发)
navigator.serviceWorker.addEventListener('controllerchange', () => {
console.log('[App] ✅ Service Worker 控制器已更新');
});
}
// 监听 Service Worker 更新
registration.addEventListener('updatefound', () => {
const newWorker = registration.installing;
console.log('[App] 🔄 发现 Service Worker 更新');
if (newWorker) {
newWorker.addEventListener('statechange', () => {
console.log(`[App] Service Worker 状态: ${newWorker.state}`);
if (newWorker.state === 'activated') {
console.log('[App] ✅ Service Worker 已激活');
// 如果有旧的 Service Worker 在控制页面,提示用户刷新
if (navigator.serviceWorker.controller) {
console.log('[App] 💡 Service Worker 已更新,建议刷新页面');
}
}
});
}
});
})
.catch((error) => { .catch((error) => {
console.error('[App] Service Worker 注册失败'); console.error('[App] Service Worker 注册失败:', error.message);
console.error('[App] 错误类型:', error.name);
console.error('[App] 错误信息:', error.message);
console.error('[App] 完整错误:', error);
// 额外检查:验证文件是否可访问
fetch('/service-worker.js', { method: 'HEAD' })
.then(response => {
if (response.ok) {
console.error('[App] Service Worker 文件存在但注册失败');
console.error('[App] 💡 可能的原因:');
console.error('[App] 1. Service Worker 文件有语法错误');
console.error('[App] 2. 浏览器不支持某些 Service Worker 特性');
console.error('[App] 3. HTTPS 证书问题Service Worker 需要 HTTPS');
} else {
console.error('[App] Service Worker 文件不存在HTTP', response.status, '');
}
})
.catch(fetchError => {
console.error('[App] 无法访问 Service Worker 文件:', fetchError.message);
}); });
}); });
});
} else {
console.warn('[App] Service Worker is not supported in this browser');
} }
} }
@@ -149,12 +85,8 @@ async function startApp() {
try { try {
const { startMockServiceWorker } = await import('./mocks/browser'); const { startMockServiceWorker } = await import('./mocks/browser');
await startMockServiceWorker(); await startMockServiceWorker();
console.log(
'%c[MSW] ✅ Mock Service Worker 已启动,开始渲染应用',
'color: #4CAF50; font-weight: bold;'
);
} catch (error) { } catch (error) {
console.error('[MSW] ❌ Mock Service Worker 启动失败,继续渲染应用:', error); console.error('[MSW] 启动失败:', error);
} }
} }

View File

@@ -47,18 +47,8 @@ export async function startMockServiceWorker() {
}); });
isStarted = true; isStarted = true;
console.log( // 精简日志:只保留一行启动提示
'%c[MSW] Mock Service Worker 已启动 🎭 (警告模式)', console.log('%c[MSW] Mock 已启用 🎭', 'color: #4CAF50; font-weight: bold;');
'color: #4CAF50; font-weight: bold; font-size: 14px;'
);
console.log(
'%c警告模式已定义 Mock → 返回假数据 | 未定义 Mock → 显示警告 ⚠️ | 允许 passthrough',
'color: #FF9800; font-weight: bold; font-size: 12px;'
);
console.log(
'%c查看 src/mocks/handlers/ 目录管理 Mock 接口',
'color: #2196F3; font-size: 12px;'
);
} catch (error) { } catch (error) {
console.error('[MSW] 启动失败:', error); console.error('[MSW] 启动失败:', error);
} finally { } finally {

View File

@@ -56,17 +56,8 @@ class SocketService {
// 注册所有暂存的事件监听器(保留 pendingListeners不清空 // 注册所有暂存的事件监听器(保留 pendingListeners不清空
if (this.pendingListeners.length > 0) { if (this.pendingListeners.length > 0) {
console.log(`[socketService] 📦 注册 ${this.pendingListeners.length} 个暂存的事件监听器`);
this.pendingListeners.forEach(({ event, callback }) => { this.pendingListeners.forEach(({ event, callback }) => {
// 直接在 Socket.IO 实例上注册(避免递归调用 this.on() this.socket.on(event, callback);
const wrappedCallback = (...args) => {
console.log(`%c[socketService] 🔔 收到原始事件: ${event}`, 'color: #2196F3; font-weight: bold;');
console.log(`[socketService] 事件数据 (${event}):`, ...args);
callback(...args);
};
this.socket.on(event, wrappedCallback);
console.log(`[socketService] ✓ 已注册事件监听器: ${event}`);
}); });
// ⚠️ 重要:不清空 pendingListeners保留用于重连 // ⚠️ 重要:不清空 pendingListeners保留用于重连
} }
@@ -82,15 +73,8 @@ class SocketService {
this.customReconnectTimer = null; this.customReconnectTimer = null;
} }
logger.info('socketService', 'Socket.IO connected successfully', { logger.info('socketService', 'Socket.IO connected', { socketId: this.socket.id });
socketId: this.socket.id,
});
console.log(`%c[socketService] ✅ WebSocket 已连接`, 'color: #4CAF50; font-weight: bold;');
console.log('[socketService] Socket ID:', this.socket.id);
// ⚠️ 已移除自动订阅,让 NotificationContext 负责订阅 // ⚠️ 已移除自动订阅,让 NotificationContext 负责订阅
// this.subscribeToAllEvents();
}); });
// 监听断开连接 // 监听断开连接
@@ -174,25 +158,12 @@ class SocketService {
); );
if (!exists) { if (!exists) {
logger.info('socketService', 'Socket not ready, queuing listener', { event });
console.log(`[socketService] 📦 Socket 未初始化,暂存事件监听器: ${event}`);
this.pendingListeners.push({ event, callback }); this.pendingListeners.push({ event, callback });
} else {
console.log(`[socketService] ⚠️ 监听器已存在,跳过: ${event}`);
} }
return; return;
} }
// 包装回调函数,添加日志 this.socket.on(event, callback);
const wrappedCallback = (...args) => {
console.log(`%c[socketService] 🔔 收到原始事件: ${event}`, 'color: #2196F3; font-weight: bold;');
console.log(`[socketService] 事件数据 (${event}):`, ...args);
callback(...args);
};
this.socket.on(event, wrappedCallback);
logger.info('socketService', `Event listener added: ${event}`);
console.log(`[socketService] ✓ 已注册事件监听器: ${event}`);
} }
/** /**
@@ -210,8 +181,6 @@ class SocketService {
} else { } else {
this.socket.off(event); this.socket.off(event);
} }
logger.info('socketService', `Event listener removed: ${event}`);
} }
/** /**
@@ -231,8 +200,6 @@ class SocketService {
} else { } else {
this.socket.emit(event, data); this.socket.emit(event, data);
} }
logger.info('socketService', `Event emitted: ${event}`, data);
} }
/** /**
@@ -355,65 +322,31 @@ class SocketService {
* 执行订阅操作(内部方法) * 执行订阅操作(内部方法)
*/ */
_doSubscribe(eventType, importance, onNewEvent, onSubscribed) { _doSubscribe(eventType, importance, onNewEvent, onSubscribed) {
console.log('\n========== [SocketService DEBUG] 开始订阅 ==========');
console.log('[SocketService DEBUG] 事件类型:', eventType);
console.log('[SocketService DEBUG] 重要性:', importance);
console.log('[SocketService DEBUG] Socket 连接状态:', this.connected);
console.log('[SocketService DEBUG] Socket ID:', this.socket?.id);
// 发送订阅请求 // 发送订阅请求
const subscribeData = { const subscribeData = {
event_type: eventType, event_type: eventType,
importance: importance, importance: importance,
}; };
console.log('[SocketService DEBUG] 准备发送 subscribe_events:', subscribeData);
this.emit('subscribe_events', subscribeData); this.emit('subscribe_events', subscribeData);
console.log('[SocketService DEBUG] ✓ 已发送 subscribe_events');
// 监听订阅确认 // 监听订阅确认
this.socket.once('subscription_confirmed', (data) => { this.socket.once('subscription_confirmed', (data) => {
console.log('\n[SocketService DEBUG] ========== 收到订阅确认 ==========');
console.log('[SocketService DEBUG] 订阅确认数据:', data);
logger.info('socketService', 'Subscription confirmed', data);
if (onSubscribed) { if (onSubscribed) {
console.log('[SocketService DEBUG] 调用 onSubscribed 回调');
onSubscribed(data); onSubscribed(data);
} }
console.log('[SocketService DEBUG] ========== 订阅确认处理完成 ==========\n');
}); });
// 监听订阅错误 // 监听订阅错误
this.socket.once('subscription_error', (error) => { this.socket.once('subscription_error', (error) => {
console.error('\n[SocketService ERROR] ========== 订阅错误 ==========');
console.error('[SocketService ERROR] 错误信息:', error);
logger.error('socketService', 'Subscription error', error); logger.error('socketService', 'Subscription error', error);
console.error('[SocketService ERROR] ========== 订阅错误处理完成 ==========\n');
}); });
// 监听新事件推送 // 监听新事件推送
// ⚠️ 注意:不要移除其他地方注册的 new_event 监听器(如 NotificationContext
// 多个监听器可以共存,都会被触发
if (onNewEvent) { if (onNewEvent) {
console.log('[SocketService DEBUG] 设置 new_event 监听器');
// ⚠️ 已移除 this.socket.off('new_event'),允许多个监听器共存
// 添加新的监听器(与其他监听器共存)
this.socket.on('new_event', (eventData) => { this.socket.on('new_event', (eventData) => {
console.log('\n[SocketService DEBUG] ========== 收到新事件推送 ==========');
console.log('[SocketService DEBUG] 事件数据:', eventData);
console.log('[SocketService DEBUG] 事件 ID:', eventData?.id);
console.log('[SocketService DEBUG] 事件标题:', eventData?.title);
logger.info('socketService', 'New event received', eventData);
console.log('[SocketService DEBUG] 准备调用 onNewEvent 回调');
onNewEvent(eventData); onNewEvent(eventData);
console.log('[SocketService DEBUG] ✓ onNewEvent 回调已调用');
console.log('[SocketService DEBUG] ========== 新事件处理完成 ==========\n');
}); });
console.log('[SocketService DEBUG] ✓ new_event 监听器已设置(与其他监听器共存)');
} }
console.log('[SocketService DEBUG] ========== 订阅完成 ==========\n');
} }
/** /**
@@ -440,11 +373,7 @@ class SocketService {
// 监听取消订阅确认 // 监听取消订阅确认
this.socket.once('unsubscription_confirmed', (data) => { this.socket.once('unsubscription_confirmed', (data) => {
logger.info('socketService', 'Unsubscription confirmed', data);
// 移除新事件监听器
this.socket.off('new_event'); this.socket.off('new_event');
if (onUnsubscribed) { if (onUnsubscribed) {
onUnsubscribed(data); onUnsubscribed(data);
} }
@@ -462,22 +391,10 @@ class SocketService {
* @returns {Function} 取消订阅的函数 * @returns {Function} 取消订阅的函数
*/ */
subscribeToAllEvents(onNewEvent) { subscribeToAllEvents(onNewEvent) {
console.log('%c[socketService] 🔔 自动订阅所有事件...', 'color: #FF9800; font-weight: bold;');
// 如果没有提供回调,添加一个默认的日志回调
const defaultCallback = (event) => {
console.log('%c[socketService] 📨 收到新事件(默认回调)', 'color: #4CAF50; font-weight: bold;');
console.log('[socketService] 事件数据:', event);
};
this.subscribeToEvents({ this.subscribeToEvents({
eventType: 'all', eventType: 'all',
importance: 'all', importance: 'all',
onNewEvent: onNewEvent || defaultCallback, onNewEvent: onNewEvent || (() => {}),
onSubscribed: (data) => {
console.log('%c[socketService] ✅ 订阅成功!', 'color: #4CAF50; font-weight: bold;');
console.log('[socketService] 订阅确认:', data);
},
}); });
// 返回取消订阅的清理函数 // 返回取消订阅的清理函数