---
title: "在应用中高效整合 Tiptap"
description: "了解如何在应用中高效整合 Tiptap 编辑器。文档中有更多信息！"
canonical_url: "https://tiptap.zhcndoc.com/guides/performance"
---

# 在应用中高效整合 Tiptap

了解如何在应用中高效整合 Tiptap 编辑器。文档中有更多信息！

> **Interactive demo:** [Book](https://embed.tiptap.dev/preview/Examples/Book)

Tiptap 是一个非常高效的编辑器（甚至能够编辑整本书！），当你遇到性能问题时，往往不是 Tiptap 本身的问题，而是你将其整合进应用中的方式。以下是一些确保你的编辑器平稳运行的提示。

## React Tiptap 编辑器整合

> **Interactive demo:** [Performance](https://embed.tiptap.dev/preview/Examples/Performance?hideSource=true)

在使用 Tiptap 和 React 时，最常见的性能问题是编辑器的重新渲染次数过于频繁。这可能由于以下几个原因：

- 当使用 `useEditor` 钩子时，默认情况下，它会在每次更改时重新渲染编辑器。因此，你应该将编辑器（以及依赖它的内容）隔离在一个单独的组件中，以防止不必要的重新渲染。
- 编辑器应该与不会影响它的渲染隔离。例如，如果你有一个不与编辑器交互的侧边栏，它应该放在一个单独的组件中。

幸运的是，解决这些问题的大部分方法都是一样的：将编辑器隔离在一个单独的组件中。下面是你可以这样做的一个示例：

做：将编辑器隔离在一个单独的组件中

```jsx
import { EditorContent, useEditor } from '@tiptap/react'

const TiptapEditor = () => {
  const editor = useEditor({
    extensions,
    content,
  })

  return (
    <>
      <EditorContent editor={editor} />
      {/* 依赖于编辑器实例的其他组件 */}
      <MenuComponent editor={editor} />
    </>
  )
}

export default TiptapEditor
```

不要：在与其他组件相同的组件中渲染编辑器

```jsx
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` 钩子允许你订阅编辑器状态的变化，并仅在必要时重新渲染。这可以帮助你防止编辑器及其组件的不必要重新渲染。

```tsx
import { useEditor, useEditorState } from '@tiptap/react'

function Component() {
  const editor = useEditor({
    extensions,
    content,
  })

  const editorState = useEditorState({
    editor,
    // 每当编辑器状态变化时，都会调用此函数
    selector: ({ editor }: { editor: Editor }) => ({
      // 只有当粗体或斜体状态变化时才会重新渲染
      isBold: editor.isActive('bold'),
      isItalic: editor.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` 选项获得更多的渲染控制。如果你想防止编辑器立即渲染或在每次事务时渲染，这可能会很有用。

```tsx
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 元素，或者采用其他方式在节点视图中渲染内容。

## 避免 React 中的 `flushSync` 警告

当在 React 中使用 Tiptap 时，你可能会在控制台中看到 `flushSync was called from inside a lifecycle method` 警告。

Tiptap 会同步分发事务。当你在 Tiptap 的 [回调或事件处理器](https://tiptap.zhcndoc.com/editor/api/events.md) 中直接更新 React 状态时，React 可能正处于渲染周期中，这会触发此警告，以防止 React 渲染期间的性能问题。

要修复此问题，请找到触发警告的代码，并将其包裹在 `queueMicrotask` 中。这样会安排更新在当前同步执行结束后立即运行，从而避免与 React 的渲染周期发生冲突。

```tsx
import { useState } from 'react'
import { useEditor } from '@tiptap/react'

function Component() {
  const [state, setState] = useState([])

  const editor = useEditor({
    onUpdate() {
      queueMicrotask(() => setState('new state'))
    },
  })

  // ...
}
```
