如何在聊天应用中优化使用Lexical作为输入组件?
针对你提到的消息列表渲染性能问题,不用每条消息都实例化完整Lexical编辑器,也不需要预存HTML,这里有几个成熟的优化方案:
1. 使用Lexical轻量只读渲染器
Lexical官方提供了针对只读场景的轻量渲染组件,不需要加载全套编辑插件和状态管理逻辑,只负责解析保存的Lexical JSON内容并渲染成对应DOM。
实现示例
可以封装一个极简的MessageRenderer组件,只加载必要的只读插件:
import { LexicalComposer } from '@lexical/react/LexicalComposer'; import { RichTextPlugin } from '@lexical/react/LexicalRichTextPlugin'; import { ContentEditable } from '@lexical/react/LexicalContentEditable'; function MessageRenderer({ lexicalJson }) { const initialConfig = { namespace: 'chat-message-renderer', editorState: lexicalJson, // 传入从数据库取出的Lexical JSON状态 editable: false, readOnly: true, // 仅保留解析富文本内容的必要插件,移除所有编辑相关插件(如UndoRedo、History) plugins: [ { plugin: RichTextPlugin, options: { contentEditable: <ContentEditable readOnly />, placeholder: null, }, }, // 按需添加格式插件(如ListPlugin、LinkPlugin),仅保留消息中用到的格式 ], }; return <LexicalComposer initialConfig={initialConfig} />; }
这个组件比完整编辑器实例轻量得多,同时复用Lexical的原生解析逻辑,既不会因为编辑器更新导致渲染失效,也能避免HTML渲染的XSS风险。
2. 结合虚拟滚动动态挂载
如果消息数量达到数百甚至上千条,虚拟滚动是必须的优化手段。通过react-window、react-virtualized这类库,只渲染视口内的消息项,并且仅当消息进入视口时才挂载MessageRenderer,离开视口时卸载。
核心思路
以react-window为例,列表项仅在进入可视区域时才渲染对应的Lexical只读组件:
import { FixedSizeList as List } from 'react-window'; function MessageList({ messages }) { const renderMessage = ({ index, style }) => { const message = messages[index]; return ( <div style={style}> <MessageRenderer lexicalJson={message.lexicalContent} /> </div> ); }; return ( <List height={600} itemCount={messages.length} itemSize={80} width="100%" > {renderMessage} </List> ); }
这种方式下,即使有上千条消息,实际渲染的Lexical实例只有视口内的十几条,完全不会有性能压力。
3. 简化保存的Lexical JSON结构
保存消息时,除了完整的Lexical状态,还可以生成一个简化版的JSON(移除编辑相关的元数据,比如选择状态、历史记录),这样渲染时解析更快、占用内存更少。
处理示例
在保存消息前,对编辑器状态做精简:
const editorState = editor.getEditorState(); const simplifiedJson = JSON.parse(JSON.stringify(editorState.toJSON(), (key, value) => { // 移除编辑相关的非内容字段 if (['selection', 'history', 'pendingEditorState'].includes(key)) { return undefined; } return value; })); // 将simplifiedJson存入数据库
渲染时直接使用这个简化后的JSON,进一步降低解析成本。
方案对比
- 若消息格式简单(纯文本、基础加粗/斜体),用轻量只读渲染器即可,成本最低。
- 若消息包含复杂富文本格式(列表、链接、图片),用带必要格式插件的只读组件,兼顾功能和性能。
- 若消息数量极大,必须结合虚拟滚动,彻底解决大量实例的性能问题。
绝对不推荐预存HTML:一方面Lexical的渲染逻辑可能随版本更新,旧HTML会出现样式或结构不一致;另一方面用户输入的内容若包含恶意HTML,直接渲染会有XSS风险,而Lexical从JSON渲染时会自动做安全过滤。
内容的提问来源于stack exchange,提问作者Andrew Rusinas

