Tiptap(基于 Prosemirror)vs TinyMCE:哪个更适合你的技术栈?

在这之前,先来介绍一下 ProseMirror


1. ProseMirror 是底层内核

  • 定位:一个强大的 富文本编辑框架/引擎,不是一个成品编辑器。

  • 作者:Marijn Haverbeke(CodeMirror 作者)。

  • 核心思想

    • schema(模式) 定义文档结构(节点、marks、attributes)。
    • 每个操作(加粗、插入段落)都是 transaction(事务)
    • 提供 collab(协作)、history、markdown 解析、插件系统 等能力。
  • 问题:API 很底层、复杂(写一个“加粗按钮”就需要懂 state/transaction/command)。


2. Tiptap 是 ProseMirror 的高级封装

  • 定位:一个 基于 ProseMirror 的现代编辑器框架

  • 特点

    • 提供了更友好的 API(例如 editor.chain().focus().toggleBold().run(),不用直接操作 transaction)。
    • 内置常用扩展(StarterKit:paragraph、heading、bold、italic、list、code 等)。
    • 框架无关(React、Vue、Svelte、纯 JS 都能用)。
    • 社区活跃,扩展丰富(Mention、SlashCommand、Table、Collaboration 等)。
    • 专门为现代 Web 应用优化(支持移动端、协同编辑、富 UI 集成)。

3. 核心区别

对比点ProseMirrorTiptap
定位底层引擎 / 框架高级封装 / 开发者友好的框架
难度高(API 底层,文档晦涩)中等(链式调用,扩展丰富)
功能一切都要自己实现内置 StarterKit,常用功能即插即用
UI没有(自己写)没有默认 UI,但社区有现成扩展
灵活性无限灵活灵活 + 更高生产力
适合人群想深度定制、写自己编辑器内核的人想快速落地 Notion/Google Docs 类应用的人

4. 关系总结

  • ProseMirror = 编辑器的内核(底层引擎)。
  • Tiptap = 基于 ProseMirror 的开发框架(更好用的外壳)。
  • 你用 Tiptap,其实是在用 ProseMirror,只不过被封装了一层,更易上手。
  • 如果你遇到 Tiptap 没提供的功能,最终可能需要写 ProseMirror 插件/扩展

  • ProseMirror vs Tiptap 的架构图:

 **ProseMirror vs Tiptap 的架构图**


下面,回归正题:

一、本文将对比 Tiptap EditorTinyMCE,从技术架构、功能、扩展性、适用场景等方面做出分析。

1. 技术架构

  • Tiptap Editor

    • 基于 ProseMirror,是一个现代化的富文本编辑框架。
    • 完全用 JavaScript/TypeScript 编写,UI 无关(React、Vue、Svelte 都能集成)。
    • 更偏向“编辑器框架”,需要开发者配置和扩展。
  • TinyMCE

    • 历史悠久的传统富文本编辑器(WYSIWYG),早期就用于网页中的文字编辑。
    • 内置 UI(工具栏、菜单)和功能较多,开箱即用。
    • 偏向于“现成的产品”,即插即用。

2. 功能特性

  • Tiptap

    • 灵活度高,可以完全自定义编辑体验(例如 Notion、Slack、Linear 都是基于 ProseMirror/Tiptap)。
    • 支持协同编辑(结合 yjs/y-websocket)。
    • 插件体系强大(markdown、mention、slash command、自定义节点)。
    • 原生支持移动端、现代前端框架。
    • UI/菜单需要自己实现或用社区扩展。
  • TinyMCE

    • 开箱即用的传统功能:字体、颜色、表格、图片上传、对齐、列表等等。
    • 插件体系完善,但更多是 WYSIWYG 的扩展(如 word count、spellcheck)。
    • 有商业版(带更多企业功能,如 MS Word/Google Docs 级别的协作和导出)。
    • 移动端体验不如 Tiptap,但兼容性很好。

3. 学习曲线

  • Tiptap:需要较强的前端开发能力,理解 ProseMirror schema、node/mark 才能发挥最大价值。
  • TinyMCE:学习成本低,只要引入脚本即可用,配置主要是工具栏和插件。

