Angular 8应用接入Angular Universal SSR时CKEditor报ckeFiller未定义错误
问题根因
这个报错是CKEditor在服务端Node环境强行执行初始化逻辑触发的:
- 富文本编辑器本身是强依赖真实浏览器DOM交互的组件,初始化过程需要依赖大量domino无法完整模拟的浏览器API
- 报错里的
ckeFiller是CKEditor内部绑定到可编辑DOM元素上的标记属性,执行到这行代码时,服务端模拟的DOM环境拿不到合法的可编辑元素实例,目标对象是undefined,直接给undefined赋值就会抛出当前错误 - 你当前在server.ts里通过domino模拟全局DOM对象的方案,根本覆盖不了CKEditor全量的运行依赖,就算临时解决了
ckeFiller的报错,后续还会陆续出现选区计算、DOM范围校验、交互事件绑定相关的其他运行错误
解决方案
这类强浏览器依赖的组件,完全没有必要在服务端渲染阶段执行初始化逻辑,把CKEditor的加载时机延迟到浏览器端运行即可,根据你的引入场景可以选下面两种实现:
场景1:业务组件内直接引入CKEditor
在用到CKEditor的组件中,通过Angular内置的平台判断能力做渲染守卫,服务端渲染阶段直接跳过CKEditor的挂载流程:
- 注入平台标识,通过
isPlatformBrowser方法判断当前代码运行环境 - 用
*ngIf控制CKEditor节点的渲染,只有确认当前是浏览器环境时,才把编辑器组件挂载到视图上
示例参考:
import { Component, Inject, PLATFORM_ID, isPlatformBrowser } from '@angular/core'; import * as ClassicEditor from '@ckeditor/ckeditor5-build-classic'; @Component({ selector: 'app-content-editor', template: ` <!-- 服务端可正常渲染的普通业务内容 --> <div class="editor-wrapper"> <ckeditor *ngIf="isBrowser" [editor]="ClassicEditor" [(ngModel)]="articleContent" ></ckeditor> </div> ` }) export class ContentEditorComponent { isBrowser = false; ClassicEditor = ClassicEditor; articleContent = ''; constructor(@Inject(PLATFORM_ID) private platformId: object) { this.isBrowser = isPlatformBrowser(this.platformId); } }
场景2:CKEditor在懒加载模块中引入
在服务端模块中为CKEditor配置空占位实现,让服务端渲染阶段直接跳过真实的CKEditor逻辑,用空组件做占位,完全不触发编辑器的初始化代码:
// app.server.module.ts import { NgModule } from '@angular/core'; import { ServerModule } from '@angular/platform-server'; import { CKEditorComponent } from '@ckeditor/ckeditor5-angular'; import { AppComponent } from './app.component'; // 服务端使用的空占位组件 class ServerEmptyEditorComponent {} @NgModule({ imports: [ ServerModule, ], declarations: [AppComponent], entryComponents: [ServerEmptyEditorComponent], // 用空占位替代真实CKEditor组件 providers: [ { provide: CKEditorComponent, useClass: ServerEmptyEditorComponent } ], bootstrap: [AppComponent] }) export class AppServerModule {}
避坑提醒
- 不要为了兼容CKEditor在server.ts里追加更多domino全局对象补丁,这类补丁的维护成本极高,也无法从根本上解决CKEditor对真实浏览器运行环境的依赖问题
- 服务端渲染阶段CKEditor的位置直接留空占位即可,等浏览器端激活应用时再加载真实编辑器,不会影响首屏渲染性能
内容的提问来源于stack exchange,提问作者Raj Santoki
相关产品推荐
相关产品推荐

