- 文档 (Documents)
- 模式 (Schemas)
- 文档变换 (Document transformations)
- 视图组件 (The view component)
- 命令 (Commands)
- 协同编辑 (Collaborative editing)
本指南介绍了 ProseMirror 库中使用的各种概念,以及它们之间的相互关系。为了对系统 有一个完整的理解,建议按照内容顺序依次阅读,至少阅读到“视图组件(The view component)”部分。ProseMirror 提供了一套用于构建富文本编辑器(rich text editors) 的工具和概念,它的灵感源自所见即所得(WYSIWYG)的用户界面,但试图避免这种编辑方 式的陷阱。ProseMirror 的主要原则是让我们的代码完全掌控文档(document)以及对文档的 所有操作。这个文档并不是一堆 HTML,而是一个自定义的数据结构(custom data structure),其中只包含我们明确允许其包含的元素及我们指定关系的元素。所有更新都 通过单一入口点进行,您可以在此检查更新并作出反应。
核心库并不是一个简单的即插即用组件(drop-in component)——我们更注重模块化和可定 制性,而不是简单性,希望未来人们能基于 ProseMirror 构建可直接使用的编辑器。因此 ,本库更像是一套乐高积木(Lego set),而不是一辆火柴盒小汽车(Matchbox car) 。ProseMirror 拥有 4 个核心模块:
- prosemirror-model 定义了编辑器的文档模型(document model),即用来描述编辑 器内容的数据结构。
- prosemirror-state 提供了描述编辑器状态(state)的数据结构,包括当前选 区(selection)和用于从一个状态移动到下一个状态的事务系统(transaction system)。
- prosemirror-view 实现了一个用户界面组件,它将一个给定的编辑状态以可编辑元 素的形式显示在浏览器中,并处理与该元素的用户交互。
- prosemirror-transform 包含用于修改文档的功能,这些更改可以被记录和重放,这
构成了
state
模块中事务的基础,并使得撤销历史(undo history)和协同编辑 (collaborative editing)成为可能。
此外,还有一些由核心团队维护的扩展模块,它们提供了额外的功能,但您可以根据需要省 略或使用其它模块替代。例如,以下模块也很有用:基本编辑命令(basic editing commands)、键绑定(binding keys)、撤销历史(undo history)、输入 宏(input macros)、协作编辑(collaborative editing)、一个简单文档模 式(simple document schema)等。这些都可以在 GitHub 上的 ProseMirror 组织 找到。
由于 ProseMirror 不是以单个浏览器可加载脚本的形式分发的,您可能需要使用打包工具 (bundler)来使用它。打包工具是一种能够自动查找脚本依赖并将它们合并为一个大文件 的工具,方便在网页中加载。您可以在网上阅读更多关于打包的内容,例 如此处。
我的第一个编辑 器
各个乐高积木组件就这样组合在一起,以创建一个非常简化的编辑器:
import { schema } from 'prosemirror-schema-basic';
import { EditorState } from 'prosemirror-state';
import { EditorView } from 'prosemirror-view';
let state = EditorState.create({ schema });
let view = new EditorView(document.body, { state });
这里 schema
(模式(schema))是一个基本模式,它被用来创建一个状态(state),该状
态 将生成一个符合该模式的空文档,并在文档的开头生成一个默认的选区。最后,我们为这
个状态创建了一个视图,并将其附加到 document.body
。这样做会将状态中的文档渲染为
一个可编辑的 DOM 节点,并让用户在其中键入时生成状态事务(state transactions)。
此时,这个编辑器还不太好用。例如,如果我们按下回车键(enter),什么也不会发生,因 为核心库并不决定回车键应该执行什么操作。我们将在稍后介绍如何处理这些操作。
事务 (Transactions)
当用户键入或以其他方式与视图交互时,将产生“状态事务”(state transactions)。这意 味着 ProseMirror 并不是就地修改文档并隐式地更新状态。相反,每次更改都会创建一个 事务(transaction),该事务描述了对状态所做的更改。我们可以应用该事务来创建一个 新的状态,然后使用这个新状态来更新视图。
默认情况下,这一切都在内部完成,但是我们可以通过编写插件(plugins)或配置视图来
介入。例如,下面的代码添加了一个 dispatchTransaction
属性(prop),该属性会在
每次创建事务时被调用:
// (忽略了 import 语句)
let state = EditorState.create({ schema });
let view = new EditorView(document.body, {
state,
dispatchTransaction(transaction) {
console.log(
'Document size went from',
transaction.before.content.size,
'to',
transaction.doc.content.size
);
let newState = view.state.apply(transaction);
view.updateState(newState);
},
});
每次状态的更新都必须通过 view.updateState
方法完成,而普通的编辑更新都会通过派
发(dispatch)事务来触发。