From 7c201f9083f060b2b85efe22954e644e09eba6a8 Mon Sep 17 00:00:00 2001 From: ayou <550244300@qq.com> Date: Mon, 15 Sep 2025 22:04:02 +0800 Subject: [PATCH] =?UTF-8?q?refactor(=E7=BB=84=E4=BB=B6):=20=E5=B0=86=20des?= =?UTF-8?q?troyOnClose=20=E6=9B=BF=E6=8D=A2=E4=B8=BA=20destroyOnHidden=20?= =?UTF-8?q?=E4=BB=A5=E4=BC=98=E5=8C=96=E7=BB=84=E4=BB=B6=E9=94=80=E6=AF=81?= =?UTF-8?q?=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit refactor(React工具): 重构 React 18 兼容性补丁,合并开发环境修复功能 优化多个组件中的销毁逻辑,统一使用 destroyOnHidden 替代 destroyOnClose。同时重构 React 18 兼容性补丁代码,将开发环境的相关修复功能整合到 setupReactDevFixes 方法中,提高代码可维护性。 --- frontend/src/flows/components/tools/index.tsx | 2 +- frontend/src/main.tsx | 35 +---- frontend/src/pages/FlowList.tsx | 4 +- frontend/src/pages/FlowRunLogs.tsx | 2 +- frontend/src/pages/Logs.tsx | 2 +- frontend/src/utils/react18-polyfill.ts | 121 +++++++++++++----- frontend/tsconfig.tsbuildinfo | 1 - 7 files changed, 98 insertions(+), 69 deletions(-) delete mode 100644 frontend/tsconfig.tsbuildinfo diff --git a/frontend/src/flows/components/tools/index.tsx b/frontend/src/flows/components/tools/index.tsx index c821416..6ae09fb 100644 --- a/frontend/src/flows/components/tools/index.tsx +++ b/frontend/src/flows/components/tools/index.tsx @@ -192,7 +192,7 @@ export const FlowTools = () => { onCancel={() => setBaseOpen(false)} okText={I18n.t('Save')} maskClosable={false} - destroyOnClose + destroyOnHidden > diff --git a/frontend/src/main.tsx b/frontend/src/main.tsx index faac1d4..c53b11a 100644 --- a/frontend/src/main.tsx +++ b/frontend/src/main.tsx @@ -6,40 +6,11 @@ import App from './App' import 'antd/dist/reset.css' import '@douyinfe/semi-ui/dist/css/semi.min.css' import './styles/global.css' -// 导入 React 18 兼容性补丁 -import { setupReact18Polyfill, setupDevSanitizeDOMProps } from './utils/react18-polyfill' +import { setupReactDevFixes } from './utils/react18-polyfill' -// 应用 React 18 兼容性补丁 -setupReact18Polyfill() -// 开发期:剔除会透传到原生 DOM 的非标准属性(localeCode/defaultCurrency/showCurrencySymbol) -setupDevSanitizeDOMProps() - -// 仅在开发环境过滤特定第三方库产生的已知无害告警 +// 仅在开发环境启用所有修复(幂等,不影响其他功能) if (import.meta.env.DEV) { - const shouldSuppress = (msg: unknown) => { - if (typeof msg !== 'string') return false - // semi-ui 某些组件链路在 React 18 下把非标准属性透传到 DOM,触发告警 - if (msg.includes('React does not recognize the `localeCode` prop on a DOM element')) return true - if (msg.includes('React does not recognize the `defaultCurrency` prop on a DOM element')) return true - if (msg.includes('React does not recognize the `showCurrencySymbol` prop on a DOM element')) return true - // 浏览器建议表单自动填充 - if (msg.includes('[DOM] Input elements should have autocomplete attributes')) return true - return false - } - - const origError = console.error - console.error = (...args: any[]) => { - const msg = args?.[0] - if (shouldSuppress(msg)) return - origError(...args) - } - - const origWarn = console.warn - console.warn = (...args: any[]) => { - const msg = args?.[0] - if (shouldSuppress(msg)) return - origWarn(...args) - } + setupReactDevFixes() } ReactDOM.createRoot(document.getElementById('root')!).render( diff --git a/frontend/src/pages/FlowList.tsx b/frontend/src/pages/FlowList.tsx index d0051ae..5377d26 100644 --- a/frontend/src/pages/FlowList.tsx +++ b/frontend/src/pages/FlowList.tsx @@ -251,7 +251,7 @@ export default function FlowList() { confirmLoading={editing} onCancel={() => { setEditOpen(false); setEditRow(null); setEditName(''); editForm.resetFields() }} okText="保存" - destroyOnClose + destroyOnHidden maskClosable={false} >
@@ -273,7 +273,7 @@ export default function FlowList() { open={createOpen} onOk={handleCreateOk} onCancel={() => setCreateOpen(false)} - destroyOnClose + destroyOnHidden maskClosable={false} > diff --git a/frontend/src/pages/FlowRunLogs.tsx b/frontend/src/pages/FlowRunLogs.tsx index 76c56ba..9f260cd 100644 --- a/frontend/src/pages/FlowRunLogs.tsx +++ b/frontend/src/pages/FlowRunLogs.tsx @@ -144,7 +144,7 @@ export default function FlowRunLogs() { pagination={{ current: page, pageSize, total, onChange: (p, ps) => fetchData(p, ps) }} /> - + {detail && ( diff --git a/frontend/src/pages/Logs.tsx b/frontend/src/pages/Logs.tsx index 0f23068..ffbd84a 100644 --- a/frontend/src/pages/Logs.tsx +++ b/frontend/src/pages/Logs.tsx @@ -138,7 +138,7 @@ export default function Logs() { pagination={{ current: page, pageSize, total, onChange: (p, ps) => fetchData(p, ps) }} /> - + {detail && ( diff --git a/frontend/src/utils/react18-polyfill.ts b/frontend/src/utils/react18-polyfill.ts index e7df1f4..2e7042d 100644 --- a/frontend/src/utils/react18-polyfill.ts +++ b/frontend/src/utils/react18-polyfill.ts @@ -1,67 +1,126 @@ /** - * React 18 兼容性补丁 + 开发期告警抑制 + * React 18 兼容性补丁 + 开发期告警修复 * - 解决第三方库使用旧版 ReactDOM.render API 的问题 * - 在开发环境下拦截并剔除部分第三方库会误透传到原生 DOM 的非标准属性 */ -import * as ReactDOM from 'react-dom/client'; -import * as React from 'react'; -import { unstableSetCreateRoot } from '@flowgram.ai/form-materials'; +import * as ReactDOM from 'react-dom/client' +import * as React from 'react' +import { unstableSetCreateRoot } from '@flowgram.ai/form-materials' // 设置使用 React 18 createRoot API 的实现 export function setupReact18Polyfill() { unstableSetCreateRoot((dom: HTMLElement) => { - const root = ReactDOM.createRoot(dom); + const root = ReactDOM.createRoot(dom) return { render(children: React.ReactNode) { - root.render(children); + root.render(children) }, // 将 root.unmount 延迟到当前渲染完成之后再执行,避免 React 的同步卸载告警 unmount() { - const doUnmount = () => root.unmount(); + const doUnmount = () => root.unmount() if (typeof queueMicrotask === 'function') { - queueMicrotask(doUnmount); + queueMicrotask(doUnmount) } else if (typeof Promise !== 'undefined') { - Promise.resolve().then(doUnmount); + Promise.resolve().then(doUnmount) } else { - setTimeout(doUnmount, 0); + setTimeout(doUnmount, 0) } }, - }; - }); - console.log('React 18 polyfill has been applied'); + } + }) + if (typeof console !== 'undefined' && console.debug) { + console.debug('React 18 polyfill has been applied') + } } // 开发环境:剔除被第三方库误透传到原生 DOM 的非标准属性,防止 React 告警 export function setupDevSanitizeDOMProps() { - if (!import.meta.env.DEV) return; - const ANY_REACT = React as any; + if (!import.meta.env.DEV) return + const ANY_REACT = React as any // 避免重复打补丁 - if (ANY_REACT.__patched_createElement__) return; + if (ANY_REACT.__patched_createElement__) return - const origCreateElement = React.createElement as any; - const STRIP_KEYS = new Set(['localeCode', 'defaultCurrency', 'showCurrencySymbol']); + const origCreateElement = React.createElement as any + const STRIP_KEYS = new Set(['localeCode', 'defaultCurrency', 'showCurrencySymbol']) const patchedCreateElement = (type: any, props: any, ...children: any[]) => { if (typeof type === 'string' && props && typeof props === 'object') { - let mutated = false; - const nextProps: any = {}; + let mutated = false + const nextProps: any = {} for (const key in props) { if (Object.prototype.hasOwnProperty.call(props, key)) { if (STRIP_KEYS.has(key)) { - mutated = true; - continue; + mutated = true + continue } - nextProps[key] = props[key]; + nextProps[key] = props[key] } } - return origCreateElement(type, mutated ? nextProps : props, ...children); + return origCreateElement(type, mutated ? nextProps : props, ...children) } - return origCreateElement(type, props, ...children); - }; - - (React as any).createElement = patchedCreateElement; - ANY_REACT.__patched_createElement__ = true; - if (typeof console !== 'undefined' && console.debug) { - console.debug('[DEV] React.createElement patched to sanitize DOM props'); + return origCreateElement(type, props, ...children) } + + ;(React as any).createElement = patchedCreateElement + ANY_REACT.__patched_createElement__ = true + if (typeof console !== 'undefined' && console.debug) { + console.debug('[DEV] React.createElement patched to sanitize DOM props') + } +} + +// 开发环境:抑制特定第三方库产生的已知无害告警(仅字符串匹配,不影响其他日志) +export function setupDevConsoleSuppression() { + if (!import.meta.env.DEV) return + const anyConsole: any = console as any + if (anyConsole.__patched_console__) return + + const shouldSuppressArgs = (args: any[]): boolean => { + try { + const joined = args + .map((a) => { + if (typeof a === 'string') return a + if (a instanceof Error && a.message) return a.message + try { + return JSON.stringify(a) + } catch { + return String(a) + } + }) + .join(' ') + + const hitsReactUnrecognized = joined.includes('React does not recognize the') + const hitsKnownKeys = joined.includes('localeCode') || joined.includes('defaultCurrency') || joined.includes('showCurrencySymbol') + + if (hitsReactUnrecognized && hitsKnownKeys) return true + if (joined.includes('[DOM] Input elements should have autocomplete attributes')) return true + return false + } catch { + return false + } + } + + const origError = console.error + const origWarn = console.warn + + console.error = (...args: any[]) => { + if (shouldSuppressArgs(args)) return + origError(...args) + } + console.warn = (...args: any[]) => { + if (shouldSuppressArgs(args)) return + origWarn(...args) + } + + anyConsole.__patched_console__ = true + if (typeof console !== 'undefined' && console.debug) { + console.debug('[DEV] console.* patched to suppress known harmless warnings') + } +} + +// 聚合:一次性启用所有开发期修复(幂等) +export function setupReactDevFixes() { + if (!import.meta.env.DEV) return + setupReact18Polyfill() + setupDevSanitizeDOMProps() + setupDevConsoleSuppression() } \ No newline at end of file diff --git a/frontend/tsconfig.tsbuildinfo b/frontend/tsconfig.tsbuildinfo deleted file mode 100644 index b698303..0000000 --- a/frontend/tsconfig.tsbuildinfo +++ /dev/null @@ -1 +0,0 @@ -{"root":["./src/app.tsx","./src/main.tsx","./src/vite-env.d.ts","./src/components/pageheader.tsx","./src/flows/app.tsx","./src/flows/editor.tsx","./src/flows/index.ts","./src/flows/initial-data.ts","./src/flows/type.d.ts","./src/flows/assets/icon-auto-layout.tsx","./src/flows/assets/icon-cancel.tsx","./src/flows/assets/icon-comment.tsx","./src/flows/assets/icon-minimap.tsx","./src/flows/assets/icon-mouse.tsx","./src/flows/assets/icon-pad.tsx","./src/flows/assets/icon-success.tsx","./src/flows/assets/icon-switch-line.tsx","./src/flows/assets/icon-warning.tsx","./src/flows/components/index.ts","./src/flows/components/add-node/index.tsx","./src/flows/components/add-node/use-add-node.ts","./src/flows/components/base-node/index.tsx","./src/flows/components/base-node/node-wrapper.tsx","./src/flows/components/base-node/styles.tsx","./src/flows/components/base-node/utils.ts","./src/flows/components/comment/constant.ts","./src/flows/components/comment/index.ts","./src/flows/components/comment/model.ts","./src/flows/components/comment/type.ts","./src/flows/components/comment/components/blank-area.tsx","./src/flows/components/comment/components/border-area.tsx","./src/flows/components/comment/components/container.tsx","./src/flows/components/comment/components/content-drag-area.tsx","./src/flows/components/comment/components/drag-area.tsx","./src/flows/components/comment/components/editor.tsx","./src/flows/components/comment/components/index.ts","./src/flows/components/comment/components/more-button.tsx","./src/flows/components/comment/components/render.tsx","./src/flows/components/comment/components/resize-area.tsx","./src/flows/components/comment/hooks/index.ts","./src/flows/components/comment/hooks/use-model.ts","./src/flows/components/comment/hooks/use-overflow.ts","./src/flows/components/comment/hooks/use-size.ts","./src/flows/components/group/color.ts","./src/flows/components/group/constant.ts","./src/flows/components/group/index.ts","./src/flows/components/group/components/background.tsx","./src/flows/components/group/components/color.tsx","./src/flows/components/group/components/header.tsx","./src/flows/components/group/components/icon-group.tsx","./src/flows/components/group/components/index.ts","./src/flows/components/group/components/node-render.tsx","./src/flows/components/group/components/title.tsx","./src/flows/components/group/components/tools.tsx","./src/flows/components/group/components/ungroup.tsx","./src/flows/components/group/components/tips/global-store.ts","./src/flows/components/group/components/tips/icon-close.tsx","./src/flows/components/group/components/tips/index.tsx","./src/flows/components/group/components/tips/is-mac-os.ts","./src/flows/components/group/components/tips/style.ts","./src/flows/components/group/components/tips/use-control.ts","./src/flows/components/line-add-button/button.tsx","./src/flows/components/line-add-button/index.tsx","./src/flows/components/line-add-button/use-visible.ts","./src/flows/components/node-menu/index.tsx","./src/flows/components/node-panel/index.tsx","./src/flows/components/node-panel/node-list.tsx","./src/flows/components/node-panel/node-placeholder.tsx","./src/flows/components/selector-box-popover/index.tsx","./src/flows/components/sidebar/index.tsx","./src/flows/components/sidebar/sidebar-node-renderer.tsx","./src/flows/components/sidebar/sidebar-provider.tsx","./src/flows/components/sidebar/sidebar-renderer.tsx","./src/flows/components/testrun/hooks/index.ts","./src/flows/components/testrun/hooks/use-fields.ts","./src/flows/components/testrun/hooks/use-form-meta.ts","./src/flows/components/testrun/hooks/use-sync-default.ts","./src/flows/components/testrun/node-status-bar/index.tsx","./src/flows/components/testrun/node-status-bar/group/index.tsx","./src/flows/components/testrun/node-status-bar/header/index.tsx","./src/flows/components/testrun/node-status-bar/render/index.tsx","./src/flows/components/testrun/node-status-bar/viewer/index.tsx","./src/flows/components/testrun/testrun-button/index.tsx","./src/flows/components/testrun/testrun-form/index.tsx","./src/flows/components/testrun/testrun-form/type.ts","./src/flows/components/testrun/testrun-json-input/index.tsx","./src/flows/components/testrun/testrun-panel/index.tsx","./src/flows/components/tools/auto-layout.tsx","./src/flows/components/tools/comment.tsx","./src/flows/components/tools/fit-view.tsx","./src/flows/components/tools/index.tsx","./src/flows/components/tools/interactive.tsx","./src/flows/components/tools/minimap-switch.tsx","./src/flows/components/tools/minimap.tsx","./src/flows/components/tools/mouse-pad-selector.tsx","./src/flows/components/tools/readonly.tsx","./src/flows/components/tools/save.tsx","./src/flows/components/tools/styles.tsx","./src/flows/components/tools/switch-line.tsx","./src/flows/components/tools/zoom-select.tsx","./src/flows/context/index.ts","./src/flows/context/node-render-context.ts","./src/flows/context/sidebar-context.ts","./src/flows/form-components/feedback.tsx","./src/flows/form-components/index.ts","./src/flows/form-components/form-content/index.tsx","./src/flows/form-components/form-content/styles.tsx","./src/flows/form-components/form-header/index.tsx","./src/flows/form-components/form-header/styles.tsx","./src/flows/form-components/form-header/title-input.tsx","./src/flows/form-components/form-header/utils.tsx","./src/flows/form-components/form-inputs/index.tsx","./src/flows/form-components/form-inputs/styles.tsx","./src/flows/form-components/form-item/index.tsx","./src/flows/hooks/index.ts","./src/flows/hooks/use-editor-props.tsx","./src/flows/hooks/use-is-sidebar.ts","./src/flows/hooks/use-node-render-context.ts","./src/flows/hooks/use-port-click.ts","./src/flows/nodes/constants.ts","./src/flows/nodes/default-form-meta.tsx","./src/flows/nodes/index.ts","./src/flows/nodes/block-end/form-meta.tsx","./src/flows/nodes/block-end/index.ts","./src/flows/nodes/block-start/form-meta.tsx","./src/flows/nodes/block-start/index.ts","./src/flows/nodes/break/form-meta.tsx","./src/flows/nodes/break/index.ts","./src/flows/nodes/code/form-meta.tsx","./src/flows/nodes/code/index.tsx","./src/flows/nodes/code/types.tsx","./src/flows/nodes/code/components/code.tsx","./src/flows/nodes/code/components/inputs.tsx","./src/flows/nodes/code/components/outputs.tsx","./src/flows/nodes/comment/index.tsx","./src/flows/nodes/condition/form-meta.tsx","./src/flows/nodes/condition/index.ts","./src/flows/nodes/condition/condition-inputs/index.tsx","./src/flows/nodes/condition/condition-inputs/styles.tsx","./src/flows/nodes/continue/form-meta.tsx","./src/flows/nodes/continue/index.ts","./src/flows/nodes/db/form-meta.tsx","./src/flows/nodes/db/index.tsx","./src/flows/nodes/end/form-meta.tsx","./src/flows/nodes/end/index.ts","./src/flows/nodes/group/index.tsx","./src/flows/nodes/http/form-meta.tsx","./src/flows/nodes/http/index.tsx","./src/flows/nodes/http/types.tsx","./src/flows/nodes/http/components/api.tsx","./src/flows/nodes/http/components/body.tsx","./src/flows/nodes/http/components/headers.tsx","./src/flows/nodes/http/components/params.tsx","./src/flows/nodes/http/components/timeout.tsx","./src/flows/nodes/llm/index.ts","./src/flows/nodes/loop/form-meta.tsx","./src/flows/nodes/loop/index.ts","./src/flows/nodes/start/form-meta.tsx","./src/flows/nodes/start/index.ts","./src/flows/nodes/variable/form-meta.tsx","./src/flows/nodes/variable/index.tsx","./src/flows/nodes/variable/types.tsx","./src/flows/plugins/index.ts","./src/flows/plugins/context-menu-plugin/context-menu-layer.tsx","./src/flows/plugins/context-menu-plugin/context-menu-plugin.ts","./src/flows/plugins/context-menu-plugin/index.ts","./src/flows/plugins/runtime-plugin/create-runtime-plugin.ts","./src/flows/plugins/runtime-plugin/index.ts","./src/flows/plugins/runtime-plugin/type.ts","./src/flows/plugins/runtime-plugin/client/base-client.ts","./src/flows/plugins/runtime-plugin/client/index.ts","./src/flows/plugins/runtime-plugin/client/browser-client/index.ts","./src/flows/plugins/runtime-plugin/client/server-client/constant.ts","./src/flows/plugins/runtime-plugin/client/server-client/index.ts","./src/flows/plugins/runtime-plugin/client/server-client/type.ts","./src/flows/plugins/runtime-plugin/runtime-service/index.ts","./src/flows/plugins/variable-panel-plugin/index.ts","./src/flows/plugins/variable-panel-plugin/variable-panel-layer.tsx","./src/flows/plugins/variable-panel-plugin/variable-panel-plugin.ts","./src/flows/plugins/variable-panel-plugin/components/full-variable-list.tsx","./src/flows/plugins/variable-panel-plugin/components/global-variable-editor.tsx","./src/flows/plugins/variable-panel-plugin/components/variable-panel.tsx","./src/flows/services/custom-service.ts","./src/flows/services/index.ts","./src/flows/shortcuts/constants.ts","./src/flows/shortcuts/index.ts","./src/flows/shortcuts/shortcuts.ts","./src/flows/shortcuts/type.ts","./src/flows/shortcuts/collapse/index.ts","./src/flows/shortcuts/copy/index.ts","./src/flows/shortcuts/delete/index.ts","./src/flows/shortcuts/expand/index.ts","./src/flows/shortcuts/paste/index.ts","./src/flows/shortcuts/paste/traverse.ts","./src/flows/shortcuts/paste/unique-workflow.ts","./src/flows/shortcuts/select-all/index.ts","./src/flows/shortcuts/zoom-in/index.ts","./src/flows/shortcuts/zoom-out/index.ts","./src/flows/typings/index.ts","./src/flows/typings/json-schema.ts","./src/flows/typings/node.ts","./src/flows/utils/index.ts","./src/flows/utils/on-drag-line-end.ts","./src/flows/utils/toggle-loop-expanded.ts","./src/flows/utils/yaml.ts","./src/layouts/mainlayout.tsx","./src/pages/dashboard.tsx","./src/pages/departments.tsx","./src/pages/flowlist.tsx","./src/pages/flowrunlogs.tsx","./src/pages/login.tsx","./src/pages/logs.tsx","./src/pages/menus.tsx","./src/pages/permissions.tsx","./src/pages/positions.tsx","./src/pages/roles.tsx","./src/pages/users.tsx","./src/utils/axios.ts","./src/utils/config.ts","./src/utils/datetime.ts","./src/utils/permission.tsx","./src/utils/react18-polyfill.ts","./src/utils/token.ts"],"version":"5.9.2"} \ No newline at end of file