You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何为基于TypeScript的React版CkEditor 5开发自定义插件?

基于TypeScript的React版CKEditor 5 视频导入自定义插件开发步骤

1. 前置环境配置

  • 确保项目内所有CKEditor 5相关依赖版本完全一致,避免类型兼容问题
  • 安装核心依赖与TS类型包:
npm install @ckeditor/ckeditor5-react @ckeditor/ckeditor5-core @ckeditor/ckeditor5-ui @ckeditor/ckeditor5-utils @types/ckeditor__ckeditor5-core @types/ckeditor__ckeditor5-ui

2. 扩展CKEditor 5 TS类型声明

新建types/ckeditor5-video.d.ts文件,补充自定义插件的类型定义,避免TS编译报错:

import '@ckeditor/ckeditor5-core';

declare module '@ckeditor/ckeditor5-core' {
  interface EditorConfig {
    video?: {
      allowedFileTypes?: string[];
      uploadApi?: string;
    }
  }

  interface PluginsMap {
    [VideoImport.pluginName]: VideoImport;
  }
}

3. 实现视频导入插件核心逻辑

新建plugins/VideoImport.ts文件:

import { Plugin } from '@ckeditor/ckeditor5-core';
import { FileDialogButtonView } from '@ckeditor/ckeditor5-ui';
import { FileRepository } from '@ckeditor/ckeditor5-upload';

export default class VideoImport extends Plugin {
  public static get pluginName() {
    return 'VideoImport' as const;
  }

  public static get requires() {
    return [FileRepository] as const;
  }

  public init(): void {
    const editor = this.editor;
    const t = editor.t;

    // 注册video标签的schema规则
    editor.model.schema.register('video', {
      allowWhere: 'imageBlock',
      isObject: true,
      isBlock: true,
      allowAttributes: ['src', 'controls', 'width', 'height']
    });

    // 配置model到视图的转换规则
    editor.conversion.for('downcast').elementToElement({
      model: 'video',
      view: (modelElement, { writer }) => {
        return writer.createEmptyElement('video', {
          src: modelElement.getAttribute('src') as string,
          controls: true,
          width: '100%'
        });
      }
    });

    // 配置视图到model的转换规则
    editor.conversion.for('upcast').elementToElement({
      view: { name: 'video' },
      model: 'video'
    });

    // 注册工具栏按钮
    editor.ui.componentFactory.add('videoImport', () => {
      const button = new FileDialogButtonView(editor.locale);
      button.set({
        label: t('插入视频'),
        tooltip: true,
        acceptType: ['video/*'],
        allowMultipleFiles: false
      });

      button.on('execute', () => {
        const files = button.fileView.files;
        if (!files?.length) return;
        this.handleVideoUpload(files[0]);
      });

      return button;
    });
  }

  private handleVideoUpload(file: File): void {
    const editor = this.editor;
    // 此处替换为你的业务侧视频上传逻辑
    const uploadPromise = new Promise<string>((resolve) => {
      // 示例:模拟上传完成后返回视频在线地址
      setTimeout(() => resolve(URL.createObjectURL(file)), 1000);
    });

    uploadPromise.then(videoUrl => {
      editor.model.change(writer => {
        const videoNode = writer.createElement('video', { src: videoUrl });
        editor.model.insertContent(videoNode, editor.model.document.selection);
      });
    }).catch(err => {
      console.error('视频上传失败:', err);
    });
  }
}

4. 在React组件中引入插件

import { CKEditor } from '@ckeditor/ckeditor5-react';
import ClassicEditor from '@ckeditor/ckeditor5-build-classic';
import VideoImport from './plugins/VideoImport';

const MyEditor = () => {
  return (
    <CKEditor
      editor={ClassicEditor}
      config={{
        extraPlugins: [VideoImport],
        toolbar: [
          'heading', 'bold', 'italic', 'videoImport'
          // 其他工具栏配置项
        ]
      }}
      onChange={(event, editor) => {
        console.log(editor.getData());
      }}
    />
  );
};

export default MyEditor;

常见问题处理

  • 如果出现插件类型不匹配的报错,可以在引入插件时添加as any临时兼容,后续再补充完整的类型定义即可
  • 如果需要支持粘贴视频链接插入的能力,可在上述插件基础上扩展弹窗表单逻辑即可

内容的提问来源于stack exchange,提问作者Milos

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.12 03:39:04