4. 可扩展性 & 自定义

  • Tiptap

    • 高度可定制,可以实现类似 Notion、Coda、Obsidian 那种 block-based 编辑器。
    • 自定义节点(自定义组件、Vue/React 元素嵌入)非常灵活。
    • 对现代 web app(SaaS、在线文档)很适合。
  • TinyMCE

    • 自定义能力有限,虽然可以写插件,但核心思想还是“富文本编辑器”。
    • 适合标准化的富文本场景(CMS、论坛、评论系统)。

5. 使用场景

  • 选择 Tiptap

    • 你在做一个 现代 SaaS/协作工具(比如 Notion、文档协作、知识库)。
    • 需要 协作编辑自定义 block/组件
    • 前端团队实力较强,可以投入时间定制。
  • 选择 TinyMCE

    • 你在做 CMS、表单、企业后台,只需要 标准富文本(发文章、加粗、插入表格图片)。
    • 团队希望 快速上线,不想花时间定制编辑器。
    • 用户群体对“Word 类似体验”有需求。

6. 对比总结

特性Tiptap Editor (ProseMirror)TinyMCE
定位编辑器框架 (现代、灵活)成品富文本编辑器 (传统、开箱即用)
UI/工具栏需要自己做 / 社区扩展内置完善工具栏
功能扩展无限灵活,可嵌入 React/Vue 组件主要是 WYSIWYG 扩展
协同编辑支持 (yjs 集成)商业版支持
移动端体验优秀一般
学习成本高(要理解 ProseMirror)低(配置工具栏即可)
最佳适用场景Notion/知识库/协作编辑CMS/后台文章/传统富文本

二、 下面将提供 Tiptap (Vue 3)TinyMCE (纯 HTML/JS) 的最小可运行示例


1. Tiptap (Vue 3 示例)

<template>
  <div>
    <EditorContent :editor="editor" class="border p-3 min-h-[200px]" />
  </div>
</template>

<script setup>
import { ref, onBeforeUnmount } from "vue"
import { EditorContent, useEditor } from "@tiptap/vue-3"
import StarterKit from "@tiptap/starter-kit"

const editor = useEditor({
  extensions: [StarterKit],
  content: "<p>Hello <b>Tiptap</b> 🚀</p>",
})

onBeforeUnmount(() => {
  editor?.destroy()
})
</script>

<style>
.ProseMirror {
  min-height: 200px;
  outline: none;
}
</style>

👉 特点:

  • 这是最小示例,只带 StarterKit(加粗、斜体、标题、列表、代码等基础功能)。
  • 你可以往里加插件,比如 mention、slash command、表格、Markdown。
  • 没有默认工具栏,要自己写按钮控制(高度可定制)。

2. TinyMCE (纯 HTML/JS 示例)

<!DOCTYPE html>
<html>
<head>
  <script src="https://cdn.tiny.cloud/1/no-api-key/tinymce/6/tinymce.min.js"></script>
</head>
<body>
  <textarea id="editor">Hello <b>TinyMCE</b></textarea>

  <script>
    tinymce.init({
      selector: '#editor',
      height: 300,
      plugins: 'lists link image table code',
      toolbar: 'undo redo | bold italic underline | bullist numlist | link image table | code',
    });
  </script>
</body>
</html>

👉 特点:

  • 一引入就能用,带完整工具栏。
  • 插件和配置非常像传统富文本(Word)。
  • 如果要集成到 Vue/React,也有官方包,但核心就是 tinymce.init

📌 总结:

  • Tiptap → 需要开发者写 UI,但能做出 Notion/Slack/协作文档 的感觉。
  • TinyMCE → 一步到位,像 Word 编辑器,但定制自由度没那么高。

三、下面示例将完善一下工具栏

Tiptap (Vue 3 + 工具栏 示例)

