CKEditor 5 Media Embed 配置实战:数据输出格式与媒体提供方的扩展、移除和重写
2026/9/16 16:35:03 网站建设 项目流程

CKEditor 5 Media Embed 配置实战:数据输出格式与媒体提供方的扩展、移除和重写

【免费下载链接】ckeditor5Powerful rich text editor framework with a modular architecture, modern integrations, and features like collaborative editing.项目地址: https://gitcode.com/GitHub_Trending/ck/ckeditor5

本篇技术指南围绕 CKEditor 5 的MediaEmbed插件配置展开,覆盖mediaEmbed配置对象中控制数据输出格式的核心参数(previewsInDataelementName)以及管理媒体提供方的三组配置(providersextraProvidersremoveProviders)。读完本文,你将能够按需决定编辑器输出“语义化<oembed>标签”还是“带预览的 HTML”,并能扩展、移除或完全重写默认支持的媒体提供方,所有配置均结合当前仓库源码实现逐一对应,可直接复制使用。

MediaEmbed是一个“粘合”插件,在 mediaembed.ts 中声明其依赖MediaEmbedEditingMediaEmbedUIAutoMediaEmbedWidget四个插件。本文讨论的全部配置项都通过editor.config.define('mediaEmbed', ...)注入,默认值定义在 mediaembedediting.ts 的构造函数中;配置类型的完整声明见 mediaembedconfig.ts 中的MediaEmbedConfig接口。

数据输出格式

MediaEmbed支持两种数据输出格式,通过config.mediaEmbed.previewsInData切换。需要注意:该选项只影响输出数据,不改变编辑器内部的显示方式——可预览的媒体在编辑器里始终显示预览(来源:media-embed-configuration.md 中的说明框)。

语义化数据输出(默认)

默认情况下(previewsInDatafalse),无论媒体是否可预览,输出的都是语义化的<oembed url="...">标签:

<figure class="media"> <oembed url="https://media-url"></oembed> </figure>

这种格式最适合以下两类场景:应用在服务端对媒体进行解析(展开),或在前端直接渲染(参见 media-embed-external-preview.md)。它保留了最灵活的数据库表示——URL 被持久化,具体展示逻辑交给消费端决定。

elementName自定义语义标签

通过config.mediaEmbed.elementName可以覆盖默认的oembed标签名。例如设置为o-embed

mediaEmbed: { elementName: 'o-embed' }

输出变为:

<figure class="media"> <o-embed url="https://media-url"></o-embed> </figure>

默认值为'oembed'(见 mediaembedconfig.ts 中elementName的 JSDoc 及@default标注)。

一个容易忽略的向后兼容细节:如果elementName被重写为非默认值,旧的<oembed>元素仍然可以被识别和显示。从源码可以确认这一点——upcast(数据到模型)转换器同时匹配两个标签名,见 mediaembedediting.ts:

// Upcast semantic media. .elementToElement( { view: element => [ 'oembed', elementName ].includes( element.name ) && element.getAttribute( 'url' ) ? { name: true } : null, // ... } )

因此即使切换到o-embed,历史数据中的<oembed>也能被正常还原为模型中的media元素。

在数据中包含预览(previewsInData)

mediaEmbed.previewsInData设为true后,媒体将以与编辑器中完全一致的形式输出:对于“可预览”媒体,预览 HTML(通常是<iframe>)会被写入数据:

<figure class="media"> <div><figure class="media"> <oembed url="https://media-url"></oembed> </figure>

源码中这一“分支决策”发生在Media.getViewElement()(mediaregistry.ts):当options.renderMediaPreview为真且该媒体带有html渲染函数时,生成带data-oembed-url属性的div并写入预览 HTML;否则生成一个带url属性的空元素(即语义化标签,标签名由elementName决定):

