返回 导航

Vue.js

hangge.com

Vue.js - 富文本编辑器Tiptap使用详解1(安装与基础配置)

作者:hangge | 2026-02-10 08:42
    Tiptap 是一个基于 ProseMirror 的无渲染富文本编辑器,专为 VueReact 等现代前端框架设计。与传统的富文本编辑器不同,Tiptap 不提供预定义的 UI 组件,而是将完整的 UI 控制权交给开发者,这使得定制化编辑器界面变得异常灵活。本文首先介绍如何在 Vue 2 项目中安装配置 Tiptap

一、安装与基础配置

1,快速安装

(1)进入 Vue 项目目录,执行如下命令安装 Tiptap 依赖:
npm install @tiptap/vue-2 @tiptap/core @tiptap/starter-kit @tiptap/extension-link @tiptap/pm

(2)各依赖的功能如下:
  • @tiptap/vue-2Vue2 的组件包装器(让我们在 Vue 2 中使用 EditorContent 等)。 
  • @tiptap/core:编辑器核心:
  • @tiptap/starter-kit:一组常用扩展(段落、粗体/斜体、列表、undo/redodropcursor 等),适合快速启动。 
  • @tiptap/pmTiptap 提供的 ProseMirror 包的打包版,能避免 ProseMirror 版本冲突(在某些包管理器或环境下显得重要)。若遇到缺失 ProseMirror 相关依赖报错,安装此包通常能解决问题

2,最小可运行示例

(1)下面代码演示如何在 Vue 2 中创建 Editor 实例并渲染编辑区域(使用 StarterKit):
<template>
  <div class="basic-tiptap">
    <!-- EditorContent 负责渲染 contenteditable 区域 -->
    <editor-content :editor="editor" />
  </div>
</template>

<script>
import { Editor } from '@tiptap/core'
import StarterKit from '@tiptap/starter-kit'
import { EditorContent } from '@tiptap/vue-2'

export default {
  name: 'BasicTiptap',
  components: { EditorContent },
  data() {
    return { editor: null }
  },
  mounted() {
    // 在 mounted 创建 Editor 实例并传入扩展与初始内容
    this.editor = new Editor({
      extensions: [ StarterKit ],
      content: `<h2>欢迎访问hangge.com</h2>
                <p>这是一个最小的示例。</p>`
    })
  },
  beforeDestroy() {
    // 组件销毁前一定清理 editor 实例,避免内存泄漏
    if (this.editor) this.editor.destroy()
  }
}
</script>

<style scoped>
.basic-tiptap {
  border: 1px solid #e6e6e6;
  padding: 12px;
  border-radius: 6px;
  min-height: 180px;
}
</style>

(2)运行后可用看到编辑区域显示出我们预设的文字。

(3)点击该区域可用进入编辑状态,此时我们可用输入、选择文字并使用浏览器快捷键,如:Ctrl + B 加粗文本、Ctrl + I 将文本设置为斜体、Ctrl + U 文本添加下划线。

3,带工具栏的基础示例

(1)通常来说我们编辑器上面还需要有一个工具栏,而不是只用快捷键)。下面示例在最小示例基础上增加了常见按钮(加粗、斜体、下划线、中划线、无序列表、有序列表、插入链接、移除链接)。
提示:由于 Tiptap 是“headless”编辑器(它不自带 UI),因此按钮、菜单栏等需要我们自己实现(像下面这样在 Vue 中调用 editor.chain()...run())。若按钮样式需求复杂,可用图标库(FontAwesome)或自行样式化。
<template>
  <div class="editor-wrap">
    <div class="toolbar" role="toolbar" aria-label="Editor toolbar">
      <button
        :class="{ active: isActive('bold') }"
        @click="toggleBold"
        :title="'加粗 (Ctrl/Cmd+B)'"
      >B</button>

      <button
        :class="{ active: isActive('italic') }"
        @click="toggleItalic"
        :title="'斜体 (Ctrl/Cmd+I)'"
      >I</button>

      <button
        :class="{ active: isActive('underline') }"
        @click="toggleUnderline"
        :title="'下划线 (Ctrl/Cmd+U)'"
      >U</button>

      <button
        :class="{ active: isActive('strike') }"
        @click="toggleStrike"
        :title="'删除线'"
      >S</button>

      <button
        :class="{ active: isActive('bulletList') }"
        @click="toggleBulletList"
        :title="'项目符号列表'"
      >• List</button>

      <button
        :class="{ active: isActive('orderedList') }"
        @click="toggleOrderedList"
        :title="'编号列表'"
      >1. List</button>

      <button @click="addLink" title="插入链接">🔗</button>
      <button @click="removeLink" title="移除链接">✖️🔗</button>
    </div>

    <editor-content :editor="editor" class="editor-content" />
  </div>