<template>
  <div class="editor">
    <!-- 工具栏 -->
    <div class="toolbar">
      <button @click="toggleBold" :class="{ active: editor.isActive('bold') }">B</button>
      <button @click="toggleItalic" :class="{ active: editor.isActive('italic') }">I</button>
      <button @click="setHeading(1)" :class="{ active: editor.isActive('heading', { level: 1 }) }">H1</button>
      <button @click="setHeading(2)" :class="{ active: editor.isActive('heading', { level: 2 }) }">H2</button>
      <button @click="toggleBulletList" :class="{ active: editor.isActive('bulletList') }">• List</button>
      <button @click="toggleOrderedList" :class="{ active: editor.isActive('orderedList') }">1. List</button>
      <button @click="toggleCodeBlock" :class="{ active: editor.isActive('codeBlock') }">Code</button>
    </div>

    <!-- 编辑区 -->
    <EditorContent :editor="editor" class="editor-content" />
  </div>
</template>

<script setup>
import { onBeforeUnmount } from "vue"
import { EditorContent, useEditor } from "@tiptap/vue-3"
import StarterKit from "@tiptap/starter-kit"

const editor = useEditor({
  extensions: [StarterKit],
  content: "<p>Hello <b>Tiptap</b> with Toolbar 🚀</p>",
})

onBeforeUnmount(() => {
  editor?.destroy()
})

// 工具栏方法
const toggleBold = () => editor.chain().focus().toggleBold().run()
const toggleItalic = () => editor.chain().focus().toggleItalic().run()
const setHeading = (level) => editor.chain().focus().toggleHeading({ level }).run()
const toggleBulletList = () => editor.chain().focus().toggleBulletList().run()
const toggleOrderedList = () => editor.chain().focus().toggleOrderedList().run()
const toggleCodeBlock = () => editor.chain().focus().toggleCodeBlock().run()
</script>

<style>
.editor {
  border: 1px solid #ccc;
  border-radius: 6px;
  padding: 8px;
  max-width: 600px;
  margin: auto;
}

.toolbar {
  border-bottom: 1px solid #ddd;
  padding-bottom: 6px;
  margin-bottom: 6px;
}

.toolbar button {
  margin-right: 6px;
  padding: 4px 8px;
  border: 1px solid #ccc;
  background: white;
  cursor: pointer;
  border-radius: 4px;
}

.toolbar button.active {
  background: #007bff;
  color: white;
}

.editor-content {
  min-height: 200px;
  padding: 6px;
  outline: none;
}
</style>

👉 这样效果就是:

  • 上面一排按钮(加粗、斜体、H1、H2、列表、代码块)。
  • 点击按钮就能直接控制编辑区。
  • 你可以继续扩展,比如 插入图片、mention、slash command,灵活度很高。

  • React + Tiptap + 工具栏示例,方便和 Vue 版本对比。

Tiptap (React + 工具栏 示例)

import React, { useEffect } from "react"
import { EditorContent, useEditor } from "@tiptap/react"
import StarterKit from "@tiptap/starter-kit"
import "./editor.css"  // 样式写在单独的 css 文件

const TiptapEditor = () => {
  const editor = useEditor({
    extensions: [StarterKit],
    content: "<p>Hello <b>Tiptap</b> with Toolbar 🚀</p>",
  })

  useEffect(() => {
    return () => editor?.destroy()
  }, [editor])

  if (!editor) return null

  return (
    <div className="editor">
      {/* 工具栏 */}
      <div className="toolbar">
        <button onClick={() => editor.chain().focus().toggleBold().run()}
                className={editor.isActive("bold") ? "active" : ""}>
          B
        </button>
        <button onClick={() => editor.chain().focus().toggleItalic().run()}
                className={editor.isActive("italic") ? "active" : ""}>
          I
        </button>
        <button onClick={() => editor.chain().focus().toggleHeading({ level: 1 }).run()}
                className={editor.isActive("heading", { level: 1 }) ? "active" : ""}>
          H1
        </button>
        <button onClick={() => editor.chain().focus().toggleHeading({ level: 2 }).run()}
                className={editor.isActive("heading", { level: 2 }) ? "active" : ""}>
          H2
        </button>
        <button onClick={() => editor.chain().focus().toggleBulletList().run()}
                className={editor.isActive("bulletList") ? "active" : ""}>
          • List
        </button>
        <button onClick={() => editor.chain().focus().toggleOrderedList().run()}
                className={editor.isActive("orderedList") ? "active" : ""}>
          1. List
        </button>
        <button onClick={() => editor.chain().focus().toggleCodeBlock().run()}
                className={editor.isActive("codeBlock") ? "active" : ""}>
          Code
        </button>
      </div>

      {/* 编辑区 */}
      <EditorContent editor={editor} className="editor-content" />
    </div>
  )
}

