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 nullreturn (<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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/web/93482.shtml
繁体地址,请注明出处:http://hk.pswp.cn/web/93482.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

多墨智能-AI一键生成工作文档/流程图/思维导图

本文转载自&#xff1a;多墨智能-AI一键生成工作文档/流程图/思维导图 - Hello123工具导航 ** 一、AI 文档与视觉化创作助手 多墨智能是一款基于人工智能的在线工具&#xff0c;支持一键生成专业文档、流程图与思维导图&#xff0c;通过关键词输入快速完成内容创作&#xff0…

Kafka_Broker_副本基本信息

Kafka副本作用&#xff1a;提高数据可靠性 Kafka默认副本1个&#xff0c;生产环境一般配置为2个&#xff0c;保证数据可靠性&#xff0c;太多副本会增加磁盘存储空间&#xff0c;增加网络上数据传输&#xff0c;降低效率 Kafka中副本分为&#xff1a;Leader和Follower&#xff…

FreeRTOS 中的守护任务(Daemon Task)

在 FreeRTOS 中&#xff0c;守护任务&#xff08;Daemon Task&#xff09;是一个特殊的系统任务&#xff0c;主要用于管理软件定时器和其他后台操作。以下是关于 FreeRTOS 守护任务的详细信息&#xff1a; 守护任务的作用软件定时器管理&#xff1a; 当启用 configUSE_TIMERS 时…

博士招生 | 麻省理工学院 招收化学+人工智能方向 博士/博士后

内容源自“图灵学术博研社”gongzhonghao学校简介麻省理工学院&#xff08;MIT&#xff09;QS世界排名第1&#xff0c;是全球科技研究领域的顶尖学府。自成立以来&#xff0c;MIT以其卓越的科研和教育质量赢得了世界的尊敬。学校在科学、工程、经济和管理等多个领域具有深远的影…

云计算-OpenStack 实战运维:从组件配置到故障排查(含 RAID、模板、存储管理,网络、存储、镜像、容器等)

介绍 在云计算技术快速发展的背景下,OpenStack 作为开源的云计算管理平台,凭借其灵活性、可扩展性和强大的组件生态,成为构建私有云、公有云和混合云的重要选择。无论是云主机的创建与管理、存储方案的配置(如 RAID 阵列、Swift 对象存储、Cinder 块存储),还是网络编排、…

idea代码bug检测插件

代码检测工具&#xff08;插件&#xff09;推荐&#xff1a;Alibaba Java Coding Guidelines、CheckStyle、PMD、FindBugs、SonarLint。可以在idea中安装插件 让你在关注代码质量的同时&#xff0c;减少 code review 的工作量&#xff0c;提高 code review 的效率&#xff0c;…

Java String为什么要设计成不可变的?

大家好&#xff0c;我是锋哥。今天分享关于【Java String为什么要设计成不可变的?】面试题。希望对大家有帮助&#xff1b; Java String为什么要设计成不可变的? 超硬核AI学习资料&#xff0c;现在永久免费了&#xff01; Java中的String类被设计为不可变&#xff08;immut…

集成电路学习:什么是ORB方向性FAST和旋转BRIEF

ORB:方向性FAST和旋转BRIEF ORB(Oriented FAST and Rotated BRIEF)是一种在计算机视觉领域广泛应用的特征描述算法,它结合了FAST角点检测算法和BRIEF描述子算法的优点,以实现高效且具有旋转不变性的特征提取和匹配。以下是关于ORB算法的详细解析: 一、ORB算法概述 …

【langgraph基础入门】

1. LangGraph图结构概念说明在以图构建的框架中&#xff0c;任何可执行的功能都可以作为对话、代理或程序的启动点。这个启动点可以是大模型的 API 接口、基于大模型构建的 AI Agent&#xff0c;通过 LangChain 或其他技术建立的线性序列等等&#xff0c;即下图中的 “Start” …

[逆向知识] AST抽象语法树:混淆与反混淆的逻辑互换(一)

博客配套代码发布于github&#xff1a;半自动化cookie更新&#xff08;欢迎顺手Star一下⭐&#xff09; 相关逆向知识&#xff1a; [逆向知识] AST抽象语法树&#xff1a;混淆与反混淆的逻辑互换&#xff08;二&#xff09;-CSDN博客 相关爬虫专栏&#xff1a;JS逆向爬虫实战…

网络安全合规6--服务器安全检测和防御技术

一、服务器安全风险主要威胁&#xff1a;不必要的服务暴露&#xff08;如仅需HTTP却开放多余端口&#xff09;。外网扫描&#xff08;IP/端口扫描&#xff09;、DDoS攻击。系统漏洞攻击&#xff08;操作系统、软件版本已知漏洞&#xff09;。Web攻击&#xff08;SQL注入、XSS、…

Mutually aided uncertainty

cycle loss calculation in order to regularize the two aux-decoders 辅助信息 作者未提供代码

go基础学习笔记

思维导图变量 声明形式为var 变量名 变量类型 赋值形式为变量名变量值 声明和赋值同时形式为变量名:变量值 多个变量同时声明使用形式为 var (x intb bool )当有多个变量类型一样时&#xff0c;可以放在一行&#xff0c;形式为var x,y int,当类型一样&#xff0c;并且需要赋值同…

C++析构函数和线程退出1

线程作为程序在操作系统中的执行单元&#xff0c;它是活动对象&#xff0c;有生命周期状态&#xff0c;它是有始有终的。有启动就有结束&#xff0c;在上篇文章中讨论了线程作为数据成员启动时的顺序问题&#xff0c;如何避免构造函数在初始化对象时对线程启动的负面影响&#…

【语法】JSON格式与基础语法

文章目录JSON 简介JSON 语法规则JSON 名称/值对JSON 值类型JSON文件存储JSON示例数据示例Python解析JSON代码JSON 简介 JSON 语法是 JavaScript 语法的子集。JSON 是存储和交换文本信息的语法。JSON: JavaScript Object Notation(JavaScript 对象表示法)。 JSON 语法规则 数…

GitHub 热榜项目 - 日榜(2025-08-16)

GitHub 热榜项目 - 日榜(2025-08-16) 生成于&#xff1a;2025-08-16 统计摘要 共发现热门项目&#xff1a;13 个 榜单类型&#xff1a;日榜 本期热点趋势总结 本期GitHub热榜呈现三大技术热点&#xff1a;1) AI应用深入垂直领域&#xff0c;SpatialLM将大语言模型应用于空间…

什么是EDA(Exploratory Data Analysis,探索性数据分析)

EDA&#xff08;Exploratory Data Analysis&#xff0c;探索性数据分析&#xff09;是一种在正式建模前&#xff0c;通过统计量和可视化方法来理解数据特征、发现模式与异常、并提出假设的过程。 这张图里你会看到&#xff1a; 直方图&#xff1a;展示单变量的分布&#xff0c;…

计算机毕业设计java的小天鹅酒店月子会所管理小天鹅酒店母婴护理中心管理系统设计小天鹅酒店产后护理会所信息化管理平台

计算机毕业设计java的小天鹅酒店月子会所管理9zl079&#xff08;配套有源码 程序 mysql数据库 论文&#xff09; 本套源码可以在文本联xi,先看具体系统功能演示视频领取&#xff0c;可分享源码参考。在当今数字化时代&#xff0c;随着人们对产后护理需求的不断增加&#xff0c;…

Docker-14.项目部署-DockerCompose

一.DockerCompose大家可以看到&#xff0c;我们部署一个简单的java项目&#xff0c;其中包含3个容器&#xff1a;MySQLNginxJava项目而稍微复杂的项目&#xff0c;其中还会有各种各样的其它中间件&#xff0c;需要部署的东西远不止3个。如果还像之前那样手动的逐一部署&#xf…

Vue组件基础解析

一、组件的核心意义 组件是Vue中实现UI复用与逻辑封装的基础单元,能将复杂UI拆分为独立、可重用的部分,最终组织成嵌套的树状结构(类似HTML元素嵌套)。Vue组件模型支持自定义内容与逻辑封装,也能兼容原生Web Component。 二、组件的定义方式 根据是否使用构建步骤,Vue…