☰
React-Page 升级迁移完全指南:从 ory-editor 到 1.0.0 的 Breaking Change 实战手册
2026/9/25 1:20:20 网站建设 项目流程
  • 前端
  • UI组件

【免费下载链接】react-page

Next-gen, highly customizable content editor for the browser - based on React and written in TypeScript. WYSIWYG on steroids.

项目地址:https://gitcode.com/gh_mirrors/rea/react-page
点击查看免费下载

本文是 UPGRADE.md 的完整展开与深化,面向正在使用 React-Page(或此前名为 ory-editor)的开发者,系统梳理 0.6.x → 0.7.x → 1.0.0 各阶段的关键破坏性变更、包重命名对照、自定义 Cell 插件与自定义 Slate 插件的迁移步骤,并结合本仓库源码(packages/editor 等)给出可验证的实现细节。读完本文,你将能独立完成一次从旧版本 API 到新版本CellPlugin体系的平滑升级,并理解数据自动迁移与渲染层变化背后的原理。

一、为什么需要这份升级指南

React-Page 的维护者在这份文档开篇即说明其意图:尽可能降低破坏性变更(breaking changes)的迁移成本。同时坦诚地提醒两点:

  • 文档不可能穷尽所有破坏性变更,并非所有 breaking change 都会收录在本文档中;
  • 破坏性变更的完整列表以官方 Release 说明为准,升级前建议先核对版本发布记录。

在动手升级前,请记住 UPGRADE.md 给出的最重要的一条操作纪律:升级前务必备份数据,一旦发现问题立即反馈 issue。这直接与 1.0.0 引入的数据迁移机制有关(详见下文"数据自动迁移"一节)。

二、1.0.0:大规模内部重构与 API 收敛

1.0.0 是一次大规模重构,改动主要集中在内部("changed and modernized a lot under the hood, but not much on the outside"),目标是:让后续改进更容易、获得更好的性能、减小打包体积(reduced bundle size)。它引入了一些为了清晰化 API 而必须做的破坏性变更,但官方认为迁移步骤是"straight forward"(直接了当)的。

2.1 包结构统一:多包并入 @react-page/editor

1.0.0 之前,核心代码被拆分为多个包。1.0.0 起发生合并:

旧包新状态
@react-page/core不再存在
@react-page/ui不再存在
@react-page/renderer不再存在

现在你只需要从@react-page/editor导入一切。这与当前仓库的源码结构完全一致:在 packages/editor/src/index.tsx 中,export * from './core/types'、export * from './core/components/hooks'、export * from './ui'以及export default Editor等导出全部从单一入口完成,Editor组件、Value类型、Migration类型、makeUniformsSchema、migrateValue、getTextContents、createValue等均可从此入口获得。仓库内所有内容插件也统一从@react-page/editor导入类型,例如 packages/plugins/content/image/src/createPlugin.tsx 中的import type { CellPlugin } from '@react-page/editor'。

注意一处细节:UPGRADE.md 的"Migrating custom plugins"小节中写着import type { CellPlugin } from '@react-page/renderer',但该文档同页已声明@react-page/renderer不再存在。结合本仓库全部实际代码(所有插件与示例均从@react-page/editor导入),正确的写法是从@react-page/editor导入。请以仓库实际代码为准。

2.2pluginsprop 更名为cellPlugins

<Editor />上的pluginsprop 被重命名为cellPlugins,目的是"为未来其他插件类型腾出命名空间"。在 packages/editor/src/editor/Editor.tsx 中可以看到,cellPlugins是Editor组件解构出的核心 prop,并被放入renderOptions传入编辑内核;同时EditorProps类型是Options & Callbacks & RenderOptions的交叉类型,其中RenderOptions在 packages/editor/src/core/defaultOptions.ts 中定义为{ cellPlugins: [], cellSpacing: null }。

实际用例如仓库示例 examples/pages/examples/simple.tsx:

import Editor, { Value } from '@react-page/editor'; import slate from '@react-page/plugins-slate'; import image from '@react-page/plugins-image'; // Define which plugins we want to use. const cellPlugins = [slate(), image]; export default function SimpleExample() { const [value, setValue] = useState<Value | null>(null); return ( <PageLayout> <Editor cellPlugins={cellPlugins} value={value} onChange={setValue} /> </PageLayout> ); }

注意这里cellPlugins接收的是CellPlugin[]数组,且布局插件与内容插件已被统一为同一种CellPlugin类型(见下一节)。

2.3 布局插件与内容插件统一为 CellPlugin