export default TiptapEditor

样式 (editor.css)

.editor {
  border: 1px solid #ccc;
  border-radius: 6px;
  padding: 8px;
  max-width: 600px;
  margin: auto;
}

.toolbar {
  border-bottom: 1px solid #ddd;
  padding-bottom: 6px;
  margin-bottom: 6px;
}

.toolbar button {
  margin-right: 6px;
  padding: 4px 8px;
  border: 1px solid #ccc;
  background: white;
  cursor: pointer;
  border-radius: 4px;
}

.toolbar button.active {
  background: #007bff;
  color: white;
}

.editor-content {
  min-height: 200px;
  padding: 6px;
  outline: none;
}

👉 这样你就有了:

  • Vue 版 Tiptap + 工具栏
  • React 版 Tiptap + 工具栏
  • TinyMCE 对比版

📌 对比下来:

  • TinyMCE:自带工具栏,不需要写 UI。
  • Tiptap:工具栏要自己写,但能完全定制,甚至做出 Notion 风格。

  • 继续,为 Tiptap 扩展一个「插入图片」功能,这样你能直接对比 TinyMCE 的图片功能。

    给出 React 示例(Vue 逻辑差不多,换 @tiptap/vue-3 即可)。


  • 补充一个图片上传功能

React + Tiptap + 工具栏 + 插入图片

import React, { useEffect } from "react"
import { EditorContent, useEditor } from "@tiptap/react"
import StarterKit from "@tiptap/starter-kit"
import Image from "@tiptap/extension-image"
import "./editor.css"

const TiptapEditor = () => {
  const editor = useEditor({
    extensions: [
      StarterKit,
      Image, // 启用图片扩展
    ],
    content: "<p>Hello <b>Tiptap</b> with Image 🚀</p>",
  })

  useEffect(() => {
    return () => editor?.destroy()
  }, [editor])

  if (!editor) return null

  // 插入图片
  const addImage = () => {
    const url = window.prompt("请输入图片地址")
    if (url) {
      editor.chain().focus().setImage({ src: url }).run()
    }
  }

  return (
    <div className="editor">
      {/* 工具栏 */}
      <div className="toolbar">
        <button onClick={() => editor.chain().focus().toggleBold().run()}
                className={editor.isActive("bold") ? "active" : ""}>B</button>
        <button onClick={() => editor.chain().focus().toggleItalic().run()}
                className={editor.isActive("italic") ? "active" : ""}>I</button>
        <button onClick={() => editor.chain().focus().toggleHeading({ level: 1 }).run()}
                className={editor.isActive("heading", { level: 1 }) ? "active" : ""}>H1</button>
        <button onClick={() => editor.chain().focus().toggleHeading({ level: 2 }).run()}
                className={editor.isActive("heading", { level: 2 }) ? "active" : ""}>H2</button>
        <button onClick={() => editor.chain().focus().toggleBulletList().run()}
                className={editor.isActive("bulletList") ? "active" : ""}>• List</button>
        <button onClick={() => editor.chain().focus().toggleOrderedList().run()}
                className={editor.isActive("orderedList") ? "active" : ""}>1. List</button>
        <button onClick={() => editor.chain().focus().toggleCodeBlock().run()}
                className={editor.isActive("codeBlock") ? "active" : ""}>Code</button>
        <button onClick={addImage}>🖼 插入图片</button>
      </div>

      {/* 编辑区 */}
      <EditorContent editor={editor} className="editor-content" />
    </div>
  )
}

export default TiptapEditor

样式 (editor.css)

.editor {
  border: 1px solid #ccc;
  border-radius: 6px;
  padding: 8px;
  max-width: 600px;
  margin: auto;
}

.toolbar {
  border-bottom: 1px solid #ddd;
  padding-bottom: 6px;
  margin-bottom: 6px;
}

.toolbar button {
  margin-right: 6px;
  padding: 4px 8px;
  border: 1px solid #ccc;
  background: white;
  cursor: pointer;
  border-radius: 4px;
}

