You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

CKEditor4升级后与dx-data-grid[innerHtml]绑定致Chrome锁死排查求助

排查Chrome锁死问题:CKEditor4升级后与Angular ngbNav、dx-data-grid的冲突

问题概述

升级CKEditor4至4.16.2以上版本(含4.19、4.19.1、4.20)后,Angular应用出现Chrome浏览器锁死现象(弹出“等待”或“关闭页面”对话框),仅在特定场景下触发。

触发条件

  • CKEditor部署在ngbNav标签页(基于UL/LI的ng-bootstrap标签组件)内
  • 切换至不含CKEditor的标签页后,导航至新URL页面
  • 新页面包含dx-data-grid表格,且某列使用Angular的[innerHtml]绑定
  • 使用CKEditor4 4.16.2以上版本

当前现状

  • 移除dx-data-grid的列模板(如下)可阻止问题,但该模板用于安全插入HTML内容,无法替代:
<div *dxTemplate="let data of 'bodyTemplate'">
    <div [innerHtml]="getSafeHtml(data.value, true)"></div>
</div>
  • 已编写导航时销毁CKEditor的代码并确认执行,但问题依旧:
destroyCKEditor() {
   const editor = window['CKEDITOR'];
   if (editor.instances) {
    for (const editorInstance in editor.instances) {
      if (editor.dialog.getCurrent()) {
        editor.dialog.getCurrent().hide();
      }
      editor.instances[editorInstance].destroy();
    }
  }
}
  • 以下场景无问题:
    • CKEditor标签页处于激活状态时导航
    • 导航至无[innerHTML]绑定的页面
    • 使用CKEditor4 4.16.2版本

排查锁死原因的建议

  1. 用Chrome DevTools定位长任务
    打开Performance面板,录制从切换标签页到导航的完整流程,查看是否存在持续运行的长任务(如无限循环、大量DOM操作),重点关注CKEditor相关的事件监听器、DOM节点操作。

  2. 检查CKEditor实例及DOM残留

    • 导航前后在Console执行CKEDITOR.instances,确认是否有未销毁的实例
    • 在Elements面板查看是否残留CKEditor相关DOM节点(如编辑器iframe、弹窗容器),对比切换标签页/导航前后的DOM变化
  3. 验证CKEditor销毁完成状态
    给每个CKEditor实例绑定destroy事件,确认销毁流程是否完成:

    editor.instances[editorInstance].on('destroy', function() {
      console.log('实例已销毁:', editorInstance);
    });
    
  4. 对比CKEditor版本变更
    查看4.16.2与4.19+的Release Notes,重点关注DOM清理、事件绑定、非激活实例处理相关的更新,定位可能引入冲突的特性。

  5. 构建最小复现Demo
    创建仅包含ngbNav、CKEditor、dx-data-grid和[innerHTML]的极简项目,逐步移除其他依赖,确认是否为三者的直接冲突。

优化CKEditor销毁逻辑的方案

当前销毁代码存在异步处理不充分、全局实例遍历风险等问题,建议调整如下:

  1. 持有组件内实例引用
    避免遍历全局CKEDITOR.instances,在Angular组件中存储当前实例的引用,仅销毁当前组件的CKEditor实例,防止误操作其他组件的实例。

  2. 异步等待销毁完成
    CKEditor的destroy()是异步操作,需等待完成后再执行导航:

    async destroyCKEditor(): Promise<void> {
      const editor = window['CKEDITOR'];
      if (editor?.instances) {
        const instanceKeys = Object.keys(editor.instances);
        for (const key of instanceKeys) {
          const instance = editor.instances[key];
          // 关闭对话框
          if (editor.dialog.getCurrent()) {
            editor.dialog.getCurrent().hide();
          }
          // 等待销毁完成
          await new Promise(resolve => {
            instance.on('destroy', resolve);
            instance.destroy();
          });
          // 手动清理DOM容器
          const domElement = instance.element?.$;
          if (domElement) {
            domElement.remove();
          }
        }
      }
    }
    
  3. 在路由守卫中执行销毁
    使用Angular的CanDeactivate守卫,确保导航前CKEditor已完成销毁:

    @Injectable()
    export class CkeditorDestroyGuard implements CanDeactivate<YourComponent> {
      async canDeactivate(component: YourComponent): Promise<boolean> {
        await component.destroyCKEditor();
        return true;
      }
    }
    
  4. 标签页切换时提前处理
    监听ngbNav的navChange事件,当CKEditor所在标签页被隐藏时立即销毁实例:

    onNavChange(event: NgbNavChangeEvent) {
      if (event.nextId !== 'ckeditor-tab-id') {
        this.destroyCKEditor();
      }
    }
    

内容的提问来源于stack exchange,提问作者Zach La Lond

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.14 20:30:57