1.0.0 之前,layout 插件与 content 插件是两种不同的体系;1.0.0 起它们被统一为CellPlugin。从 packages/editor/src/core/types/plugins.ts 的CellPlugin类型定义可以看出,一个插件同时具备渲染(Renderer)、编辑控制(controls)、数据初始化(createInitialData)、子插件约束(childConstraints、cellPlugins)、内嵌布局(cellSpacing、createInitialChildren)等能力,因此既能做内容插件(如 image 插件),也能做布局插件(如packages/plugins/layout/background)。这也是仓库中packages/plugins/content与packages/plugins/layout目录并存但共享同一类型体系的根本原因。

2.4defaultPlugin不再需要

1.0.0 之前,编辑器需要defaultPlugin来自动填充空白单元。1.0.0 起:

  • defaultPlugin不再是必填项;
  • 编辑器在内容为空时不再自动添加一个 cell;
  • 取而代之的是在空白处显示一个"添加新 cell"的按钮,由用户显式选择要插入的插件。

2.5 数据自动迁移(无向下迁移)

1.0.0 将内容数据迁移到新格式。迁移发生在用户下一次保存新内容时,并且:

  • 是单向的,没有 down migration(向下迁移);
  • 因此官方强烈建议在升级前备份数据;
  • 升级后一旦发现任何问题,应立即填写 issue 求助。

从源码看,这套机制由 packages/editor/src/core/migrations/migrate.ts 中的migrate与migrateValue实现:编辑器会根据Value中记录的version与当前内置的EDITABLE_MIGRATIONS列表,循环执行fromVersion <= currentDataVersion && toVersion > currentDataVersion的迁移链,逐级把旧数据推进到最新版本;同时会对每个 cell 的插件数据执行插件自身的migrations(当c.plugin.version与cellPlugins中对应插件version不一致时),并调用unserialize还原数据。migrateValue也被显式导出,可在 packages/editor/src/index.tsx 中看到,方便你在服务端或保存前主动完成迁移。

三、迁移自定义插件(Migrating custom plugins)

如果你有自定义插件,UPGRADE.md 给出了如下迁移清单,下面逐条结合源码展开:

3.1 类型与导入

强烈建议使用 TypeScript,并将插件类型声明为:

import type { CellPlugin } from '@react-page/editor'; const myPlugin: CellPlugin<MyData> = { ... };

CellPlugin是一个泛型类型,接收一个类型参数Data(可选),表示插件的数据对象类型。在 packages/editor/src/core/types/plugins.ts 中可见默认值DataTType = Record<string, unknown>。

3.2 字段改名:name→id,text→title

  • name(插件唯一标识)改名为id;
  • text(插件人类可读标题)改名为title。

在 CellPlugin 类型定义 中,id的注释明确写着"the plugins unique id. Only one plugin with the same id may be used",而name、text两个旧字段仍然保留,但均被标注为@deprecated please set id/@deprecated please set title。这说明当前版本为平滑过渡保留了旧字段,但新代码应一律使用新名字。

3.3Component拆分为Renderer+controls

旧版插件直接提供Component;1.0.0 起改为分别定义渲染组件与编辑控件:

  • Renderer:显示在 cell 中的组件,接收一个dataprop,其类型即Data。在 CellPlugin 类型 中,Renderer被定义为React.ComponentType<CellPluginComponentProps<DataT>>,而 CellPluginComponentProps 提供了nodeId、data、onChange、remove、readOnly、focused、lang、isPreviewMode、isEditMode等完整上下文。注意类型注释中的提醒:不要在 Renderer 中使用编辑器内部 hooks,因为它们无法在 readOnly 模式下工作。

  • controls:定义如何编辑该插件,两种方式二选一:

    1. schema 驱动的自动表单:{ type: 'autoform', schema: JsonSchema },由 JSON Schema 自动生成表单(还可以通过columnCount控制列数、通过Content自定义表单内部布局,见 AutoformControlsDef);
    2. 自定义控件组件:{ type: 'custom', Component },提供完全自定义的控件组件(见 CustomControlsDef)。

以仓库内置的 image 插件为例(packages/plugins/content/image/src/createPlugin.tsx):

const createPlugin = (settings?: ImageSettings): CellPlugin<ImageState> => { const mergedSettings = { ...defaultSettings, ...settings }; const Controls = mergedSettings.Controls; return { controls: { type: 'custom', Component: (props) => ( <Controls {...props} translations={mergedSettings.translations} imageUpload={mergedSettings.imageUpload} /> ), }, Renderer: mergedSettings.Renderer, id: 'ory/editor/core/content/image', version: 1, icon: mergedSettings.icon, title: mergedSettings.translations?.pluginName, isInlineable: true, description: mergedSettings.translations?.pluginDescription, }; };

这个实际实现恰好是 UPGRADE.md 迁移清单的完整示范:id(而非name)、title(而非text)、Renderer+controls(而非Component)、version(配合数据迁移使用)。关于自定义 cell 插件的更完整指南,见 docs/custom-cell-plugins.md;内置插件清单见 docs/builtin_plugins.md。

