更新到 CKEditor 5 v25.x
更新 CKEditor 5 安装时,确保所有包的版本一致,以避免错误。
对于自定义构建,您可以尝试删除package-lock.json
或yarn.lock
文件(如果适用)并重新安装所有包,然后再重建编辑器。为了获得最佳结果,请确保您使用最新的包版本。
# 更新到 CKEditor 5 v25.0.0
发布于 2021 年 1 月 27 日。
有关版本 25.0.0 中引入的所有更改列表,请参阅CKEditor 5 v25.0.0 的发行说明。
本迁移指南列出了在升级到 CKEditor 5 v25.0.0 时需要您注意的最重要更改,这些更改是由协作功能和批注 API 的重新设计引入的。
# 批注 API 的重新设计
整个批注 API 进行了重新设计,以支持同时使用多个批注 UI。一个很好的例子是,在侧边栏中显示评论,同时在内联气球中显示来自修订跟踪的建议,当它们的对应建议处于活动状态时。因此,在本版本中进行了一些架构更改,并且引入了许多重大更改。请参阅下面的迁移指南,了解如何适应这些更改。
# 概念和架构更改
新的批注 API 允许同时激活(附加)多个批注 UI。为了允许同时使用多个 UI,必须在激活批注 UI 时注册批注过滤功能。尽管如此,一个批注仍然只能由一个批注 UI 处理。
新的批注 API 允许同时设置多个活动批注(每个批注 UI 最多一个),因此,在某些情况下,多个批注可能对不同的批注 UI 处于活动状态。
现在,批注 UI 随其自己的过滤后的批注集合一起激活,并负责对更多事件做出反应。这允许更多的自定义可能性,但是,不幸的是,它需要为自定义批注 UI 类编写更多代码。更改后,批注 UI 负责
- 显示批注。
- 当批注应该被激活时将其标记为活动状态,并通过可观察属性传播此更改。
- 对编辑器事件做出反应(尽管这计划在将来进行更改和简化)。
- 对批注集合中的焦点更改做出反应。
- 处理新添加和删除的批注。
- 通过
setActiveAnnotation()
方法处理来自外部位置的批注激活和停用。
现有的Annotations
类已拆分为新的Annotations
类(这是一个全局批注集合)和新的AnnotationsUIs
类(主要负责注册和激活批注 UI)。
在使用AnnotationView
的地方,使用了新的Annotations
类实例,它主要公开以下属性
target
– 批注应该绑定到的目标元素。它可以是Rect
实例或 HTML 元素。view
–AnnotationView
实例。type
– 批注的类型。isActive
– 一个属性,用于确定批注视图和批注内部视图是否处于活动状态。当它更改活动批注时,应该由批注 UI 更改它。
Sidebar
类停止对全局批注集合进行操作。从现在起,负责显示侧边栏的批注 UI 应该为侧边栏提供其批注集合,并使用新的rearrange()
和refresh()
方法通知可能的动作。
EditorAnnotations
开始触发事件,而不是操作全局批注集合。从现在起,每个批注 UI 都需要创建与该类的集成。这可能会在将来发生变化,因为它会使自定义 UI 的实现更加复杂。
# 代码迁移示例
# 注册和激活批注 UI
之前
const annotations = editor.plugins.get( 'Annotations' );
annotations.register( 'myCustomUI', myCustomUI );
annotations.switchTo( 'myCustomUI' );
// Activate and deactivate the UI.
annotations.attach( 'myCustomUI' );
annotations.detach( 'myCustomUI' );
之后
const annotationsUIs = editor.plugins.get( 'AnnotationsUIs' );
annotationsUIs.register( 'myCustomUI', myCustomUI );
annotationsUIs.switchTo( 'myCustomUI' );
// Activate and deactivate the UI.
annotationsUIs.activate( 'myCustomUI' );
annotationsUIs.deactivate( 'myCustomUI' );
// Activate two different UIs for comments and suggestions.
annotationsUIs.activate( 'wideSidebar', annotation => annotation.type === 'comment' );
annotationsUIs.activate( 'inline', annotation => annotation.type !== 'comment' );
annotationsUIs.deactivateAll();
# 使用全局批注集合和活动批注
之前
const annotations = editor.plugins.get( 'Annotations' );
// An annotation view or `null`.
const activeAnnotationView = annotations.activeView;
// A collection of annotation views.
const annotationViewCollection = annotations.items;
// Adding an annotation to the collection.
const target = new Rect( { left: 0, top: 0 } );
annotations.add( innerView, target );
// Removing an annotation based on the inner view.
annotations.remove( innerView );
之后
const annotations = editor.plugins.get( 'Annotations' );
// A set of active annotations.
const activeAnnotations = annotations.activeAnnotations;
// An array of active annotation views.
Array.from( activeAnnotations, annotation => annotation.view );
// A collection of annotations (`Annotation` class items).
const annotationCollection = annotations.collection;
// Adding an annotation to the collection.
annotations.add( new Annotation( {
view: new AnnotationView( innerView ),
target: new Rect( { left: 0, top: 0 } ),
type: 'comment' // The type is used only for the AnnotationsUIs filtering mechanism.
} ) );
// Removing an annotation based on the inner view.
const annotation = annotations.getByInnerView( innerView );
annotations.remove( annotation );
# 创建自定义批注 UI
之前
class CustomAnnotationUI {
attach() {
const annotations = this.editor.plugins.get( 'Annotations' );
// The code responsible for displaying annotations
// based on the global collection of annotations.
}
detach() {
// The code responsible for hiding the UI and detaching
// listeners from the global annotation collection.
}
}
之后
class CustomAnnotationUI extends ContextPlugin {
constructor() {
this.set( 'activeAnnotation', null );
}
setActiveAnnotation( annotation ) {
// The code responsible for reacting to annotation changes.
if ( annotation === this.activeAnnotation ) {
return;
}
if ( this.activeAnnotation ) {
this.activeAnnotation.isActive = false;
}
if ( annotation ) {
annotation.isActive = true;
}
this.activeAnnotation = annotation;
}
attach( annotations ) {
// The code responsible for displaying annotations
// based on the annotation collection passed to this UI.
// The code responsible for setting an active and non-active annotation
// based on the annotation focus changes.
this.listenTo( annotations, 'focus', ( evt, annotation ) => {
this.setActiveAnnotation( annotation );
} );
this.listenTo( annotations, 'blur', () => {
this.setActiveAnnotation( null );
} );
// The code responsible for the integration with editor annotation markers,
// editor events and the editor selection changes.
// The requirement of this integration might change in the future.
// If the plugin was initialized as an editor plugin, the integration
// should look like the following:
const editorAnnotations = editor.plugins.get( 'EditorAnnotations' );
this.listenTo( editorAnnotations, 'refresh', () => refreshActiveAnnotation.bind( this ) );
this.listenTo( editorAnnotations, 'blur' ( evt, { isAnnotationTracked } ) => {
if ( this.activeAnnotation && isAnnotationTracked( this.activeAnnotation ) ) {
this.setActiveAnnotation( null );
}
} );
this.listenTo( editorAnnotations, 'uiUpdate' () => annotations._refreshPositioning();
function refreshActiveAnnotation() {
const selectedAnnotations = editorAnnotations.getOrderedSelectedAnnotations( {
annotations: this._annotations,
activeAnnotation: this.activeAnnotation
} );
this.setActiveAnnotation( selectedAnnotations[ 0 ] || null );
}
}
detach() {
// The code responsible for hiding the UI and detaching
// listeners from the annotation collection, editor annotations and others.
}
}
我们每天都在努力使我们的文档保持完整。您是否发现过时的信息?是否有遗漏的内容?请通过我们的问题跟踪器报告。
随着版本 42.0.0 的发布,我们重写了许多文档,以反映新的导入路径和功能。感谢您的反馈,帮助我们确保文档的准确性和完整性。