如何将 Tiptap v2 升级至 v3

Editor

Tiptap v3 是一次包含重大更改的主要更新。本指南将帮助你将 Tiptap v2 项目升级到版本 3。

重大更改

包更改

  • 已移除 UMD 构建(现在使用 tsup)

某些包可能已被移除或重命名,因此你可能需要更新你的导入和依赖。以下是一些常见的更改:

表格相关软件包
- import Table from '@tiptap/extension-table'
- import TableRow from '@tiptap/extension-table-row'
- import TableCell from '@tiptap/extension-table-cell'
- import TableHeader from '@tiptap/extension-table-header'
+ import { Table, TableRow, TableCell, TableHeader } from '@tiptap/extension-table'

列表相关软件包
- import BulletList from '@tiptap/extension-bullet-list'
- import OrderedList from '@tiptap/extension-ordered-list'
- import ListItem from '@tiptap/extension-list-item'
- import ListKeymap from '@tiptap/extension-list-keymap'
- import TaskList from '@tiptap/extension-task-list'
- import TaskItem from '@tiptap/extension-task-item'
+ import { BulletList, OrderedList, ListItem, ListKeymap, TaskList, TaskItem } from '@tiptap/extension-list'

扩展
- import Focus from '@tiptap/extension-focus'
- import Placeholder from '@tiptap/extension-placeholder'
- import History from '@tiptap/extension-history'
- import Dropcursor from '@tiptap/extension-dropcursor'
- import Gapcursor from '@tiptap/extension-gapcursor'
- import CharacterCount from '@tiptap/extension-character-count'
+ import { Focus, Placeholder, UndoRedo, Dropcursor, Gapcursor, CharacterCount } from '@tiptap/extensions'

CollaborationCursor
- import CollaborationCursor from '@tiptap/extension-collaboration-cursor'
+ import CollaborationCaret from '@tiptap/extension-collaboration-caret'

Collaboration History
- import CollaborationHistory from '@tiptap-pro/extension-collaboration-history'
+ import Snapshot from '@tiptap-pro/extension-snapshot'

React Menus
- import { BubbleMenu, FloatingMenu } from '@tiptap/react'
+ import { BubbleMenu, FloatingMenu } from '@tiptap/react/menus'

Vue 3 菜单
- import { BubbleMenu, FloatingMenu } from '@tiptap/vue-3'
+ import { BubbleMenu, FloatingMenu } from '@tiptap/vue-3/menus'

Vue 2 菜单
- import { BubbleMenu, FloatingMenu } from '@tiptap/vue-2'
+ import { BubbleMenu, FloatingMenu } from '@tiptap/vue-2/menus'

菜单系统

所有悬浮元素的库由 Tippy.js 替换为 Floating UI。这影响以下包:

  • @tiptap/extension-bubble-menu
  • @tiptap/extension-drag-handle
  • @tiptap/extension-drag-handle-react
  • @tiptap/extension-drag-handle-vue-2
  • @tiptap/extension-drag-handle-vue-3
  • @tiptap/extension-floating-menu
  • @tiptap/extension-mention
  • @tiptap/suggestion
  • @tiptap-pro/extension-emoji

迁移示例:

// 迁前
import { BubbleMenu } from '@tiptap/react'

const 菜单 = <BubbleMenu tippyOptions={{ duration: 100 }}>{/* 菜单内容 */}</BubbleMenu>

// 迁后
import { BubbleMenu } from '@tiptap/react/menus'
import { offset } from '@floating-ui/dom'
const 菜单 = (
  <BubbleMenu
    options={{
      offset: 6,
      placement: 'top',
    }}
  >
    {/* 菜单内容 */}
  </BubbleMenu>
)
  • 新的依赖项:@floating-ui/dom。安装方式:
npm install @floating-ui/dom@^1.6.0

Make sure to uninstall tippy.js if you were using it, as it is no longer needed.

npm uninstall tippy.js

API Changes Text Style Updates

  • mergeNestedSpanStyles 现在默认为 true
  • 新增文本样式扩展:
    • font-size
    • background-color
    • line-height
  • 提供 TextStyleKit API,用于配置多个文本样式扩展

您可以使用新的 TextStyleKit 简化配置:

import { TextStyleKit } from '@tiptap/extension-text-style'

new Editor({
  extensions: [
    TextStyleKit.configure({
      backgroundColor: {
        types: ['textStyle'],
      },
      color: {
        types: ['textStyle'],
      },
      fontFamily: {
        types: ['textStyle'],
      },
      fontSize: {
        types: ['textStyle'],
      },
      lineHeight: {
        types: ['textStyle'],
      },
    }),
  ],
})

引入 shouldRerenderOnTransaction

@tiptap/react 中的 Editor 配置现在支持 shouldRerenderOnTransaction,用于控制组件是否重渲染。

