在应用中高效整合 Tiptap
Tiptap 是一个非常高效的编辑器(甚至能够编辑整本书!),当你遇到性能问题时,往往不是 Tiptap 本身的问题,而是你将其整合进应用中的方式。以下是一些确保你的编辑器平稳运行的提示。
React Tiptap 编辑器整合
在使用 Tiptap 和 React 时,最常见的性能问题是编辑器的重新渲染次数过于频繁。这可能由于以下几个原因:
- 当使用
useEditor钩子时,默认情况下,它会在每次更改时重新渲染编辑器。因此,你应该将编辑器(以及依赖它的内容)隔离在一个单独的组件中,以防止不必要的重新渲染。 - 编辑器应该与不会影响它的渲染隔离。例如,如果你有一个不与编辑器交互的侧边栏,它应该放在一个单独的组件中。
幸运的是,解决这些问题的大部分方法都是一样的:将编辑器隔离在一个单独的组件中。下面是你可以这样做的一个示例:
做:将编辑器隔离在一个单独的组件中
import { EditorContent, useEditor } from '@tiptap/react'
const TiptapEditor = () => {
const editor = useEditor({
extensions,
content,
})
return (
<>
<EditorContent editor={editor} />
{/* 依赖于编辑器实例的其他组件 */}
<MenuComponent editor={editor} />
</>
)
}
export default TiptapEditor不要:在与其他组件相同的组件中渲染编辑器
import { EditorContent, useEditor } from '@tiptap/react'
const App = () => {
const [sidebarOpen, setSidebarOpen] = React.useState(false)
const editor = useEditor({
extensions,
content,
})
return (
<>
<UnrelatedSidebar onChange={setSidebarOpen} />
<EditorContent editor={editor} />
<MenuComponent editor={editor} />
<Sidenav isSidebarOpen={sidebarOpen}>
<AnotherComponent />
</Sidenav>
</>
)
}
export default App这些无关的组件会导致编辑器比必要时更频繁地重新渲染,并使每次渲染的开销更大。
追踪性能问题
你可以使用 React DevTools Profiler 来查看哪些组件正在重新渲染以及原因。另一种策略是在编辑器组件中放置一个 console.count('editor render'),查看它的重新渲染次数。这可以帮助你识别哪些组件导致了不必要的重新渲染。
如果它的重新渲染次数超过了你的预期,你可以采取以下步骤:
- 检查编辑器是否因为其父组件而重新渲染。
- 将编辑器与无关的状态变化隔离(例如,打开侧边栏不应导致编辑器重新渲染)。
- 使用
useEditorState来防止编辑器组件内部的不必要重新渲染。
希望这些提示能帮助你追踪和修复遇到的任何性能问题。
使用 useEditorState 防止不必要的重新渲染
useEditorState 钩子允许你订阅编辑器状态的变化,并仅在必要时重新渲染。这可以帮助你防止编辑器及其组件的不必要重新渲染。
import { useEditor, useEditorState } from '@tiptap/react'
function Component() {
const editor = useEditor({
extensions,
content,
})
const editorState = useEditorState({
editor,
// 每当编辑器状态变化时,都会调用此函数
selector: ({ editor }: { editor: Editor }) => ({
// 仅在粗体或斜体状态变化时重新渲染
isBold: editorInstance.isActive('bold'),
isItalic: editorInstance.isActive('italic'),
}),
})
return (
<>
<EditorContent editor={editor} />
<button
onClick={() => editor.chain().focus().toggleBold().run()}
className={editorState.isBold ? 'primary' : ''}
>
粗体
</button>
<button
onClick={() => editor.chain().focus().toggleItalic().run()}
className={editorState.isItalic ? 'primary' : ''}
>
斜体
</button>
</>
)
}selector 函数允许你指定想要订阅的编辑器状态的哪些部分。默认情况下,这将与之前选定的状态进行深度比较,并仅在其变化时重新渲染。你可以选择编辑器状态的任何部分,或甚至从中派生出新值。
获取更多渲染控制
从 Tiptap v2.5.0 开始,你可以通过使用 immediatelyRender 和 shouldRerenderOnTransaction 选项获得更多的渲染控制。如果你想防止编辑器立即渲染或在每次事务时渲染,这可能会很有用。
import { useEditor } from '@tiptap/react'
function Component() {
const editor = useEditor({
extensions,
content,
/**
* 此选项使我们能够控制启用编辑器立即渲染的默认行为。
*/
immediatelyRender: true,
/**
* 此选项使我们能够控制禁用在每次事务期间重新渲染编辑器的默认行为。
*/
shouldRerenderOnTransaction: false,
})
return <EditorContent editor={editor} />
}React 节点视图性能
节点视图允许你在编辑器中替换节点渲染自定义组件。这使你能够在编辑器中嵌入任何类型的内容。但是,在使用 React 组件时,要注意可能的性能影响。
出于技术原因,节点视图预计会同步渲染。Tiptap 会为每个节点视图创建新元素并在其中挂载你的 React 组件。这可能会很昂贵,特别是如果你在编辑器中有多个节点视图的实例。
我们尽可能地在我们的端进行优化,但是如果你发现渲染节点视图会造成性能问题,可以考虑使用普通的 HTML 元素或其他方法在你的节点视图中渲染内容。