Remirror项目深度解析:如何创建支持内容编辑的NodeView
remirror ProseMirror toolkit for React 🎉 项目地址: https://gitcode.com/gh_mirrors/re/remirror
前言
在现代富文本编辑器开发中,自定义节点(Custom Node)的实现是一个常见需求。Remirror作为基于ProseMirror的React富文本编辑器框架,提供了强大的NodeView机制来满足这一需求。本文将深入探讨如何在Remirror中创建支持内容编辑的自定义节点。
NodeView基础概念
NodeView是Remirror中的核心概念,它允许开发者在文档中插入自定义节点。简单来说,NodeView就是Remirror节点在DOM中的可视化表现。
举个例子,假设我们需要在编辑器中插入一个用户卡片,包含用户头像、姓名等信息,并且还希望在这个卡片内添加可编辑的评论区域。这种复杂需求就需要通过NodeView来实现。
两种实现NodeView的方式
Remirror提供了两种创建NodeView的方法:
- 底层方法:通过
NodeViewsExtension
手动创建NodeView,这种方式更灵活但复杂度较高 - 高级方法:使用
ReactComponentExtension
将React组件自动转换为NodeView,这是对React开发者更友好的方式
本文将重点介绍第二种方法,即如何使用React组件创建支持内容编辑的NodeView。
创建基础NodeView(无内容)
第一步:定义节点扩展
首先需要创建一个新的节点扩展,让Remirror认识我们的自定义节点。这里我们创建一个UserCardExtension
:
class UserCardExtension extends NodeExtension {
get name() {
return 'user-card' as const;
}
createNodeSpec(): NodeExtensionSpec {
return {
attrs: {
id: { default: null },
name: { default: '' },
image: { default: '' },
},
content: '', // 不允许内容
toDOM: (node) => {
const attrs: DOMCompatibleAttributes = {
'data-user-id': node.attrs.id,
'data-user-name': node.attrs.name,
'data-user-image': node.attrs.image,
};
return ['div', attrs];
},
parseDOM: [
{
attrs: {
id: { default: null },
name: { default: '' },
image: { default: '' },
},
tag: 'div[data-user-id]',
getAttrs: (dom) => {
const node = dom as HTMLAnchorElement;
return {
id: node.getAttribute('data-user-id'),
name: node.getAttribute('data-user-name'),
image: node.getAttribute('data-user-image'),
};
},
},
],
};
}
}
关键点说明:
name
属性定义了节点的唯一标识attrs
定义了节点支持的属性content: ''
表示该节点不允许包含内容toDOM
定义了如何将节点渲染到DOMparseDOM
定义了如何从DOM解析回节点
第二步:创建React组件
接下来创建一个简单的React组件来表示用户卡片:
function UserCard({ node }) {
const { name, imageSrc } = node.attrs;
return (
<div className='card'>
<img src={imageSrc} />
<h4>{name}</h4>
</div>
);
}
第三步:关联组件与扩展
最后将React组件关联到扩展:
class UserCardExtension extends NodeExtension {
// ...其他代码
ReactComponent: ComponentType<NodeViewComponentProps> = UserCard;
}
至此,我们已经创建了一个不支持内容编辑的基础NodeView。
进阶:支持内容编辑的NodeView
现在我们来升级这个NodeView,使其支持内容编辑功能。
修改节点规范
首先修改createNodeSpec
方法:
createNodeSpec(): NodeExtensionSpec {
return {
attrs: {
id: { default: null },
name: { default: '' },
imageSrc: { default: '' },
},
content: 'block*', // 允许空内容或块级内容
toDOM: (node) => {
const attrs: DOMCompatibleAttributes = {
'data-user-id': node.attrs.id,
'data-user-name': node.attrs.name,
'data-user-image-url': node.attrs.imageSrc,
};
return ['div', attrs, 0]; // 注意这里的数字0
},
// ...其他代码
};
}
关键变更:
content: 'block*'
表示允许空内容或块级内容toDOM
返回值数组中添加了数字0,这是ProseMirror的特殊标记,表示内容插入位置
更新React组件
修改React组件以支持内容编辑:
function UserCard({ node, forwardRef }) {
const { name, imageSrc } = node.attrs;
return (
<div className='card'>
<div contentEditable='false'>
<img src={imageSrc} alt='Avatar' style={{ width: '100%' }} />
<h4>
<b>{name}</b>
</h4>
</div>
<p ref={forwardRef} />
</div>
);
}
关键点说明:
forwardRef
必须附加到内容渲染的位置- 使用
contentEditable='false'
来禁用特定元素的编辑功能
技术原理深入
contentDOM机制
当NodeView需要包含可编辑内容时,Remirror使用contentDOM机制来管理这部分内容。forwardRef
实际上就是contentDOM的挂载点。
内容位置标记
在toDOM
方法中返回的数组中的数字0(称为"hole")是一个特殊标记,它告诉ProseMirror在哪里插入子节点。这个标记必须是其父节点中的唯一子元素。
最佳实践
- 内容控制:对于不需要编辑的部分,记得添加
contentEditable='false'
- 样式隔离:为自定义节点添加特定类名,避免样式污染
- 性能优化:复杂的NodeView应考虑使用React.memo进行优化
- 可访问性:确保自定义节点满足无障碍访问要求
总结
通过本文,我们学习了如何在Remirror中创建支持内容编辑的自定义NodeView。关键步骤包括:
- 创建节点扩展并定义其规范
- 开发对应的React组件
- 使用contentDOM机制支持内容编辑
- 合理控制节点的可编辑区域
这种机制非常灵活,可以用来实现各种复杂的编辑器功能,如嵌入式卡片、可编辑的代码块等。掌握NodeView的创建方法,可以大大扩展Remirror的编辑能力。
remirror ProseMirror toolkit for React 🎉 项目地址: https://gitcode.com/gh_mirrors/re/remirror
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考