在 3.0.0 之前,编辑器会在每次事务导致的状态变化时重新渲染,便于持续跟踪编辑器对象,但可能会影响性能。

此选项默认关闭。启用方法是在配置中添加:

shouldRerenderOnTransaction: true

升级后,依赖于编辑器状态的UI元素可能变得无响应。这时可以选择关闭此选项(可能影响性能),或通过手动追踪状态实现同步。例如:

function MyEditor() {
  const [selection, setSelection] = useState({ from: 0, to: 0 });

  const editor = useEditor({
    // 其他配置项
    onTransaction({ transaction }) {
      setSelection({
        from: transaction.selection.from,
        to: transaction.selection.to,
      });
    },
  });
}

也可以使用 useEditorState 提取整个状态或部分值:

const { currentSelection } = useEditorState({
  editor,
  selector: (snapshot) => {
    return { currentSelection: snapshot.editor.state.selection }
  },
})

这样可以明确提取和管理状态,从而确保UI逻辑的正确性。

命令(Command)变更

  • clearContentsetContent 现在在默认情况下会发出更新
  • setContent 的参数签名变为 (content, options)
  • insertContent 的行为调整为避免在开头拆分文本节点

NodeView 变更

NodeViewRendererProps 中的 getPos 函数现在可以返回 undefined。请确保代码正确处理此情况,例如:

// 之前
const pos = nodeViewProps.getPos()

// 之后
const pos = nodeViewProps.getPos()
if (pos !== undefined) {
  // 使用 pos
}

已移除功能

  • editor.getCharacterCount() 方法已被移除
  • considerAnyAsEmpty 选项已从占位符扩展中移除

新增功能

Extensions 软件包

新推出的 @tiptap/extensions 软件包整合了多项实用扩展:

导入方式将自动升级。

迁移示例:

- import CharacterCount from '@tiptap/extension-character-count'
+ import { CharacterCount } from '@tiptap/extensions'

服务器端渲染(SSR)

增强 SSR 支持,可在无需渲染的情况下在服务端运行编辑器:

const editor = new Editor({
  element: null, // 选择启用 SSR 模式
  content: {
    type: 'doc',
    content: [
      /* 内容省略 */
    ],
  },
  extensions: [
    /* 扩展列表 */
  ],
})

标记(Mark)视图支持

新增对 mark views 的支持,让您可以自定义标记在编辑器中的呈现方式。例如,用于渲染文本颜色或超链接的自定义UI。

支持 React 和 Vue 3:

// 纯 JavaScript
import { Mark } from '@tiptap/core'

Mark.create({
  // 其他选项...
  addMarkView() {
    return ({ mark, HTMLAttributes }) => {
      const dom = document.createElement('b')
      const contentDOM = document.createElement('span')

      dom.appendChild(contentDOM)

      return {
        dom,
        contentDOM,
      }
    }
  },
})

// React
import { Mark } from '@tiptap/core'
import { ReactMarkViewRenderer } from '@tiptap/react'

Mark.create({
  // 其他配置
  addMarkView() {
    return ReactMarkViewRenderer(YourComponent)
  },
})

// Vue 3
import { Mark } from '@tiptap/core'
import { VueMarkViewRenderer } from '@tiptap/vue-3'

Mark.create({
  addMarkView() {
    return VueMarkViewRenderer(VueComponent)
  },
})

其他改进

  • 新增删除事件追踪
  • HTML 解析提升,采用 happy-dom-without-node
  • 增强对节点和标记属性的验证
  • 提升事务处理性能

StarterKit 更新

StarterKit 现已默认包含以下扩展:

如果已单独安装这些扩展,可以将它们移除:

- import Link from '@tiptap/extension-link'
- import ListKeymap from '@tiptap/extension-list-keymap'
- import Underline from '@tiptap/extension-underline'

如果您正在使用禁用的扩展名,请确保在您的 StarterKit 配置中更新它们的名称:

const editor = new Editor({
  extensions: [
    StarterKit.configure({
-      history: false, // disable history
+      undoRedo: false // disable undo/redo (previously history)
    })
  ]
})

迁移步骤

  1. 更新依赖

    • 移除依赖 UMD 的代码
    • 安装 @floating-ui/dom(用于气泡和浮动菜单)
    • 将 Tiptap 包更新到 v3
  2. 更新菜单实现

    • 替换 tippyOptions 为 Floating UI 配置
    • 更新菜单导入路径,加入 /menus 后缀
  3. 审查 NodeView 使用

    • getPos 添加 undefined 检查
  4. 升级扩展

    • 按需迁移到 @tiptap/extensions
    • 移除冗余的 StarterKit 依赖
    • 调整文本样式配置
  5. 审查命令用法

    • 更新 setContent 调用以适配新签名
    • 检查 clearContent 在新版本中的行为(默认为发出更新)