if ( options.renderForEditingView || ( options.renderMediaPreview && this.url && this._previewRenderer ) ) { // 生成 <div>ClassicEditor .create( { // ... Other configuration options ... mediaEmbed: { extraProviders: [ { // 提供方名称,例如用于 removeProviders 时引用: name: 'myProvider', // URL 正则或正则数组: url: /^example\.com\/media\/(\w+)/, // 仅当媒体可预览时才需要定义: html: match => '...' } ] } } ) .then( /* ... */ ) .catch( /* ... */ );

从源码结构看,合并逻辑发生在MediaRegistry构造函数(mediaregistry.ts):config.providers.concat(config.extraProviders)之后再按removeProviders过滤,即extraProviders总是排在默认提供方之后。配合“先到先得”的 URL 匹配规则(见下文),这意味着默认提供方对同一 URL 拥有更高优先级。

移除媒体提供方:removeProviders

config.mediaEmbed.removeProviders接受提供方名称数组,从合并后的提供方列表中剔除指定提供方。官方文档给出的典型用例是只保留可预览的提供方:

ClassicEditor .create( { // ... Other configuration options ... mediaEmbed: { removeProviders: [ 'instagram', 'twitter', 'googleMaps', 'flickr', 'facebook' ] } } ) .then( /* ... */ ) .catch( /* ... */ );

实现上,构造函数将removeProviders装入Set,过滤掉name命中其中的提供方;同时,缺少name字段的提供方会被media-embed-no-provider-name警告丢弃且不生效(mediaregistry.ts)。这一点值得注意:自定义提供方一定要写name,否则既无法被引用也无法被移除。

对应的测试用例见 tests/mediaembedediting.js 中#extraProviders#removeProviders两个 describe 块(含removeProviders同时作用于providersextraProviders的验证),以及 tests/mediaregistry.js 中注册表层面的扩展/移除测试。

重写媒体提供方:providers

要完全接管提供方列表,使用config.mediaEmbed.providers并按提供方语法定义你自己的集合——这会整体替换默认列表(默认 9 个提供方全部失效):

ClassicEditor .create( { // ... Other configuration options ... mediaEmbed: { providers: [ { name: 'example', // URL 正则或正则数组: url: /^example\.com\/media\/(\w+)/, // 仅当媒体可预览时才需要定义: html: match => `The HTML representing the media with ID=${ match[ 1 ] }.` } // ... 更多提供方 ] } } ) .then( /* ... */ ) .catch( /* ... */ );

官方文档建议以仓库中的默认配置为模板,即 mediaembedediting.ts) 中editor.config.define('mediaEmbed', ...)内的定义。

提供方语法详解

提供方是一个MediaEmbedProvider对象(mediaembedconfig.ts),包含三个字段:

  • name: string(必填):提供方名称,removeProviders按此匹配;
  • url: RegExp | RegExp[](必填):媒体 URL 的正则,可以是单个正则或正则数组,任一匹配即视为该提供方的媒体。RegExp.match()的完整结果数组会传入html渲染函数;
  • html?: ( match: RegExpMatchArray ) => string(可选):渲染函数,用于生成编辑视图与(开启previewsInData时的)数据输出中的预览 HTML。未定义时,媒体以通用表示呈现,数据输出恒为语义化标记。

url正则时不必包含协议(http://https://)和www子域——匹配前它们会被剥离。源码中 MediaRegistry._getUrlMatches() 按三步尝试:

  1. 直接用原始 URL 匹配(不剥离协议和www);
  2. 剥离协议后匹配(url.replace( /^https?:\/\//, '' ));
  3. 再剥离www.子域后匹配。

任意一步命中即返回匹配结果,因此url: /^example\.com\/media\/(\w+)/同时能覆盖https://www.example.com/...http://example.com/...与裸example.com/...等形式。

匹配优先级:提供方按配置顺序处理,第一个匹配该 URL 的提供方获胜,URL 不会再与后续提供方比较(该规则写在providers字段的 JSDoc 中,mediaembedconfig.ts;实现见 MediaRegistry._getMedia() 的线性循环)。所以如果你想用“允许一切”的正则兜底,必须把它放在最后:

{ name: 'allow-all', url: /^.+/ }

响应式媒体写法MediaEmbedProvider的 JSDoc(mediaembedconfig.ts)给出了一段可直接参考的html实现——外层用普通<div>包裹(便于外部样式或选择器命中该容器),内层<iframe>用 HTMLwidth/height属性提供固有尺寸、用 CSSwidth: 100%; height: auto; aspect-ratio: 16 / 9;实现随容器缩放并保持宽高比:

html: match => '<div>' + `<iframe src="..." width="1280" height="720" ` + `style="width: 100%; height: auto; aspect-ratio: 16 / 9; border: 0; display: block;" ` + 'frameborder="0" allowfullscreen>' + '</iframe>' + '</div>'

默认的 YouTube、Vimeo、Dailymotion 提供方正是采用这套 16:9 的响应式写法(mediaembedediting.ts)。

配置如何驱动数据管线

理解了配置项,可以再看一眼它们在转换管线中的落点,帮助排查“输出不是我想要的格式”这类问题(实现均在 mediaembedediting.ts 的init()中):

  • 数据下转(模型 → 数据)dataDowncast把模型media元素转换为<figure class="media">包裹的结构;是否渲染预览由renderMediaPreview: !!url && renderMediaPreviewrenderMediaPreviewpreviewsInData配置值)决定,语义标签名由elementName决定;
  • 编辑视图下转(模型 → 视图):同样调用createMediaFigureElement()(utils.ts),但强制renderForEditingView: true——可预览媒体渲染真实预览,不可预览媒体渲染占位符(图标 + 可点击的 URL 链接,即文首截图所示样式);
  • URL 属性变更attribute:url:media的转换器(converters.ts)会在 URL 修改后整体重建<figure>内容,因此粘贴新链接时预览/语义标记会即时切换;
  • upcast(数据 → 模型):两条路径分别还原语义化元素(oembed或自定义elementName,需带url属性)与非语义化的div[data-oembed-url],且两种情况都会先经registry.hasMedia(url)校验 URL 是否仍被某个现存提供方匹配——这也是“移除提供方后旧媒体 URL 不再被识别”的根源。

小结

配置项作用默认值
previewsInDatatrue时可预览媒体的输出包含预览 HTML(div[data-oembed-url]+<iframe>);不可预览媒体恒为语义化输出false(语义化输出)
elementName语义化输出的标签名;旧oembed标签保持兼容可读'oembed'
providers整体替换默认提供方列表9 个内置提供方
extraProviders在默认列表之后追加提供方
removeProviders按名称从providers+extraProviders合并结果中剔除

核心决策路径很简单:想让数据库只存 URL、展示交给服务端或前端渲染,就保持默认语义化输出;想让存储的数据在网站上开箱即用,就开启previewsInData并用removeProviders/providers把支持的 URL 限定在可预览范围内;要接入自有视频或第三方内容平台,则用extraProviders追加(保留默认)或providers重写(完全接管),并按“先到先得”的优先级安排正则顺序。

【免费下载链接】ckeditor5Powerful rich text editor framework with a modular architecture, modern integrations, and features like collaborative editing.项目地址: https://gitcode.com/GitHub_Trending/ck/ckeditor5

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询