- 前端
- UI组件
【免费下载链接】react-page
Next-gen, highly customizable content editor for the browser - based on React and written in TypeScript. WYSIWYG on steroids.
本文是 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:定义如何编辑该插件,两种方式二选一:- schema 驱动的自动表单:
{ type: 'autoform', schema: JsonSchema },由 JSON Schema 自动生成表单(还可以通过columnCount控制列数、通过Content自定义表单内部布局,见 AutoformControlsDef); - 自定义控件组件:
{ type: 'custom', Component },提供完全自定义的控件组件(见 CustomControlsDef)。
- schema 驱动的自动表单:
以仓库内置的 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 的错误假设而遇到类型报错。处理原则:
- 如果认为是 react-page 自身的问题,请上报 issue;
- 如果是自己的代码问题,请修正代码;
- 临时应急方案:如果确实是 react-page 代码的问题,又不想因此停滞开发进度,可以利用 tsconfig 中的
paths属性覆盖 react-page 的类型定义:
{ "compilerOptions": { "paths": { // 将 @react-page/core 等指向你自己的类型修复文件 "@react-page/core": ["./types-fixes/core.d.ts"] } } }但官方明确提醒:一旦官方修复了问题,请尽快移除这些"脏修复"(dirty fixes),以便始终跟进最新变更。
七、升级后的验证建议
完成上述迁移后,建议从以下几个方面验证升级正确性:
- 数据层:升级前备份旧数据;升级后让用户保存一次新内容,确认 migrateValue 触发的自动迁移正常完成,检查保存出的新格式内容是否完整(尤其多语言
dataI18n与嵌套rows)。 - 渲染层:用
readOnly模式渲染历史内容,确认HTMLRenderer(在 packages/editor/src/renderer/HTMLRenderer.tsx)能正确输出;Editor.tsx 中编辑器始终先以 readOnly 方式挂载再切换编辑态,因此 SSR 场景也应在升级后回归。 - 插件层:逐个验证自定义插件在新
CellPlugin类型下编译通过,Renderer在编辑/只读模式下均正常,controls的 autoform/custom 两种形态均可打开与保存。 - 编辑器行为:确认空内容时不再自动插入默认 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.
相关推荐
Flue 迁移指南:从 1.0.0-beta.9 升级到 Flue 2 的完整实战手册
Flue 迁移指南:从 1.0.0 beta.9 升级到 Flue 2 的完整实战手册 本指南面向运行中的 beta 应用,系统讲解将 Flue 代码库从 1.
人工智能大模型AI AgentAgent 框架工具调用Agent 沙箱MCP ClientsActix Web 4.0 升级迁移完全指南:从 v3 到 v4 的 Breaking Changes 逐项解析与实战迁移
Actix Web 4.0 升级迁移完全指南:从 v3 到 v4 的 Breaking Changes 逐项解析与实战迁移 导读 本文以 actix web/M
后端Web框架React Native Elements 4.0 迁移指南:从 v3 升级到 @rneui/themed 的完整实战手册
React Native Elements 4.0 迁移指南:从 v3 升级到 @rneui/themed 的完整实战手册 React Native Eleme
UI组件移动开发前端
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考