# 内存泄漏与大内存/大计算风险审计 审计日期:2026-04-27 审计范围:`apps/lcdp/src/**`、`packages/**`、`docs/**`、根目录配置与现有 `perf/**` 文档。排查方式为静态代码扫描加重点文件复核,重点关注全局监听、路由守卫、事件总线、定时器、observer、第三方实例、Blob URL、长连接/流式请求、无限缓存、全量拉取、深拷贝、深度监听、递归和批量计算。 执行方式:主线程扫描与复核 + 4 个并行 agent 分区审计。分区覆盖 `apps/lcdp`、`packages/business-components`、`packages/form-designer`、`packages/business-module`、`packages/core-utils`、构建脚本和既有 `perf` 性能文档。本文已合并 agent 回传结果。 风险等级说明: | 等级 | 含义 | | ---- | ------------------------------------------------------------------------------------ | | 高 | 较确定会在路由切换、组件卸载、微前端重复挂载后残留;或单次操作能产生明显大内存峰值。 | | 中高 | 泄漏或大内存风险依赖数据量/交互路径,但影响面较大。 | | 中 | 有残留或性能风险,需要补生命周期或限流;通常不会立即导致崩溃。 | | 低 | 边界场景风险,建议随相关模块迭代修复。 | ## 总体结论 当前项目最需要优先治理的不是单一组件 bug,而是几类长期对象缺少统一释放机制: 1. 微前端重复挂载相关:`main.ts`、路由守卫、平台 message handler、全局 bus、全局请求配置都缺少统一 disposer。 2. 第三方实例相关:ERD Editor、Monaco Editor、自定义组件渲染、文档中心 SDK、Sortable 的实例生命周期不完整。 3. 指令/组件监听相关:部分 DOM 监听、window 监听、watch、debounce、timeout 未在卸载时清理。 4. 大数据路径相关:多处 `size: 999/9999/99999`、全量缓存、深拷贝、深度 watch、`JSON.stringify` 大对象会造成明显内存峰值和主线程阻塞。 5. 构建脚本相关:多个 package 的 `build` 同时运行 `vue-tsc --build` 与 `vite build`,再叠加 `vite-plugin-dts`、sourcemap 或 postinstall 自动构建,会放大 CI 和本地 install/build 的峰值内存。 建议优先级: 1. 先修高风险全局残留:路由守卫、postMessage、DataModelView、Monaco、自定义组件渲染、InputFormat 指令。 2. 再修卸载后异步任务:search input、plugin select、FieldDialog、UseModelAuth、Preview debounce、DocLib。 3. 最后做大数据治理:全量拉取改分页/虚拟列表,深拷贝和深度 watch 改增量 dirty 标记,AI prompt 和 ERD 数据做裁剪或 worker 化。 4. 构建阶段峰值内存单独治理:先把并行 type-check/build 改为可配置串行,再处理 dts rollup、sourcemap 和 postinstall 自动构建。 ## 高风险:确定性残留或高概率泄漏 | 位置 | 风险 | 证据 | 影响 | 建议 | | ---------------------------------------------------------------------------------------------------------------------- | ---- | --------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------- | | `apps/lcdp/src/router/navigation-guards/agentGuards.ts:4` | 高 | `initAgentRouterGuards()` 内注册 `router.afterEach`,未保存注销函数;`apps/lcdp/src/main.ts:147` 每次 render 都调用。 | 微前端反复挂载时导航守卫叠加,闭包保留 agent 注册逻辑。 | 模块级 installed guard;保存 `const remove = router.afterEach(...)`;在子应用卸载时调用。 | | `apps/lcdp/src/feature/modeling/components/model-details/entity-model/DataModelView.vue:13` | 高 | 覆盖 `Element.prototype.attachShadow`,未恢复。 | 路由切走后全局 monkey patch 继续影响所有组件和第三方库。 | 避免全局 patch;必须 patch 时幂等保存原函数,并在 `onUnmounted` 恢复。 | | `apps/lcdp/src/feature/modeling/components/model-details/entity-model/DataModelView.vue:299` | 高 | 主/子 ERD editor 添加 `contextmenu/click` 监听、`setInterval`、`MutationObserver`,未统一移除或 disconnect。 | observer/timer/listener 持有 editor、shadowRoot 和组件闭包,路由切换后残留。 | 保存 listener、timer、observer 句柄;返回主视图和卸载时统一 `removeEventListener/clearInterval/clearTimeout/disconnect`。 | | `apps/lcdp/src/feature/page-preview/hook/UsePostMessage.ts:124` | 高 | 注册 `registerPostMessageHandle` 三个全局 handler,卸载时只清理临时数据。 | handler 持有 `previewRef/baseInfo`,页面重进会重复响应。 | `registerPostMessageHandle` 返回 unregister;或补 `unregisterPostMessageHandle(type, handler)`,卸载时注销。 | | `packages/core-utils/src/utils/PostMessageUtil.ts:42` | 高 | `postMessageHandleMap[type] = handle` 无 unregister;`openListen` 注册匿名 `message` listener 无关闭能力。 | 全局 handler 表只能覆盖不能释放;message listener 生命周期不可控。 | 补 unregister 和 `closeListen`;监听函数用具名引用;仅在 handler/asyncMap 非空时保持监听。 | | `packages/business-components/src/components/common/monaco-editor/MonacoEditor.vue:64` | 高 | 创建 editor 后没有 `onBeforeUnmount`;`pluginRuntime.dispose()` 未调用。`Plugins.ts:70` 的 dispose 逻辑只有其内部 effectScope stop 后才会执行。 | Monaco editor、model、监听、CodeLens provider、resize/key listeners 可能跨组件残留。 | `onBeforeUnmount(() => pluginRuntime.dispose())`,并在 runtime dispose 中 `editor.dispose()`、清理 editor listeners。 | | `packages/business-components/src/components/common/monaco-editor/EditorWorker.ts:33` | 高 | 为 worker loader 创建 `blobUrl` 后直接缓存 worker,未 `URL.revokeObjectURL(blobUrl)`,也未提供 worker terminate/clear API。 | Monaco 多语言 worker 常驻是可接受的设计,但 Blob URL 与 worker 生命周期不可控;热更新、微前端重挂载或多版本加载会累积资源。 | worker 创建后 revoke blob URL;提供 `disposeMonacoWorkers()`,在子应用卸载或版本切换时 terminate 并清空 `workerCache`。 | | `packages/form-designer/src/config/widget/CustomComponentRender.vue:119` | 高 | `renderFrameworkESM` 返回 `destroy`,调用方只保存 `componentInstance`;卸载时只尝试 `$destroy`。UMD Vue3/React 分支也没有 app/root unmount 返回。 | 自定义组件的 Vue/React 子应用、响应式 scope、DOM 和事件可能无法释放。 | 调用方保存 renderer handle,卸载调用 `handle.destroy()`;`renderFramework` 的 Vue3/React/Vue2 分支统一返回 `destroy`。 | | `apps/lcdp/src/directives/InputFormatHandle.ts:27` | 高 | `focus/blur` 使用匿名 listener,`watch([value, formatterValue], ...)` 无 stop,且无 `unmounted`。 | 指令卸载后 DOM 监听和 watcher 持有 input、value、formatterValue。 | handler 和 `stop` 存到 `el`,`unmounted` 中 remove + stop。 | | `packages/form-designer/src/config/widget/CustomMobileNumber.vue:264` | 高 | watch 中创建 `ResizeObserver`,没有保存,也没有 `disconnect`。 | wrapper 变化或组件卸载后 observer 残留。 | 保存 observer;watch cleanup 或 `onBeforeUnmount` 中 disconnect。 | | `packages/business-components/src/components/common/table/hooks/UseConfig.ts:23` | 高 | `new Sortable(el, ...)` 未保存实例,未 `destroy()`;`tableKey` 变化会重复初始化。 | 同一表格 tbody 上叠加拖拽监听和闭包。 | 用 `let sortable: Sortable | null`;重新初始化前和卸载时`sortable.destroy()`。 | | `packages/form-designer/src/components/file-preview/doc-lib-preview/UseDocLib.ts:90` | 高 | `DocumentCenterSdk` 初始化后,`onBeforeUnmount` 中 `destroy()` 被注释。 | SDK iframe/overlay/message listener/会话资源可能残留。 | 需要和 SDK 方确认 `destroy` bug;至少调用 `destroyCurrentSession()`,并清理 debounce executor。 | | `packages/form-designer/src/config/widget/file-vxe/utils/fileVxe.ts:249`、`:272` | 高 | `eventBus.on(BATCH_DELETE_ATTACHMENT, () => {})` 和 `eventBus.on(BATCH_DOWNLOAD_ATTACHMENT, () => {})` 使用匿名函数,`onBeforeUnmount` 只清字段变更监听。 | 每次附件表格挂载都会保留闭包和 `vxeGridRef/pageConfig/fieldtotal`。 | 将两个 handler 提升为具名函数,并在 `onBeforeUnmount` 成对 `eventBus.off(type, handler)`。 | | `packages/business-module/src/components/script-panel/ScriptRightPanel.vue:89` | 高 | 注册的是匿名包装函数,卸载时传入 `handlerSwitch` 执行 `off`。 | on/off 函数引用不一致,监听无法移除。 | 保存 `const handleSwitchRightHeader = (...) => { ... }`,注册和移除使用同一引用。 | | `packages/form-designer/src/config/settings/event-list/components/ActionContent.vue:247` | 高 | `eventBus.on(..., (id) => customTreeRef.value?.remove(id))` 无对应卸载清理。 | 会保留 `customTreeRef/pageConfig/jsData` 等上下文,事件页反复打开后监听叠加。 | 增加命名 handler;`onBeforeUnmount` 中 `eventBus.off(type, handler)`,并清理 `setTimeout`。 | | `apps/lcdp/src/feature/modeling/components/custom-component/group-tree-select/component/group-panel/store/group.ts:37` | 高 | 模块级 `storeCache: Map` 只增不删。 | 切换多个 app 后 store 构造器和 group 数据长期驻留。 | 改单 store keyed state;或提供 `clearGroupStore(appId)`/LRU,并在离开应用时清理。 | | `apps/lcdp/src/App.vue:20`、`apps/lcdp/src/AppMobile.vue:15` | 中高 | `` 无 `max/include/exclude`。 | runtime 页面、设计器、预览页长期保留组件实例、请求状态和第三方实例。 | 设置 `max`;只缓存明确需要的轻页面;重页面禁用缓存或实现淘汰策略。 | ## 中高风险:卸载后异步任务、全局注册和常驻 UI | 位置 | 风险 | 证据 | 影响 | 建议 | | ------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------ | --------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------ | | `apps/lcdp/src/main.ts:139`、`:150`、`:174` | 中高 | `requestIdleCallback`、`setTimeout` 未保存 handle。 | 微前端卸载后仍可能执行组件注册、语言包加载,闭包持有 app。 | 保存 idle/timeout id;unmount 时 cancel;回调中判断 app 是否仍有效。 | | `apps/lcdp/src/utils/UsePlatformPostMessageHandler.ts:7` | 中高 | 底层 `init()` 注册 window message,未接收 disposer。 | 重复初始化可能叠加平台 message listener。 | 包装为幂等单例,暴露 destroy;确认底层是否支持 stop。 | | `apps/lcdp/src/utils/MsgPackInterceptors.ts:5` | 中高 | 每次调用注册 axios interceptors,未保存 id,未 eject。当前 main 中注释,但启用后有风险。 | 重复挂载导致拦截器叠加。 | installed guard;保存 request/response id;cleanup 时 eject。 | | `apps/lcdp/src/utils/mobileAuthUtils.ts:32` | 中 | `execAutoRefreshToken(reIns.getAxiosIns())` 无 stop/幂等保护。 | 自动刷新定时器或拦截器可能重复注册。 | 确认底层返回值;增加单例 guard 和 stop。 | | `apps/lcdp/src/components/common/search-input/index.vue:29`、`:59` | 中高 | debounce timeout 未卸载清理;focus 时 window keydown,blur 时移除。 | 聚焦状态下组件销毁可能残留 window listener;最后一个 timeout 仍写状态/emit。 | `onBeforeUnmount` 中 `clearTimeout(timeout)` 和 `removeEventListener`。 | | `apps/lcdp/src/directives/ImageHandle.ts:32`、`:45` | 中 | 原生 img 与内部 img 使用匿名 `error/load` handler;卸载只 disconnect observer。 | 已绑定到真实 img 的 listener 不能精准移除。 | 记录 img 与 handler,卸载时 remove;移除临时 img 并清空字段。 | | `apps/lcdp/src/components/business-common/plugin-select/hook/usePluginSelect.ts:91`、`:105`、`:197`、`:228`、`:265` | 中 | 请求完成后继续写 state;多处 timeout 未清理。 | 卸载后异步回调操作 DOM/ref;请求结果过期仍覆盖新状态。 | disposed flag 或 request id;保存 timeout id 数组并在卸载清理。 | | `apps/lcdp/src/components/business-common/field-dialog/FieldDialog.vue:120` | 中 | 请求完成后写 `list/total/status`,关闭弹窗只重置 `isInit`。 | 弹窗关闭/卸载后旧请求仍写状态。 | 请求版本号或 AbortController;关闭/卸载时忽略旧响应。 | | `packages/form-designer/src/components/btn-action/components/model-import-export/components/file/File.vue:22` | 中 | 上传态 `setInterval` 只在 `state.type` 变化时清理,没有卸载清理。 | 弹窗或组件在上传态关闭时 interval 留存,继续写 `progress`。 | 引入 `onUnmounted(() => clearInterval(timer))`;重新进入 upload 前先清旧 interval。 | | `packages/core-utils/src/hooks/UseBatchDownload.ts:248` | 中 | 每次调度都向外部 `AbortSignal` 添加 `abort` listener,未在调度结束后移除。 | 长生命周期 signal 多次下载会积累 listener;listener 闭包持有 controller。 | 使用 `{ once: true }`,或保存 handler 并在 scheduler resolve/finally 后 remove。 | | `apps/lcdp/src/feature/modeling/components/model-details/entity-model/CurrentModeInfo.vue:116`、`:145` | 中 | lodash debounce 未 cancel;tooltip timeout 未清理。 | pending debounce 可能在卸载后重新绑定 DOM 监听。 | `deCostFormMounted.cancel()`、`clearTimeout(time)`,并加 disposed guard。 | | `apps/lcdp/src/feature/page-preview/Preview.vue:71`、`PreviewMobile.vue:94` | 中 | lodash debounce 未 cancel;多个 deep watch 改 `key` 触发重挂载。 | 失活/卸载后 pending 路由回调写状态;深 watch 扩大重渲染面。 | `onDeactivated/onUnmounted` cancel;watch 标量数组而非 deep object。 | | `apps/lcdp/src/feature/modeling/components/model-details/auth-control/UseModelAuth.ts:34`、`:66` | 中 | debounced save/fetch 未 cancel;请求无 stale 判断。 | 权限页卸载后延迟保存/拉取继续持有 `authInfo`。 | 保存 debounced fetch 引用;卸载时 cancel/flush;请求加 stale token。 | | `apps/lcdp/src/feature/modeling/components/list/business-model/BusinessModelList.vue:222`、`page-model/PageModelList.vue:182`、`mixed-list/MixedList.vue:191` | 中高 | `showMessage({ duration: 0, append: h(... onClick ...) })`。 | 全局常驻 message 闭包持有组件方法和列表数据。 | 保存 message handle,卸载或列表刷新时 close;设置有限 duration;点击时按 id 查询当前数据。 | | `packages/form-designer/src/config/widget/list-vxe/composables/fetch.ts:196` | 中 | `getTableData` 是 lodash debounce;文件内未见 `onUnmounted`/`onScopeDispose` 中 `.cancel()`。 | 列表卸载后 pending debounce 仍可能触发请求或写入已失效的 ref;已有 abort 只在函数执行后才生效。 | 作用域销毁时 `getTableData.cancel()` 并 `abortPendingRequest()`。 | | `packages/business-components/src/utils/VirtualTooltip.ts:5` | 中 | 全局 tooltip container/vnode/currentEl/timer 无销毁 API。 | 长生命周期单例可接受,但当前元素引用和 popper listener 可能滞留到下一次显示。 | 提供 `destroyGlobalVirtualTooltip()`;隐藏时清空 virtualRef/currentEl 并清理 timer。 | | `packages/business-components/src/composables/VirtualPopover.tsx:70`、`:222` | 低到中 | unmount 时 off 了 mitt,但 debounce open/close 未 cancel。 | pending debounce 可能在根组件卸载后改全局 state。 | `onUnmounted` 中 cancel `debounceOpenPopover/debounceClosePopover`。 | ## 大内存和大计算热点 | 位置 | 风险 | 证据 | 影响 | 建议 | | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------ | ------------------------------------------------------------------------------------------------------ | -------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------- | | `apps/lcdp/src/feature/modeling/components/model-details/entity-model/DataModelView.vue:87`、`:142`、`:377` | 高 | 每 200ms 递归 `querySelectorAll('*')` 扫 Shadow DOM 并重新注入样式;ERD 数据整体 `JSON.stringify`。 | 大 ER 图下 CPU 高、短期对象多,可能触发频繁 GC。 | 优先使用 ERD API/配置隐藏工具栏;observer 只监听目标节点;大数据转换和序列化移 worker 或限量。 | | `apps/lcdp/src/service/BillTransferService.ts:67` | 高 | 流式 AI 响应 `accumulatedContent += value`;`cancelFn` 到 onDone 才随 resolve 返回。 | 调用方请求中途拿不到取消句柄;页面卸载后流继续累积大字符串。 | 立即返回 `{ promise, cancelFn }` 或接收 `AbortSignal`;限制累计长度;取消/失败时清空缓冲。 | | `apps/lcdp/src/feature/bill-transfer/hooks/useFieldMappingAI.ts:117`、`:121`、`:147`、`:201` | 中高 | `flatTableData` 有数据就不重建;`cloneDeep(tableData)`;Map 持有行对象;`JSON.stringify(pairs)`。 | 表格变更后缓存不失效;大映射序列化和 Map 引用旧行。 | 按 source/target/tableData 版本重建;完成后清空 Map;AI 入参分批或裁剪字段。 | | `apps/lcdp/src/service/ModelingService.ts:67` | 中高 | 导出模型 `JSON.stringify(res, null, 2)`,再 Blob 和 `URL.createObjectURL`,未 revoke。 | 大模型导出有字符串+Blob 双份内存峰值;Blob URL 泄漏。 | 复用 `downloadBlob` 或点击后 `URL.revokeObjectURL(url)`;大数据后端直接返回 blob/流。 | | `apps/lcdp/src/service/ApplicationService.ts:95`、`:203` | 中高 | `size: 99999`;递归转换中反复复制 path 数组。 | 大租户业务/应用树会带来网络、内存和 CPU 压力。 | 后端分页/懒加载;限制 size;路径用 parentId 或共享数组迭代构建。 | | `apps/lcdp/src/components/business-common/business-dropdown/index.vue:28` | 中 | `size: 99999` 拉全量,再前端 slice 展示。 | 大业务分类时前端持有全量列表。 | 分页/远程搜索;输入 debounce;不要保留全量。 | | `apps/lcdp/src/components/business-common/field-dialog/FieldDialog.vue:125`、`:177` | 中 | 主列表和子节点都 `size: 999`,展开时 map/filter。 | 引用字段树懒加载可能多次拉大列表和复制数组。 | 后端分页/限制层级;按 referenceId 缓存;只加载必要字段。 | | `apps/lcdp/src/components/common/search-select/index.vue:69` | 低到中 | `watch(searchOptions, ..., { deep: true })`。 | 选项多时深度遍历成本高。 | 改浅 watch 或赋值后直接同步 `resShowOptions`。 | | `apps/lcdp/src/components/business-common/app-select/index.vue:34` | 低 | 打开弹窗 `cloneDeep(list)`。 | list 较大时开窗产生完整副本。 | 只复制会被修改字段;用浅拷贝加按需 clone。 | | `apps/lcdp/src/feature/modeling/components/model-details/entity-model/hooks/UseTreeLogic.ts:229`、`:263`、`:387`、`:632` | 中高 | 多处 `cloneDeep` 整树/对象;深度 watch `currentSelectedData` 后 `treeEvent.editNode(cloneDeep(...))`。 | 字段属性编辑会频繁触发树节点重写和深拷贝。 | 只 watch 模型基础字段;字段表单变更不要驱动树;dirty/version 标记替代深度 watch。 | | `apps/lcdp/src/feature/modeling/components/model-details/entity-model/hooks/UseModelAndFieldChange.ts:31`、`Util.ts:7` | 中高 | `requestDataIsChange` 对整个 requestData 做 `isEqual`;复制模型路径多处 `cloneDeep`。 | 模型/子模型/字段操作列表越大,任意响应式变更都可能触发递归比较。 | 维护增量 dirty flag;按 operation list 判断变更;避免 computed 中全量深比较。 | | `apps/lcdp/src/feature/modeling/components/model-details/feature-variable/hooks/useFeatureGroup.ts:225`、`:245`、`:304`、`:342` | 中高 | `tableListGather` 缓存全量数据;分页/搜索前 `cloneDeep` 全量列表; debounce 未 cancel。 | 数据多时内存翻倍,分页和搜索 CPU 高。 | 服务端分页/搜索;只克隆当前页;卸载清空缓存并 cancel debounce。 | | `apps/lcdp/src/feature/print/hooks/print.ts:197`、`Config/utils.ts:12`、`:33` | 中 | computed 中反复 flatten tree;节点上挂 `parent`;transform 写 parent/path。 | 大打印模型树会产生大量临时对象和父链引用。 | 数据变化时一次性构建 `_path -> node` Map;关闭弹窗清理;节点只存 parentId/path。 | | `apps/lcdp/src/feature/modeling/components/list/business-model/BusinessModelList.vue:112`、`page-model/PageModelList.vue:222`、`mixed-list/MixedList.vue:518`、`:545` | 中 | 批量引用详情用 `Promise.all(cannotDelete*.map(...))`。 | 大量不可删项会并发打满请求,并一次性保存全部详情。 | 限制并发;弹窗打开后分页/按需请求引用详情。 | | `apps/lcdp/src/feature/modeling/components/list/mixed-list/MixedList.vue:136`、`model-details/index.vue:419`、`business-operation/FormEdit.vue:156` | 中 | 多处 `size: 9999` 拉页面/规则/操作列表。 | 一次性大列表进入 refs/options,路由切换无 abort。 | 改后端分页/搜索型 select;弹开再加载;请求加 AbortController 或 stale id。 | | `apps/lcdp/src/feature/modeling/components/model-details/entity-model/hooks/UseAiBatchAdd.ts:304`、`:322` | 中 | AI 上下文 `JSON.stringify(treeData.map(...))` 和全部字段列表;还有大对象 console。 | 大模型会生成大 prompt 字符串并阻塞主线程。 | 限制字段数量/深度,只传当前模型摘要;移除大对象 log;必要时 worker 构造 prompt。 | | `apps/lcdp/src/feature/deployment-package/components/log/index.vue:23`、`:39` | 中低 | 一次性 `logList.value = res`,全量 `v-for` 渲染日志。 | 大安装日志占内存并卡渲染。 | 分页/虚拟列表;限制最大展示行数并提供完整日志下载。 | | `apps/lcdp/src/views/demo/vxe-table/index.vue:57`、`grid.vue:240`、`tree-v2/index.vue:77` | 中 | demo 可生成 5w 行 × 120 列,树 demo 参数组合可生成极大节点数。 | 若路由可访问,会冻结主线程和内存暴涨。 | 加总量上限;大数据生成分片/worker;非虚拟表不要提供极端数据。 | | `apps/lcdp/src/views/RuleRuntimeTest.vue:62` | 中低 | `new RuntimeRuleEventCenter(...)` 未见卸载销毁。 | 如果规则中心内部有事件或缓存,实例可能跨页面滞留。 | 确认类是否有 destroy/off/dispose;卸载调用。 | | `packages/form-designer/src/config/widget/list-vxe/preview/core-table/VxeTablePlus.vue:239` | 中 | 纵向虚拟滚动已开启,但 `virtualXConfig.enabled=false`。 | 37+ 列宽表仍会让所有列参与渲染,横向大列数下 DOM/布局成本高。 | 在 fixed 列场景回归后开启横向虚拟滚动,或按列数阈值启用。 | | `packages/form-designer/src/config/widget/table/FieldViewMap.ts:36` | 中高 | 绑定模型的选择字段仍走 `Tag/TextCollapseTag/LightText` 等组件渲染。 | 大列表中每单元格生成组件/VNode,已优化的纯文本路径不能覆盖该场景。 | 列表模式下为绑定模型选择字段提供纯文本 formatter,只有交互态再挂重组件。 | | `packages/form-designer/src/config/widget/list-vxe/composables/useCacheComputed.ts:5` | 中 | `stableStringify` 对 watcher 值排序 key 并 `JSON.stringify`;结果还用 `isEqual` 比较。 | 大 schema/columns 变化时深遍历、字符串分配和深比较叠加。 | 用明确依赖的 shallow watch/computed 替代;逐步移除大对象 stringify。 | | `packages/form-designer/src/config/widget/list-vxe/preview/core-table/useMergeCells.ts:117` | 中 | 每次为字段路径创建 `values/truthy/sigs` 数组,对对象值 `safeStringify`。 | O(rows \* paths) 预计算和字符串分配会在大表格下形成内存峰值。 | 先判断是否存在合并/分组列;只预计算可合并字段。 | | `packages/form-designer/src/config/widget/list-vxe/preview/ListVxePreview.vue:632`、`:637` | 中 | 暴露的 `getTableData/addTableData` 对整页表格数据 `deepCloneObj`。 | 外部频繁读取/新增时复制整页对象图。 | 读接口返回只读引用或浅拷贝;只在外部可变写入边界 clone。 | | `packages/form-designer/src/config/widget/list-vxe/index.ts:2` | 中 | 仍导出旧 `VxeTable.vue`,外部 import 可能绕过 `VxeTablePlus`。 | 旧实现的 `useCacheComputed` 与渲染路径仍可能被业务使用。 | 标记旧导出废弃或指向 Plus;补兼容测试。 | | `packages/form-designer/src/components/settings-operation/index.vue:101` | 中 | 默认 `size: 9999` 获取所有操作。 | 大模型操作多时,一次性进入 `optionList` 并渲染。 | 改远程搜索/分页 select;仅弹窗打开时加载当前页。 | | `packages/form-designer/src/hooks/useLoadPage.ts:149`、`:565` | 中 | 页面加载两处字段列表请求 `size: 999`。 | 多页面/多模型初始化时会把字段元数据批量拉入 store。 | 字段元数据按组件/引用路径懒加载;通用 field key 服务加缓存上限和按页清理。 | | `packages/business-module/src/components/script-panel/Event.ts:60` | 中 | 脚本面板加载页面列表 `size: 999`,并在内存中构建树。 | 大模型多页面场景下脚本面板打开即产生大列表和树对象。 | 改按关键字/节点懒加载;树节点展开再请求。 | | `packages/business-components/src/components/business/reference-data-select/FieldDialog.vue:188`、`hooks/useReferenceDataSource.ts:85`、`hooks/useFeatureDomainDataSource.ts:157`、`hooks/useFilterDataSource.ts:75` | 中 | 引用字段选择多处子节点加载 `size: 999`。 | 深引用模型下每次展开都可能拉完整字段列表,并映射生成路径字符串。 | 按层分页、限制最大展开数;相同 referenceId 加 TTL/LRU 缓存。 | | `packages/form-designer/src/widget-mobile/composables/list-fetch/useMobileFetch.ts:249`、`:260` | 中 | deep watch 内用 `JSON.stringify` 比较 filter/order。 | 条件树变大时每次变更都会整树序列化,触发主线程阻塞和临时字符串内存。 | 在 store 内维护版本号/hash,或按字段浅比较。 | ## packages 目录的长期缓存与工具风险 | 位置 | 风险 | 证据 | 影响 | 建议 | | --------------------------------------------------------------------------------------------------------------------------------------- | ------ | -------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------- | | `packages/form-designer/src/service/FilterService.ts:27` | 中高 | `fieldOptionsCache`、`resolverWaits` 为模块级 Map;成功和失败结果都写入缓存,未见清理 API。 | 访问大量 model 后字段选项长期驻留。 | 增加 `clearFieldOptionsCache(modelId?)`、TTL/LRU;页面卸载或切 app 时清理。 | | `packages/form-designer/src/widget-mobile/utils/field-mapping-cache.ts:11`、`:15` | 中 | `fieldMappingCache`、`fieldSchemaCache` 按 modelId 全局缓存完整字段映射和字段 schema。 | 移动端多模型页面切换后不会自动释放字段配置对象。 | 页面卸载或模型切换时调用 `clearFieldMappingCache(modelId)`;加容量限制。 | | `packages/form-designer/src/service/FieldKeyService.ts:20`、`:40` | 中 | `fieldTreeParamsList/fetchLocal/fieldTreeTimer` 按 `model_app` key 保留;请求 flush 后只清数组,不删除 key 和 timer 引用。 | 多 model/app 组合会留下大量空数组、空队列和 timer key。 | 请求结束后删除空 key;失败也清理;必要时加 `clearFieldTreeBatchCache(tagKey?)`。 | | `packages/business-components/src/service/common/BatchFetchLocalService.ts:11`、`BatchParamsService.ts:14`、`BatchParamsService.ts:111` | 中 | 模块级请求聚合缓存,返回前 `deepCloneObj`;10s 后清理成功响应;`ApiLocalMap[primaryKey]` 不删除空容器。 | 短时间大量 key 会积压响应和 promise;不同 params 会留下大量空 Map;深拷贝放大 CPU/内存峰值。 | 增加最大 key 数、总大小限制;response/request 为空时删除 primaryKey;失败/超时清理 request;大响应避免深拷贝。 | | `packages/business-module/src/stores/Page.ts:22` | 中 | `pageStoreMap` 只增不删,`resetPageDataCode` 只重置数据,不 dispose store。 | 脚本面板多 pageId 切换会保留 Pinia store 和响应式对象。 | 增加 `deletePageStore(pageId)`:调用 `$dispose()` 并从 Map 删除;面板卸载时调用。 | | `packages/business-components/src/utils/InterceptorUtils.ts:43` | 中 | `CACHE_INFO_DATA` localStorage 对象只增不删。 | 长期使用多个页面/模型后本地缓存膨胀。 | 给缓存项加版本/时间戳/LRU;应用切换时清理旧项。 | | `packages/form-designer/src/utils/LoadUmd.ts:6`、`ImportScript.ts:19` | 中 | UMD load promise 全局缓存;script 成功后不移除;`loadSourceWithCache` 的 then 中没有 return。 | 动态组件越多,script/window 全局和 promise 缓存越多。 | 按组件版本设计 LRU;提供 unload 能力;修复 `return get(window, umdPathKey)`。 | | `packages/form-designer/src/utils/LoadCustomResource.ts:172`、`:185` | 中 | `cssLinkMap` 全局缓存并向 head 追加 link,无移除。 | 动态加载大量组件样式后 CSS 常驻。 | 明确这是进程级缓存;或按组件引用计数清理。 | | `packages/lowcode-create/src/logics/SandBox.ts:57`、`:140` | 中 | watcher 在 `effectScope` 中,`CodeProtocol.destroy()` 会 stop;但 `setFieldStyle` 的 3s timeout 未保存。 | 表单卸载后 timeout 仍可能查询/写 DOM。 | 保存 timeout id;`stopEffect` 时 clear;DOM 操作前检查 disposed。 | | `packages/form-designer/src/config/widget/list-vxe/preview/core-table/VxeTable.vue:449`、`VxeTablePlus.vue:462` | 低到中 | ResizeObserver 有 disconnect,但 debounce/内部 setTimeout 未 cancel。 | 卸载边界下仍可能执行一次 recalculate 相关回调。 | 卸载时 cancel debounce;setTimeout 前后判断 mounted。 | | `packages/business-components/src/components/business/upload/UseUploadModel.ts:14` | 低 | Blob URL 在 scope dispose 时统一 revoke。 | 删除单个 raw 文件时 URL 会保留到组件卸载。 | 文件移除时同步 revoke 对应 URL。 | ## 构建脚本内存峰值风险 | 位置 | 风险 | 证据 | 影响 | 建议 | | ------------------------------------------------------------------------------------ | ---- | ----------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------- | | `packages/form-designer/package.json:22`、`packages/business-module/package.json:22` | 高 | `build` 使用 `run-p type-check "build-only"` 并行运行 `vue-tsc --build` 和 `vite build`。 | 两个进程同时加载 TS/Vue/Rollup 依赖图,CI 或本地低内存机器峰值接近翻倍。 | CI 改串行阶段;或拆 job;必要时为 build/type-check 单独设置 `NODE_OPTIONS=--max-old-space-size=...`。 | | `packages/widget-pc/package.json:13` | 高 | `pnpm test && run-p type-check "build-only"`。 | 测试结束后仍并行类型检查和构建,内存峰值高;失败重试成本也高。 | 测试、类型检查、构建分阶段执行;发布流水线再组合。 | | `packages/widget-pc/vite.config.ts:20`、`:23` | 中 | `vite-plugin-dts` 启用 `rollupTypes: true`。 | dts rollup 会构建类型图,和 Vite/Rollup 同进程叠加内存。 | 非发布构建关闭 `rollupTypes`;类型声明单独命令生成。 | | `packages/lowcode-create/vite.config.ts:21`、`:55` | 中 | lib build 同时开启 `sourcemap: true` 和 `dts()`。 | sourcemap 与类型生成在同一构建进程中增加内存占用。 | 发布构建才开 sourcemap;类型生成拆为独立步骤。 | | `package.json:25` | 中 | `postinstall` 自动执行 `pnpm run build:core`。 | install 阶段触发重型 Vite 构建,CI 缓存失效或本地安装时容易出现不可预期峰值。 | 移出 postinstall,改为显式 `bootstrap/build` 步骤。 | ## 已检查但暂未列为主要问题 | 位置 | 结论 | | ------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------- | | `apps/lcdp/src/directives/TextHoverHandle.ts`、`apps/lcdp/src/directives/vTooltip.ts` | DOM listener 有成对 remove,主要问题是全局 tooltip 单例本身缺少 destroy。 | | `packages/form-designer/src/hooks/useResizable.ts` | mousemove/mouseup 和 ResizeObserver 在卸载时清理,当前实现基本完整。 | | `packages/form-designer/src/config/widget/list-vxe/preview/core-table/*` | ResizeObserver 有 disconnect;残余风险在 debounce/setTimeout cancel。 | | `packages/business-components/src/components/business/upload/UseUploadModel.ts` | 已在 `onScopeDispose` revoke Object URL。 | | `perf/list-vxe-performance-analysis.md` | 已有 list-vxe 渲染卡顿分析,结论可继续沿用:大分页下主要瓶颈是 vxe-grid DOM 渲染,而非请求。 | ## 建议落地任务 ### P0:先止住确定性泄漏 1. 给微前端生命周期加统一 cleanup registry:路由守卫、idle/timeout、平台 message、AI 初始化、theme adapter、axios interceptors 都登记 disposer。 2. 修复 `DataModelView.vue`:恢复 `attachShadow`、清理 editor listener/timer/observer,避免 200ms 轮询扫全 Shadow DOM。 3. 修复 `MonacoEditor.vue` 和 plugin runtime:组件卸载时 dispose editor 和所有 provider/listener。 4. 修复 `EditorWorker.ts`:revoke Blob URL,并提供 worker cache 的 terminate/clear。 5. 修复自定义组件渲染:`renderFramework/renderFrameworkESM/renderDebugFrameworkESM` 返回统一 handle,调用方卸载时调用 `destroy()`。 6. 修复 `InputFormatHandle.ts`、`CustomMobileNumber.vue`、`UseConfig.ts` Sortable、`UsePostMessage.ts`、`fileVxe.ts`、`ScriptRightPanel.vue`、`ActionContent.vue` 的生命周期缺口。 ### P1:控制异步与常驻 UI 1. 所有请求型组件增加 stale request id 或 AbortController。 2. lodash debounce/throttle 统一在卸载 cancel。 3. `showMessage({ duration: 0 })` 保存 handle,组件卸载时关闭。 4. `getAICodeService` 改为可立即取消,并限制流式内容累计大小。 5. 对通用 eventBus/mitt 封装开发期泄漏检测:同一组件重复注册、匿名 handler、on/off 引用不一致时输出告警。 ### P2:降低大数据内存和 CPU 峰值 1. 清理 `size: 999/9999/99999`,改分页、懒加载、远程搜索或虚拟列表。 2. 深拷贝和深度 watch 改为 dirty flag、版本号、局部浅拷贝。 3. 大 prompt、大 ERD JSON、大导出 JSON 放到 worker 或后端生成。 4. 模块级 Map 缓存增加 TTL/LRU 和显式 clear API。 5. list-vxe 继续验证横向虚拟滚动、绑定模型字段纯文本渲染、`useCacheComputed` 和 merge-cell 预计算优化。 ### P3:降低构建阶段峰值内存 1. `form-designer`、`business-module`、`widget-pc` 的 build 改为 test/type-check/build 分阶段串行。 2. dts rollup、sourcemap 和 Vite build 拆成发布专用步骤。 3. 移除根 `postinstall` 的自动重构建,改显式 bootstrap。 ## 验证建议 1. 微前端重复挂载测试:同一页面 mount/unmount 20 次,检查路由守卫数量、message listener、axios interceptors 是否增长。 2. Chrome Memory heap snapshot:进入/退出建模详情、ERD 视图、页面预览、Monaco 编辑器、自定义组件渲染页,比较 detached DOM、ResizeObserver、MutationObserver、Monaco model、Sortable 实例数量。 3. 大数据压测:业务分类、字段选择、应用树、特征变量、打印模型树用 1k/5k/10k 数据量测试内存峰值和 long task。 4. AI 流式请求测试:发起后立即切路由,确认请求被 abort,`accumulatedContent` 不再增长。 5. 增加开发期诊断:包装 eventBus/router/axios/registerPostMessageHandle,开发模式输出当前 listener/interceptor/handler 计数。 6. list-vxe 当前主路径已使用 `VxeTablePlus`,建议重新补跑 `perf/list-vxe-performance-test.md` 的基线数据,避免后续继续引用旧实现测试结果。 ## Agent 分工记录 | 分工 | 覆盖范围 | 结果 | | ------- | ----------------------------------------------------- | ------------------------------------------------------------------------------- | | 主线程 | 全仓静态扫描、重点文件复核、文档汇总 | 已合并所有确认项。 | | Agent A | `apps/lcdp/src` 全局生命周期、页面预览、建模详情 | 发现路由守卫、postMessage、ERD editor、指令、全量请求等问题。 | | Agent B | `packages/business-components`、`packages/core-utils` | 发现 Monaco、PostMessageUtil、Batch service、Tooltip、UseBatchDownload 等问题。 | | Agent C | `packages/form-designer` 运行时、页面加载、动态组件 | 发现自定义组件渲染、DocLib、FilterService、LoadUmd、list/tree 字段加载等问题。 | | Agent D | `list-vxe`、脚本面板、构建脚本 | 补充 list-vxe 热点、匿名 eventBus 泄漏、脚本面板 store 缓存、构建峰值内存风险。 |