React项目自定义CKEditor 5出现getAttribute空引用错误如何修复?
问题描述
在React项目中替换CKEditor构建版本时出现错误:使用CKEditor 5在线构建工具生成包含SourceEditing插件的CustomEditor配置文件(config.ts),替代原本正常运行的@ckeditor/ckeditor5-build-classic中的ClassicEditor后,抛出TypeError: Cannot read properties of null (reading 'getAttribute')错误。
相关代码
config.ts
/** * @license Copyright (c) 2014-2023, CKSource Holding sp. z o.o. All rights reserved. * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license */ import { ClassicEditor } from '@ckeditor/ckeditor5-editor-classic'; import { Autoformat } from '@ckeditor/ckeditor5-autoformat'; import { Bold, Italic } from '@ckeditor/ckeditor5-basic-styles'; import { BlockQuote } from '@ckeditor/ckeditor5-block-quote'; import { CKBox } from '@ckeditor/ckeditor5-ckbox'; import { CloudServices } from '@ckeditor/ckeditor5-cloud-services'; import type { EditorConfig } from '@ckeditor/ckeditor5-core'; import { Essentials } from '@ckeditor/ckeditor5-essentials'; import { Heading } from '@ckeditor/ckeditor5-heading'; import { Image, ImageCaption, ImageStyle, ImageToolbar, ImageUpload, PictureEditing } from '@ckeditor/ckeditor5-image'; import { Indent } from '@ckeditor/ckeditor5-indent'; import { Link } from '@ckeditor/ckeditor5-link'; import { List } from '@ckeditor/ckeditor5-list'; import { MediaEmbed } from '@ckeditor/ckeditor5-media-embed'; import { Paragraph } from '@ckeditor/ckeditor5-paragraph'; import { PasteFromOffice } from '@ckeditor/ckeditor5-paste-from-office'; import { SourceEditing } from '@ckeditor/ckeditor5-source-editing'; import { Table, TableToolbar } from '@ckeditor/ckeditor5-table'; import { TextTransformation } from '@ckeditor/ckeditor5-typing'; import { Undo } from '@ckeditor/ckeditor5-undo'; class CustomEditor extends ClassicEditor { public static override builtinPlugins = [ Autoformat, BlockQuote, Bold, CKBox, CloudServices, Essentials, Heading, Image, ImageCaption, ImageStyle, ImageToolbar, ImageUpload, Indent, Italic, Link, List, MediaEmbed, Paragraph, PasteFromOffice, PictureEditing, SourceEditing, Table, TableToolbar, TextTransformation, Undo ]; public static override defaultConfig: EditorConfig = { toolbar: { items: [ 'heading', '|', 'bold', 'italic', 'link', 'bulletedList', 'numberedList', '|', 'outdent', 'indent', '|', 'imageUpload', 'blockQuote', 'insertTable', 'mediaEmbed', 'undo', 'redo', 'sourceEditing' ] }, language: 'en', image: { toolbar: [ 'imageTextAlternative', 'toggleImageCaption', 'imageStyle:inline', 'imageStyle:block', 'imageStyle:side' ] }, table: { contentToolbar: [ 'tableColumn', 'tableRow', 'mergeTableCells' ] } }; } export default CustomEditor;
TDEditorNew组件代码
import React from "react"; import { CKEditor } from "@ckeditor/ckeditor5-react"; import CustomEditor from "./config"; import axios from "axios"; import * as authHelper from "@/app/modules/auth/core/AuthHelpers"; import { requestGET, requestPOST_NEW, requestPUT_NEW, API_URL, FILE_URL, } from "@/utils/baseAPI"; const uploadAdapter = (loader) => { const { token } = authHelper.getAuth(); return { upload: () => { return new Promise(async (resolve, reject) => { try { const file = await loader.file; const response = await axios.request({ method: "POST", url: `${API_URL}/api/v1/attachments/public`, data: { files: file, }, headers: { "Content-Type": "multipart/form-data", Authorization: `Bearer ${token}`, }, }); resolve({ default: `${FILE_URL}${response.data.data[0].url}`, }); } catch (error) { console.log(error); } }); }, abort: () => {}, }; }; function uploadPlugin(editor) { editor.plugins.get("FileRepository").createUploadAdapter = (loader) => { return uploadAdapter(loader); }; } const TDEditorNew = ({ data, onChange }) => { return ( <CKEditor editor={CustomEditor} config={{ extraPlugins: [uploadPlugin], }} data={data} onChange={(event, editor) => { const data = editor.getData(); onChange(data); }} /> ); }; export default TDEditorNew;
组件使用代码
import TDEditorNew from '@/app/components/TDEditorNew'; ... <div className='col-xl-12 col-lg-12'> <FormItem label='content' name='content'> <TDEditorNew data={form.getFieldValue('content')} onChange={(newContent) => { form.setFieldsValue({ content: newContent }); }} /> </FormItem> </div>
错误栈信息
@ckeditor_ckeditor5-react.js?v=be2d4ad6:1084 TypeError: Cannot read properties of null (reading 'getAttribute') at _IconView._updateXMLContent (chunk-PUXRYV7X.js?v=be2d4ad6:2330:27) at _IconView.render (chunk-PUXRYV7X.js?v=be2d4ad6:2313:10) at _IconView.<anonymous> (chunk-AKXJUVNH.js?v=be2d4ad6:7658:37) at _IconView.fire (chunk-AKXJUVNH.js?v=be2d4ad6:7337:35) at <computed> [as render] (chunk-AKXJUVNH.js?v=be2d4ad6:7661:21) at ViewCollection._renderViewIntoCollectionParent (chunk-PUXRYV7X.js?v=be2d4ad6:1125:12) at ViewCollection.<anonymous> (chunk-PUXRYV7X.js?v=be2d4ad6:1019:12) at ViewCollection.fire (chunk-AKXJUVNH.js?v=be2d4ad6:7337:35) at ViewCollection.addMany (chunk-PUXRYV7X.js?v=be2d4ad6:9371:12) at ViewCollection.add (chunk-PUXRYV7X.js?v=be2d4ad6:9347:17) {phase: 'initialization', willEditorRestart: false}
原因分析
这个错误的根源是CKEditor的图标资源未正确加载或打包缺失。当自定义构建编辑器时,若插件依赖的SVG图标资源未被正确引入,IconView组件在尝试读取DOM元素的getAttribute方法时,会因为元素为null而抛出异常。常见诱因包括:
- 项目中CKEditor各依赖包版本与在线构建工具选择的版本不一致
- 在线构建生成的代码遗漏了图标相关的打包配置
- 自定义编辑器配置中未正确包含图标处理相关的基础插件
修复方案
1. 统一CKEditor依赖版本
检查项目package.json中所有@ckeditor/ckeditor5-*开头的依赖版本,确保它们与在线构建工具中选择的版本完全一致。版本不匹配会导致资源引用冲突,引发图标加载失败。
2. 重新生成并替换自定义构建包
- 打开CKEditor 5在线构建工具,选择ClassicEditor作为基础构建
- 添加SourceEditing插件,确认所有插件的版本同步
- 下载最新的构建包,替换项目中的
config.ts文件,并根据构建包的提示更新package.json中的依赖版本
3. 确保图标资源正确打包
如果手动维护config.ts,需确认构建流程中包含了CKEditor的图标资源处理:
- 确保构建工具(如Vite、Webpack)能正确解析SVG文件
- 若使用在线构建工具,下载的包已默认包含图标资源,直接替换即可
4. 验证上传插件兼容性
确认自定义编辑器中已包含FileRepository插件(从你的代码看已包含,因为依赖ImageUpload),且上传插件的逻辑未干扰编辑器初始化流程。
内容的提问来源于stack exchange,提问作者Chung Truong
相关产品推荐
相关产品推荐

