探索 Tiptap V3 的最新功能

从 Editor.js 迁移到 Tiptap

Editor

如果你想替换 Editor.js 编辑器,Tiptap 是一个流行的替代方案。从 Editor.js 迁移到 Tiptap 非常简单。本指南将帮助你从 Editor.js 的基于块的结构平滑过渡到 Tiptap 的文档模型。

内容迁移

HTML 内容兼容性

Editor.js 使用基于块的 JSON 结构,需要转换为 HTML 以供 Tiptap 使用:

// 将 Editor.js 块转换为 HTML
function editorJsToHtml(editorJsData) {
  let html = ''

  editorJsData.blocks.forEach((block) => {
    switch (block.type) {
      case 'paragraph':
        html += `<p>${block.data.text}</p>`
        break
      case 'header':
        html += `<h${block.data.level}>${block.data.text}</h${block.data.level}>`
        break
      case 'list':
        const listType = block.data.style === 'ordered' ? 'ol' : 'ul'
        const items = block.data.items.map((item) => `<li>${item}</li>`).join('')
        html += `<${listType}>${items}</${listType}>`
        break
      case 'quote':
        html += `<blockquote><p>${block.data.text}</p></blockquote>`
        break
      case 'code':
        html += `<pre><code>${block.data.code}</code></pre>`
        break
      case 'image':
        html += `<img src="${block.data.file.url}" alt="${block.data.caption || ''}" />`
        break
      // 根据需要添加更多块类型
    }
  })

  return html
}

// 在 Tiptap 中使用转换后的 HTML
const htmlContent = editorJsToHtml(editorJsData)
const editor = new Editor({
  content: htmlContent,
  extensions: [StarterKit],
})

如果你已经有来自 Editor.js 的 HTML 输出,可以直接使用:

// 你已有的 Editor.js HTML 内容
const existingContent = '<p>Hello <strong>world</strong>!</p>'

// 直接在 Tiptap 中使用
const editor = new Editor({
  content: existingContent,
  extensions: [StarterKit],
})

虽然 HTML 直接使用没问题,但我们建议将其转换为 Tiptap 的 JSON 格式,以获得更好的性能和可读性。对于现有内容的批量转换,可使用 HTML 工具 程序化地将 HTML 转为 JSON。

编辑器设置

安装

首先安装 Tiptap 及其依赖:

npm install @tiptap/core @tiptap/starter-kit

Tiptap 支持所有现代前端 UI 框架,如 React 和 Vue。请参阅我们的 安装指南 以获取针对具体框架的安装说明。

基础编辑器设置

将你的 Editor.js 初始化替换为 Tiptap:

// Editor.js(之前)
import EditorJS from '@editorjs/editorjs'
import Header from '@editorjs/header'
import List from '@editorjs/list'
import Quote from '@editorjs/quote'
import Code from '@editorjs/code'
import Image from '@editorjs/image'

const editor = new EditorJS({
  holder: 'editorjs',
  tools: {
    header: Header,
    list: List,
    quote: Quote,
    code: Code,
    image: Image,
  },
})

// Tiptap(之后)
import { Editor } from '@tiptap/core'
import StarterKit from '@tiptap/starter-kit'

const editor = new Editor({
  element: document.querySelector('#editor'),
  extensions: [StarterKit],
  content: '<p>Hello World!</p>',
})

扩展

理解 Tiptap 的扩展系统

Tiptap 使用模块化扩展系统,区别于 Editor.js 的工具块方式。Tiptap 使用连续的文档模型,扩展提供格式化和功能支持。

StarterKit 是所有基础扩展的合集,你可以根据需要添加或删除其他扩展。

请查阅我们的 扩展指南 了解所有可用扩展,或者参考 自定义扩展 创建自己的扩展以支持自定义功能和 HTML 元素。

常用 Editor.js 工具与 Tiptap 扩展对应关系

Editor.js 工具Tiptap 扩展备注
@editorjs/paragraphParagraph包含于 StarterKit
@editorjs/headerHeading包含于 StarterKit
@editorjs/listBulletList, OrderedList, ListItem包含于 StarterKit
@editorjs/quoteBlockquote包含于 StarterKit
@editorjs/codeCodeBlock包含于 StarterKit
@editorjs/imageImage单独提供
@editorjs/tableTable单独提供
@editorjs/linkLink包含于 StarterKit
@editorjs/markerHighlight单独提供
@editorjs/delimiterHorizontalRule包含于 StarterKit

扩展配置

import { Editor } from '@tiptap/core'
import StarterKit from '@tiptap/starter-kit'
import Image from '@tiptap/extension-image'
import Table from '@tiptap/extension-table'
import TableRow from '@tiptap/extension-table-row'
import TableHeader from '@tiptap/extension-table-header'
import TableCell from '@tiptap/extension-table-cell'
import Highlight from '@tiptap/extension-highlight'

const editor = new Editor({
  extensions: [
    StarterKit,
    Image.configure({
      inline: true,
      allowBase64: true,
    }),
    Table.configure({
      resizable: true,
    }),
    TableRow,
    TableHeader,
    TableCell,
    Highlight.configure({
      multicolor: true,
    }),
  ],
})

自定义扩展

针对 Editor.js 的自定义工具,请创建对应的 Tiptap 自定义扩展。详细步骤请参阅我们的 自定义扩展指南

UI 实现

工具栏实现

可用 Tiptap 的工具栏组件复刻 Editor.js 的内联工具栏:

// Tiptap 工具栏(React 示例)
function Toolbar({ editor }) {
  if (!editor) return null

  return (
    <div className="toolbar">
      <button
        onClick={() => editor.chain().focus().toggleBold().run()}
        className={editor.isActive('bold') ? 'active' : ''}
      >
        Bold
      </button>
      <button
        onClick={() => editor.chain().focus().toggleItalic().run()}
        className={editor.isActive('italic') ? 'active' : ''}
      >
        Italic
      </button>
      <button
        onClick={() => editor.chain().focus().toggleUnderline().run()}
        className={editor.isActive('underline') ? 'active' : ''}
      >
        Underline
      </button>
    </div>
  )
}

预构建 UI 组件

为了加快开发速度,可以使用 Tiptap 预构建的 UI 组件:

内联工具栏(Editor.js 样式)

使用 Tiptap 的 BubbleMenu 复制 Editor.js 的内联工具栏:

import { BubbleMenu } from '@tiptap/react'

function MyEditor() {
  const editor = useEditor({
    extensions: [StarterKit],
  })

  return (
    <>
      <EditorContent editor={editor} />
      <BubbleMenu editor={editor}>
        <button
          onClick={() => editor.chain().focus().toggleBold().run()}
          className={editor.isActive('bold') ? 'active' : ''}
        >
          Bold
        </button>
        <button
          onClick={() => editor.chain().focus().toggleItalic().run()}
          className={editor.isActive('italic') ? 'active' : ''}
        >
          Italic
        </button>
      </BubbleMenu>
    </>
  )
}

基于块的 UI(替代方案)

如果你更喜欢 Editor.js 的基于块的方式,可使用我们的 块编辑器模板(敬请期待),开箱即可实现类似 Notion 的块状文本编辑器界面。

迁移清单

后续步骤