如何无需额外Lexical编辑器,从editorState解析HTML?
问题分析
你目前通过渲染辅助编辑器来转换editorState到HTML的方式确实属于过度设计——完全没必要加载额外的编辑器实例来做格式转换,Lexical本身提供了更轻量的转换方式。
改进方案
方案一:存储帖子时同时保存HTML(推荐)
在用户提交帖子的阶段,直接将editorState转换为HTML,和原始的JSON状态一起存入Postgres数据库。这样渲染帖子时直接读取HTML即可,完全不用在前端处理格式转换。
实现示例
前端提交前转换
import { createEditor, $generateHtmlFromNodes } from 'lexical'; // 导入你用到的所有自定义节点 import { HeadingNode, ListNode, ListItemNode, QuoteNode, CodeNode, CodeHighlightNode, TableNode, TableCellNode, TableRowNode, AutoLinkNode, LinkNode, } from './your-node-config'; // 提交帖子时调用 async function submitPost(content) { const editor = createEditor(); // 注册所有需要的节点 [ HeadingNode, ListNode, ListItemNode, QuoteNode, CodeNode, CodeHighlightNode, TableNode, TableCellNode, TableRowNode, AutoLinkNode, LinkNode, ].forEach(Node => editor.registerNode(Node)); // 转换editorState到HTML let html = ''; editor.update(() => { html = $generateHtmlFromNodes(editor, null); }); // 同时提交editorState JSON和HTML到后端 await api.post('/posts', { content: JSON.stringify(content), contentHtml: html, // 其他帖子字段... }); }
后端转换(更安全,避免前端环境差异)
如果用Node.js后端,直接用Lexical的API转换:
const { createEditor, $generateHtmlFromNodes } = require('lexical'); const { HeadingNode, ListNode, ListItemNode, QuoteNode, CodeNode, CodeHighlightNode, TableNode, TableCellNode, TableRowNode, AutoLinkNode, LinkNode, } = require('./your-node-config'); async function convertEditorStateToHtml(editorStateJson) { const editor = createEditor(); // 注册节点 [ HeadingNode, ListNode, ListItemNode, QuoteNode, CodeNode, CodeHighlightNode, TableNode, TableCellNode, TableRowNode, AutoLinkNode, LinkNode, ].forEach(Node => editor.registerNode(Node)); // 解析JSON到EditorState const editorState = editor.parseEditorState(editorStateJson); let html = ''; editor.update(() => { html = $generateHtmlFromNodes(editor, null); }); return html; } // 处理帖子提交的接口 app.post('/posts', async (req, res) => { const { content } = req.body; const contentHtml = await convertEditorStateToHtml(content); // 存入数据库:content(JSON字符串)和contentHtml await db.query('INSERT INTO posts (content, content_html) VALUES ($1, $2)', [content, contentHtml]); res.status(201).send('Post created'); });
方案二:前端离线转换editorState到HTML(无需渲染编辑器)
如果不想修改存储逻辑,可以在前端直接创建一个离线的Editor实例,不用挂载到DOM,直接转换editorState到HTML,去掉AuxEditor组件。
修改后的useGetHtmlFromState
import { useState, useEffect } from 'react'; import { createEditor, $generateHtmlFromNodes, EditorState } from 'lexical'; import exampleTheme from './your-theme'; // 导入所有需要的节点 import { HeadingNode, ListNode, ListItemNode, QuoteNode, CodeNode, CodeHighlightNode, TableNode, TableCellNode, TableRowNode, AutoLinkNode, LinkNode, } from './your-nodes'; export const useGetHtmlFromState = (state) => { const [html, setHtml] = useState(""); useEffect(() => { if (!state) return; const editor = createEditor({ namespace: "MyEditor", theme: exampleTheme, }); // 注册所有需要的节点 [ HeadingNode, ListNode, ListItemNode, QuoteNode, CodeNode, CodeHighlightNode, TableNode, TableCellNode, TableRowNode, AutoLinkNode, LinkNode, ].forEach(Node => editor.registerNode(Node)); // 设置editorState并转换为HTML editor.setEditorState(state); editor.update(() => { const generatedHtml = $generateHtmlFromNodes(editor, null); setHtml(generatedHtml); }); // 清理编辑器实例 return () => { editor.destroy(); }; }, [state]); return { html }; // 不再返回AuxEditor };
修改后的PostDetails组件
export const PostDetails = () => { const postSlug = useParam("postSlug", "string"); const [postDetails] = useQuery(getPostPageDetails, { slug: postSlug }); const { html } = useGetHtmlFromState(postDetails.content as unknown as EditorState); return ( <Text dangerouslySetInnerHTML={{ __html: html }} /> ); };
方案对比
- 方案一:优点是前端渲染零开销,性能最优;缺点是需要修改存储逻辑,若后续编辑器节点配置变更,需要批量更新历史帖子的HTML(可通过脚本处理)。
- 方案二:优点是无需修改后端和存储,前端直接处理;缺点是每次渲染都要创建编辑器实例,有一定性能开销,但比渲染辅助编辑器轻量很多。
内容的提问来源于stack exchange,提问作者reymon359
相关产品推荐
相关产品推荐

