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