.toolbar button.active {
  background: #007bff;
  color: white;
}

.editor-content {
  min-height: 200px;
  padding: 6px;
  outline: none;
}

.editor-content img {
  max-width: 100%;
  height: auto;
  border-radius: 4px;
  margin: 8px 0;
}

👉 效果:

  • 点击「🖼 插入图片」会弹出一个输入框,输入 URL 就能插图。
  • 图片支持缩放、响应式。
  • 如果要扩展成 上传图片到服务器再插入,只要改 addImage 方法,走接口拿到 urlsetImage 即可。

👉 总结一句话:

  • 想做 Notion/Google Docs 类现代应用 → 用 Tiptap
  • 想做 CMS/传统后台表单 → 用 TinyMCE

### 关于 Element Plus 中富文本编辑器的使用教程 Element Plus 是 Vue 3 的官方组件库之一,提供了丰富的 UI 组件支持。然而需要注意的是,Element Plus 官方并未直接内置富文本编辑器功能[^1]。因此,在实际开发中通常会通过集成第三方富文本编辑器来满足需求。 #### 基于 Element Tiptap 的解决方案 Element Tiptap 结合了 Tiptap 和 Element Plus,是一种专门为 Vue 3 设计的富文本编辑器方案。它具有高度自定义性和良好的用户体验。以下是其基本使用流程: 1. **安装依赖** 需要先安装 `@tiptap/vue-3` 和其他必要的扩展包。 ```bash npm install @tiptap/vue-3 prosemirror-schema-basic ``` 2. **引入并初始化** 下面是一个简单的示例代码展示如何在 Vue 3 项目中使用 Element Tiptap: ```vue <template> <element-tiptap v-model="content" /> </template> <script setup> import { ref } from 'vue'; import ElementTiptap from '@epicmaxco/element-tiptap'; const content = ref('<p>Hello, world!</p>'); </script> ``` 上述代码展示了如何绑定数据模型以及渲染基础内容。 --- #### WangEditor 的集成方式 WangEditor 是国内广泛使用的开源富文本编辑器,同样可以很好地与 Vue 3 及 Element Plus 进行集成。具体步骤如下: 1. **安装 WangEditor** 使用 npm 或 yarn 安装 WangEditor: ```bash npm install wangeditor --save ``` 2. **创建编辑器实例** 在 Vue 3 项目中可以通过脚本动态加载 WangEditor 并完成初始化操作。 ```javascript import E from 'wangeditor'; export default { data() { return { editorContent: '' }; }, mounted() { const editor = new E('#editor'); editor.config.onchange = (newHtml) => { this.editorContent = newHtml; }; editor.create(); } }; ``` 此外还可以根据业务需求进一步定制工具栏选项或其他高级特性[^2][^3]。 --- #### TinyMCE 的 Script Setup 实现 TinyMCE 提供了一个强大的在线富文本编辑体验,并且能够轻松嵌入到基于 Composition API 构建的应用程序里。下面给出一段典型例子用于演示设置过程: ```html <template> <textarea id="my-editor"></textarea> </template> <script setup lang="ts"> import tinymce from 'tinymce/tinymce'; // 引入主文件 import 'tinymce/themes/silver/theme'; // 主题样式 import 'tinymce/icons/default'; // 图标字体 const initEditor = () => { tinymce.init({ selector: '#my-editor', plugins: ['link image code'], toolbar: 'undo redo | link image | code' }); }; onMounted(() => { initEditor(); }); </script> ``` 上述片段重点在于正确配置插件列表和按钮组布局等内容[^4]。 --- ### 总结对比分析 | 编辑器名称 | 易用程度 | 功能全面性 | 社区活跃度 | |--------------------|----------|---------------------|------------| | Element Tiptap | ★★★★☆ | ★★★★☆ | ★★★☆☆ | | WangEditor | ★★★★★ | ★★★★★ | ★★★★☆ | | TinyMCE | ★★★☆☆ | ★★★★★ | ★★★★★ | 每种选择都有各自的优势领域,请依据项目的实际情况挑选最适合技术栈
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

不老刘

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值