React 富文本编辑器组件
React 允许您使用称为组件的各个部分构建用户界面。CKEditor 5 可以用作此类组件之一。
从该软件包的 6.0.0 版本开始,您可以使用 CKEditor 5 提供的本机类型定义。查看有关 TypeScript 支持 的详细信息。
CKEditor 5 生成器
在我们的交互式生成器中,您可以快速体验 CKEditor 5。它提供了一个易于使用的用户界面,可以帮助您配置、预览和下载适合您需求的编辑器。您可以轻松地选择
- 编辑器类型。
- 您需要的功能。
- 首选框架(React、Angular、Vue 或 Vanilla JS)。
- 首选分发方法。
最后,您将获得适合您需求的即用型代码!
# 快速开始
# 设置项目
本指南假设您有一个 React 项目。您可以使用 Vite 创建一个基本的 React 项目。请参考 React 文档 了解如何在框架中设置项目。
# 从 npm 安装
首先,安装 CKEditor 5 软件包
ckeditor5
– 包含开源插件和功能的软件包。ckeditor5-premium-features
– 包含高级插件和功能的软件包。
根据您的配置和选定的插件,您可能需要安装第一个软件包或两个软件包。
npm install ckeditor5 ckeditor5-premium-features
然后,安装 用于 React 的 CKEditor 5 WYSIWYG 编辑器组件
npm install @ckeditor/ckeditor5-react
在您的项目中使用 <CKEditor>
组件。以下示例展示了如何在组件中使用开源和高级插件。
import { CKEditor } from '@ckeditor/ckeditor5-react';
import { ClassicEditor, Bold, Essentials, Italic, Mention, Paragraph, Undo } from 'ckeditor5';
import { SlashCommand } from 'ckeditor5-premium-features';
import 'ckeditor5/ckeditor5.css';
import 'ckeditor5-premium-features/ckeditor5-premium-features.css';
function App() {
return (
<CKEditor
editor={ ClassicEditor }
config={ {
toolbar: {
items: [ 'undo', 'redo', '|', 'bold', 'italic' ],
},
plugins: [
Bold, Essentials, Italic, Mention, Paragraph, SlashCommand, Undo
],
licenseKey: '<YOUR_LICENSE_KEY>',
mention: {
// Mention configuration
},
initialData: '<p>Hello from CKEditor 5 in React!</p>',
} }
/>
);
}
export default App;
请记住导入必要的样式表。ckeditor5
软件包包含开源功能的样式,而 ckeditor5-premium-features
软件包包含高级功能样式。
# 组件属性
<CKEditor>
组件支持以下属性
editor
(必填)– 要使用的Editor
构造函数。data
– 创建的编辑器的初始数据。请参阅 获取和设置数据 指南。config
– 编辑器配置。请参阅 配置 指南。id
– 编辑器 ID。当此属性更改时,组件将使用新数据重新启动编辑器,而不是将其设置在已初始化的编辑器上。disabled
– 布尔值。如果属性设置为true
,editor
将切换到只读模式。disableWatchdog
– 布尔值。如果设置为true
,则将禁用 看门狗功能。默认情况下设置为false
。watchdogConfig
– 用于 看门狗功能 的配置对象。onReady
– 当编辑器准备好具有editor
实例时调用的函数。如果发生错误,此回调也会在组件重新初始化后调用。onAfterDestroy
– 在组件呈现的编辑器实例成功销毁后调用的函数。如果编辑器在错误后重新初始化,此回调也会触发。组件在调用此函数时不保证已挂载。onChange
– 当编辑器数据发生更改时调用的函数。请参阅editor.model.document#change:data
事件。onBlur
– 当编辑器失去焦点时调用的函数。请参阅editor.editing.view.document#blur
事件。onFocus
– 当编辑器获得焦点时调用的函数。请参阅editor.editing.view.document#focus
事件。onError
– 当编辑器在初始化期间或运行时崩溃时调用的函数。它接收两个参数:错误实例和错误详细信息。错误详细信息是一个包含两个属性的对象{String} phase
:'initialization'|'runtime'
– 通知错误发生的时间(在编辑器或上下文初始化期间,或初始化后)。{Boolean} willEditorRestart
– 当为true
时,表示编辑器组件将重新启动自身。
编辑器事件回调(onChange
、onBlur
、onFocus
)接收两个参数
# 上下文功能
@ckeditor/ckeditor5-react
软件包为 上下文功能 提供了一个即用型组件,该功能在与一些 CKEditor 5 协作功能 一起使用时非常有用。
import { ClassicEditor, Context, Bold, Essentials, Italic, Paragraph, ContextWatchdog } from 'ckeditor5';
import { CKEditor, CKEditorContext } from '@ckeditor/ckeditor5-react';
import 'ckeditor5/ckeditor5.css';
function App() {
return (
<CKEditorContext
context={ Context }
contextWatchdog={ ContextWatchdog }
onChangeInitializedEditors={ ( editors ) => {
console.info( editors.editor1?.instance, editors.editor1?.yourAdditionalData );
} }
>
<CKEditor
editor={ ClassicEditor }
config={ {
plugins: [ Essentials, Bold, Italic, Paragraph ],
toolbar: [ 'undo', 'redo', '|', 'bold', 'italic' ],
} }
data='<p>Hello from the first editor working with the context!</p>'
contextItemMetadata={{
name: 'editor1',
yourAdditionalData: 2
}}
onReady={ ( editor ) => {
// You can store the "editor" and use when it is needed.
console.log( 'Editor 1 is ready to use!', editor );
} }
/>
<CKEditor
editor={ ClassicEditor }
config={ {
plugins: [ Essentials, Bold, Italic, Paragraph ],
toolbar: [ 'undo', 'redo', '|', 'bold', 'italic' ],
} }
data='<p>Hello from the second editor working with the context!</p>'
onReady={ ( editor ) => {
// You can store the "editor" and use when it is needed.
console.log( 'Editor 2 is ready to use!', editor );
} }
/>
</CKEditorContext>
);
}
export default App;
CKEditorContext
组件支持以下属性
context
(必填)– CKEditor 5 上下文类。contextWatchdog
(必填)– Watchdog 上下文类。config
– CKEditor 5 上下文配置。isLayoutReady
– 当设置为false
时延迟上下文创建的属性。一旦它为true
或未设置,它将创建上下文和编辑器子项。当使用 CKEditor 5 注释或存在列表时很有用。id
– 上下文 ID。当此属性更改时,组件将使用其编辑器重新启动上下文,并根据当前配置对其进行重新初始化。onChangeInitializedEditors
– 当树中的任何编辑器初始化或销毁时调用的函数。它接收一个完全初始化的编辑器字典,其中键是在CKEditor
组件上设置的contextItemMetadata.name
属性的值。如果contextItemMetadata
属性不存在,则编辑器的 ID 是键。可以在CKEditor
组件中向contextItemMetadata
添加其他数据,这些数据将传递给onChangeInitializedEditors
函数。onReady
– 当上下文初始化但在树中的编辑器设置之前调用的函数。执行此函数后,您可以使用context.editors.on('change', () => {})
方法跟踪上下文树中的添加和删除。onError
– 当上下文在初始化期间或运行时崩溃时调用的函数。它接收两个参数:错误实例和错误详细信息。错误详细信息是一个包含两个属性的对象{String} phase
:'initialization'|'runtime'
– 通知错误发生的时间(在编辑器或上下文初始化期间,或初始化后)。{Boolean} willContextRestart
– 当为true
时,表示上下文组件将重新启动自身。
可以在 CKEditor 5 协作示例 中找到公开上下文和经典编辑器的示例构建。
# 如何?
# 使用文档编辑器类型
如果您使用的是 文档(分离)编辑器,则需要 手动将工具栏添加到 DOM
import { useEffect, useRef, useState } from 'react';
import { DecoupledEditor, Bold, Essentials, Italic, Paragraph } from 'ckeditor5';
import { CKEditor } from '@ckeditor/ckeditor5-react';
import 'ckeditor5/ckeditor5.css';
function App() {
const editorToolbarRef = useRef( null );
const [ isMounted, setMounted ] = useState( false );
useEffect( () => {
setMounted( true );
return () => {
setMounted( false );
};
}, [] );
return (
<div>
<div ref={ editorToolbarRef }></div>
<div>
{ isMounted && (
<CKEditor
editor={ DecoupledEditor }
data='<p>Hello from CKEditor 5 decoupled editor!</p>'
config={ {
plugins: [ Bold, Italic, Paragraph, Essentials ],
toolbar: [ 'undo', 'redo', '|', 'bold', 'italic' ]
} }
onReady={ ( editor ) => {
if ( editorToolbarRef.current ) {
editorToolbarRef.current.appendChild( editor.ui.view.toolbar.element );
}
}}
onAfterDestroy={ ( editor ) => {
if ( editorToolbarRef.current ) {
Array.from( editorToolbarRef.current.children ).forEach( child => child.remove() );
}
}}
/>
) }
</div>
</div>
);
}
export default App;
# 将编辑器与协作插件一起使用
我们提供了一些 可立即使用的集成,这些集成在 React 应用程序中提供协作编辑功能
在上述示例之上构建应用程序并非强制性,但它们应该可以帮助您入门。
# 本地化
CKEditor 5 支持 多种 UI 语言,官方 React 组件也支持。请按照以下说明在您的 React 应用程序中翻译 CKEditor 5。
与 CSS 样式表类似,这两个软件包都有单独的翻译。如以下示例所示导入它们。然后,将它们传递到 CKEditor 5 组件的 config
属性中的 translations
数组中。
import { ClassicEditor } from 'ckeditor5';
import { CKEditor } from '@ckeditor/ckeditor5-react';
// More imports...
import coreTranslations from 'ckeditor5/translations/es.js';
import premiumFeaturesTranslations from 'ckeditor5-premium-features/translations/es.js';
// Style sheets imports...
function App() {
return (
<CKEditor
editor={ ClassicEditor }
config={ {
translations: [ coreTranslations, premiumFeaturesTranslations ],
initialData: '<p>Hola desde CKEditor 5 en React!</p>',
} }
/>
);
}
export default App;
有关更多信息,请参考 设置 UI 语言 指南。
# Jest 测试
Jest 是许多 React 应用程序使用的默认测试运行器。不幸的是,Jest 不会使用真正的浏览器。相反,它在使用 JSDOM 的 Node.js 中运行测试。JSDOM 不是完整的 DOM 实现,虽然它足以用于标准应用程序,但它无法模拟 CKEditor 5 所需的所有 DOM API。
为了测试 CKEditor 5,建议使用利用真实浏览器并提供完整 DOM 实现的测试框架。一些流行的选择包括
这些框架为测试 CKEditor 5 提供了更好的支持,并更准确地反映了编辑器在真实浏览器环境中的行为。
如果不可能,并且您仍然想使用 Jest,您可以模拟一些必需的 API。以下是模拟 CKEditor 5 使用的一些 API 的示例
import React, { useRef } from 'react';
import { render, waitFor, screen } from '@testing-library/react';
import { userEvent } from '@testing-library/user-event';
import { DecoupledEditor, Essentials, Paragraph } from 'ckeditor5';
import { CKEditor } from '@ckeditor/ckeditor5-react';
beforeAll( () => {
window.scrollTo = jest.fn();
window.ResizeObserver = class ResizeObserver {
observe() {}
unobserve() {}
disconnect() {}
};
for ( const key of [ 'InputEvent', 'KeyboardEvent' ] ) {
window[ key ].prototype.getTargetRanges = () => {
const range = new StaticRange( {
startContainer: document.body.querySelector( '.ck-editor__editable p' ),
startOffset: 0,
endContainer: document.body.querySelector( '.ck-editor__editable p' ),
endOffset: 0
} );
return [ range ];
};
}
Range.prototype.getClientRects = () => ( {
item: () => null,
length: 0,
[ Symbol.iterator ]: function* () {}
} );
} );
const SomeComponent = ( { value, onChange } ) => {
const editorRef = useRef();
return (
<div
style={{
border: '1px solid black',
padding: 10,
}}
>
<CKEditor
editor={ DecoupledEditor }
config={{
plugins: [ Essentials, Paragraph ],
}}
onReady={ (editor) => {
editorRef.current = editor;
} }
data={ value }
onChange={ () => {
onChange( editorRef.current?.getData() );
} }
/>
</div>
);
};
it( 'renders', async () => {
render( <SomeComponent value="this is some content" /> );
await waitFor( () => expect( screen.getByText( /some content/ ) ).toBeTruthy());
} );
it( 'updates', async () => {
const onChange = jest.fn();
render( <SomeComponent value="this is some content" onChange={onChange} /> );
await waitFor( () => expect( screen.getByText( /some content/ ) ).toBeTruthy() );
await userEvent.click( document.querySelector( '[contenteditable="true"]' ) );
userEvent.keyboard( 'more stuff' );
await waitFor( () => expect( onChange ).toHaveBeenCalled() );
} );
上面显示的模拟只测试了两个基本场景,可能需要添加更多场景,这些场景可能会随着每个编辑器版本的发布而改变。
# 贡献和报告问题
用于 React 的富文本编辑器组件的源代码在 GitHub 上的 https://github.com/ckeditor/ckeditor5-react 中提供。
我们每天都在努力使我们的文档保持完整。您是否发现过时信息?是否有内容缺失?请通过我们的问题追踪器报告。
随着 42.0.0 版本的发布,我们已经重新编写了大部分文档,以反映新的导入路径和功能。我们感谢您的反馈,这将帮助我们确保文档的准确性和完整性。