3.4 移除 create-plugin-materialui

如果你使用了@react-page/create-plugin-materialui,1.0.0 起可以直接移除它——它不再需要,改用上文提到的CellPlugin类型与controls体系即可(仓库中也已不存在该包)。

四、迁移自定义 Slate 插件

对于使用自定义数据的自定义 Slate 插件,1.0.0 的 API 略有调整,并且与 CellPlugin 的 API 完成了统一:

  • Slate 插件现在同样接收controls;
  • controls取值有两种形态,与 CellPlugin 完全一致:
    • type: "autoform"+schema(JsonSchema),自动生成表单;
    • type: "custom",使用自定义控件。

仓库中的 Slate 插件实现在 packages/plugins/content/slate/src 下,其插件工厂(如pluginFactories目录下的createComponentPlugin、createDataPlugin、createMarkPlugin、createListPlugin等)与控件机制可作参考;更详细的 Slate 插件开发文档见 docs/slate.md。

五、0.7.x:维护者变更与包重命名

0.7.x 是一个特殊阶段:该包的维护者发生变更,项目更名为react-page。如果你仍在使用旧包名,只需按下列对照表更新依赖即可:

旧包名新包名
ory-editor@react-page/react-page
ory-editor-core@react-page/core
ory-editor-plugins-divider@react-page/plugins-divider
ory-editor-plugins-html5-video@react-page/plugins-html5-video
ory-editor-plugins-image@react-page/plugins-image
ory-editor-plugins-default-native@react-page/plugins-default-native
ory-editor-plugins-slate@react-page/plugins-slate
ory-editor-plugins-spacer@react-page/plugins-spacer
ory-editor-plugins-video@react-page/plugins-video
ory-editor-plugins-background@react-page/plugins-background
ory-editor-plugins-parallax-background@react-page/plugins-parallax-background
ory-editor-renderer@react-page/renderer
ory-editor-ui@react-page/ui

注意:这张表对应 0.7.x 时代的分包结构;如果你要直接升级到 1.0.0,请以第二节为准——@react-page/core、@react-page/renderer、@react-page/ui等已全部并入@react-page/editor。当前仓库的 monorepo 结构(packages 目录)中仅保留@react-page/editor、@react-page/plugins-*内容插件与布局插件、以及@react-page/react-admin集成包,与 1.0.0 的收敛目标一致。

六、0.6.x:全面 TypeScript 支持

0.6.x 引入了完整的 TypeScript 支持。如果你使用 TypeScript 并编写自己的插件,可能会因为对 ory-editor 传入 props 的错误假设而遇到类型报错。处理原则:

  1. 如果认为是 react-page 自身的问题,请上报 issue;
  2. 如果是自己的代码问题,请修正代码;
  3. 临时应急方案:如果确实是 react-page 代码的问题,又不想因此停滞开发进度,可以利用 tsconfig 中的paths属性覆盖 react-page 的类型定义:
{ "compilerOptions": { "paths": { // 将 @react-page/core 等指向你自己的类型修复文件 "@react-page/core": ["./types-fixes/core.d.ts"] } } }

但官方明确提醒:一旦官方修复了问题,请尽快移除这些"脏修复"(dirty fixes),以便始终跟进最新变更。

七、升级后的验证建议

完成上述迁移后,建议从以下几个方面验证升级正确性:

  1. 数据层:升级前备份旧数据;升级后让用户保存一次新内容,确认 migrateValue 触发的自动迁移正常完成,检查保存出的新格式内容是否完整(尤其多语言dataI18n与嵌套rows)。
  2. 渲染层:用readOnly模式渲染历史内容,确认HTMLRenderer(在 packages/editor/src/renderer/HTMLRenderer.tsx)能正确输出;Editor.tsx 中编辑器始终先以 readOnly 方式挂载再切换编辑态,因此 SSR 场景也应在升级后回归。
  3. 插件层:逐个验证自定义插件在新CellPlugin类型下编译通过,Renderer在编辑/只读模式下均正常,controls的 autoform/custom 两种形态均可打开与保存。
  4. 编辑器行为:确认空内容时不再自动插入默认 cell,而是显示添加按钮。

仓库中还提供了多个可直接运行的示例用于对照验证,例如编辑示例 examples/pages/examples/simple.tsx、只读示例 examples/pages/examples/readonly.tsx、以及展示自定义插件迁移成果的 customContentPlugin.tsx、customLayoutPlugin.tsx 等,均可作为升级后的回归测试参照。

  • 前端
  • UI组件

【免费下载链接】react-page

Next-gen, highly customizable content editor for the browser - based on React and written in TypeScript. WYSIWYG on steroids.

项目地址:https://gitcode.com/gh_mirrors/rea/react-page
点击查看免费下载

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

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

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

立即咨询