如何为Tiptap创建自定义节点以直接渲染前端自定义HTML?
Tiptap自定义HTML节点渲染解决方案
1. 先定义能存储HTML的自定义节点
首先要在自定义节点的schema里添加专门存储HTML内容的属性,确保编辑器能正确保存和解析这个字段:
import { Node } from '@tiptap/core'; export const CustomHTMLNode = Node.create({ name: 'customHTML', group: 'block', atom: true, // 标记为原子节点,防止被编辑器拆分 // 定义存储HTML的属性 addAttributes() { return { htmlContent: { default: '', parseHTML: element => element.getAttribute('data-html'), renderHTML: attributes => ({ 'data-html': attributes.htmlContent }), }, }; }, // 编辑器内的渲染(可选,比如显示占位或预览) renderHTML({ HTMLAttributes }) { return ['div', HTMLAttributes, '自定义HTML内容']; }, // 支持从HTML解析到节点 parseHTML() { return [{ tag: 'div[data-html]' }]; }, });
2. 前端渲染实现类似dangerouslySetInnerHTML的效果
因为generateHTML默认会转义HTML字符串,所以不能直接靠它输出原始HTML,有两种可行方案:
方案一:遍历文档节点分别处理
手动遍历ProseMirror文档的节点,遇到自定义HTML节点就直接提取属性用React的dangerouslySetInnerHTML渲染,其他节点继续用generateHTML处理:
import { generateHTML } from "@tiptap/html"; function renderEditorContent(doc) { const contentElements = []; doc.content.forEach((node, index) => { if (node.type.name === 'customHTML') { // 直接渲染自定义HTML内容 contentElements.push( <div key={index} dangerouslySetInnerHTML={{ __html: node.attrs.htmlContent }} /> ); } else { // 其他节点用常规方法生成HTML const nodeHtml = generateHTML(node, [ StarterKit, RelatedArticleNode, InArticleImageNode, CustomHTMLNode ]); contentElements.push( <div key={index} dangerouslySetInnerHTML={{ __html: nodeHtml }} /> ); } }); return <div className="editor-content">{contentElements}</div>; }
方案二:自定义HTML生成器
重写generateHTML对自定义节点的处理逻辑,让它直接输出原始HTML,之后整体渲染:
import { generateHTML, HTMLGenerator } from "@tiptap/html"; // 创建自定义HTML生成器 const htmlGenerator = new HTMLGenerator([ StarterKit, RelatedArticleNode, InArticleImageNode, CustomHTMLNode ]); // 注册自定义节点的渲染规则 htmlGenerator.registerNode('customHTML', (node) => { return node.attrs.htmlContent; }); // 生成完整HTML内容 const fullHtml = htmlGenerator.generate(proseMirrorDocument); // 在React中渲染 <div dangerouslySetInnerHTML={{ __html: fullHtml }} />
3. 额外注意事项
- XSS风险:直接渲染原始HTML非常危险,必须确保
htmlContent的来源是完全可信的(比如仅后台可控或经过严格的HTML过滤)。 - 编辑器内预览:如果需要在Tiptap编辑器里直接预览HTML内容,可以给自定义节点添加React节点视图:
import { NodeViewWrapper } from '@tiptap/react'; export const CustomHTMLNodeView = ({ node }) => { return ( <NodeViewWrapper> <div dangerouslySetInnerHTML={{ __html: node.attrs.htmlContent }} /> </NodeViewWrapper> ); }; // 在CustomHTMLNode的定义里添加: addNodeView() { return CustomHTMLNodeView; }
内容的提问来源于stack exchange,提问作者Rápli András
相关产品推荐
相关产品推荐

