Contribute to this guide

guide更新到 CKEditor 5 v25.x

更新 CKEditor 5 安装时,确保所有包的版本一致,以避免错误。

对于自定义构建,您可以尝试删除package-lock.jsonyarn.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 元素。
  • viewAnnotationView实例。
  • 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.
    }
}