</template>

<script>
import { Editor } from '@tiptap/core'
import StarterKit from '@tiptap/starter-kit'
import Link from '@tiptap/extension-link'
import Underline from '@tiptap/extension-underline' // 可选:若 StarterKit 未包含
import Strike from '@tiptap/extension-strike' // 若需要删除线(StarterKit 通常包含)
import { EditorContent } from '@tiptap/vue-2'

export default {
  name: 'EditorFormatting',
  components: { EditorContent },
  data() {
    return { editor: null }
  },
  mounted() {
    this.editor = new Editor({
      extensions: [ StarterKit, Link, Underline, Strike ],
      content: `<h2>欢迎访问hangge.com</h2>
            <p>这是一个最小的示例。这是一个最小的示例。这是一个最小的示例。这是一个最小的示例。</p>
            <p>列表1</p>
            <p>列表2</p>
            <p>列表3</p>
            <p>列表1</p>
            <p>列表2</p>
            <p>列表3</p>`
      // onUpdate / onTransaction 可在此加入,但示例以最小化为主
    })
  },
  methods: {
    /* 文本样式命令(使用链式 API 并 focus() 保证焦点) */
    toggleBold() { this.editor.chain().focus().toggleBold().run() },
    toggleItalic() { this.editor.chain().focus().toggleItalic().run() },
    toggleUnderline() { this.editor.chain().focus().toggleUnderline().run() },
    toggleStrike() { this.editor.chain().focus().toggleStrike().run() },

    /* 列表 */
    toggleBulletList() { this.editor.chain().focus().toggleBulletList().run() },
    toggleOrderedList() { this.editor.chain().focus().toggleOrderedList().run() },

    /* 链接操作:插入或移除 */
    addLink() {
      const url = prompt('请输入链接 URL(包含 http:// 或 https://)')
      if (!url) return
      // extendMarkRange 可以确保扩展当前选区的 mark 范围(便于整段变为 link)
      this.editor.chain().focus().extendMarkRange('link').setLink({ href: url }).run()
    },
    removeLink() {
      this.editor.chain().focus().unsetLink().run()
    },

    /* 判断当前格式是否激活(用于按钮高亮) */
    isActive(name, attrs = {}) {
      return this.editor && this.editor.isActive(name, attrs)
    }
  },
  beforeDestroy() {
    if (this.editor) this.editor.destroy()
  }
}
</script>

<style scoped>
.editor-wrap { border: 1px solid #ddd; border-radius:6px; }
.toolbar { padding:8px; background:#fafafa; display:flex; gap:6px; flex-wrap:wrap; }
.toolbar button { padding:6px 8px; border:1px solid #eaeaea; 
    background:white; border-radius:4px; cursor:pointer; }
.toolbar button.active { background:#e6f7ff; border-color:#91d5ff; font-weight:600; }
.editor-content { min-height:200px; padding:12px; }
</style>

(2)运行程序可用看到编辑器上面出现工具栏,点击相关按钮即可实现对应快捷操作。
评论

全部评论(0)

回到顶部