使用tiptap开发一个富文本编辑器 —— 起始篇

tiptap介绍

tiptap是一个"无头"的富文本编辑器。"无头",即,非开箱即用,不提供功能面板,需要用户自己开发功能面板的UI。

官方文档地址:Introduction -- Tiptap

tiptap安装(以vue为例)

新建一个vue工程

shell 复制代码
pnpm create vue@latest

# 输入项目名称,配置工程内容等

pnpm i

安装tiptap依赖

shell 复制代码
pnpm install @tiptap/vue-3 @tiptap/pm @tiptap/starter-kit

初始化tiptap

html 复制代码
<template>
  <EditorContent :editor="editor"></EditorContent>
</template>

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

const editor = useEditor({
  content: 'hello tiptap',
  extensions: [StarterKit]
})
</script>

预览效果如下

好的,相信你已经学会了tiptap编辑器的基本内容,接下来我们就去开发一个飞书文档吧(大雾)。

首先对这3个依赖进行作用分析:

@tiptap/vue3提供vue3里的组件引用与editor暴露,同时,所有在@tiptap/core中导出的api,也会在该库中导出

@tiptap/pm提供tiptapProseMirror的api,tiptap是基于ProseMirror开发,pm库属于必要库。 @tiptap/start-kit为插件合集库,包含了如下内容:

需要配置上述插件能力的地方,可以直接从start-kit中进行处理,也可以不使用start-kit包,自身配置一个最简易的文本输入可通过配置DocumentTextParagraph3个插件实现。

tiptap功能面板实现

首先准备一份富文本图标(本文从iconfont上白嫖一份公开库:富文本编辑器图标),并实现一下基本UI和交互逻辑

tiptap的outline可以通过注入属性的方式去除,根据当前的UI,需要实现如下功能:

根据API实现代码:

html 复制代码
<template>
  <div style="border: 2px solid #dcdfe6; width: 600px; height: 300px">
    <div class="tool-panel">
      <div
        :class="['btn', editor.isActive('bold') && 'active']"
        @click="editor.chain().focus().toggleBold().run()"
      >
        <i class="icon iconfont icon-bold"></i>
      </div>
      <div
        :class="['btn', editor.isActive('italic') && 'active']"
        @click="editor.chain().focus().toggleItalic().run()"
      >
        <i class="icon iconfont icon-italic"></i>
      </div>
      <div
        :class="['btn', editor.isActive('orderedList') && 'active']"
        @click="editor.chain().focus().toggleOrderedList().run()"
      >
        <i class="icon iconfont icon-list-order"></i>
      </div>
      <div
        :class="['btn', editor.isActive('bulletList') && 'active']"
        @click="editor.chain().focus().toggleBulletList().run()"
      >
        <i class="icon iconfont icon-list-disorder"></i>
      </div>
      <div
        :class="['btn', editor.isActive('blockquote') && 'active']"
        @click="editor.chain().focus().toggleBlockquote().run()"
      >
        <i class="icon iconfont icon-double-quotes-left"></i>
      </div>
      <div class="btn" @click="editor.chain().focus().setHorizontalRule().run()">
        <i class="icon iconfont icon-move-horizontal"></i>
      </div>
      <div class="btn" @click="editor.chain().focus().toggleCodeBlock().run()">
        <i class="icon iconfont icon-code-block"></i>
      </div>
    </div>
    <div style="height: calc(100% - 30px)">
      <EditorContent :editor="editor" class="tiptap-container"></EditorContent>
    </div>
  </div>
</template>

<script setup>
import { useEditor, EditorContent } from '@tiptap/vue-3'
import { StarterKit } from '@tiptap/starter-kit'
const editor = useEditor({
  content: 'hello tiptap',
  extensions: [StarterKit],
  editorProps: {
    attributes: {
      class: 'tiptap-editor'
    }
  }
})
</script>

<style>
.tiptap-editor {
  width: 100%;
  height: 100%;
  outline: none;
}

.tiptap-container {
  width: 100%;
  height: 100%;
}

.tool-panel {
  border-bottom: 2px solid #dcdfe6;
  height: 30px;
  display: flex;
  align-items: center;
}

.icon {
  font-size: 20px;
}
.btn {
  width: 30px;
  height: 30px;
  line-height: 30px;
  text-align: center;
  cursor: pointer;
}
.active {
  background-color: #909399;
}
blockquote {
  padding-left: 1rem;
  border-left: 3px solid rgb(144, 143, 153);
}

pre {
  background: #0d0d0d;
  color: #fff;
  font-family: 'JetBrainsMono', monospace;
  padding: 0.75rem 1rem;
  border-radius: 0.5rem;
}
pre code {
  color: inherit;
  padding: 0;
  background: none;
  font-size: 0.8rem;
}
</style>

效果图如下:

editor.isActive可以检测当前正在激活的nodemark,可用于确认当前文本为哪种格式(示例中已激活的粗体)

各图标绑定了点击事件,使用editor.chain().focus().xxx().run()。这事使用tiptap中的command能力来使富文本进行指定交互,除了上述方法外,也可以通过editor.commands.xxx(),但是还是推荐使用chain来实现。chain的使用实际上可以进行复合命令输入,即可以实现一次注入多个命令,例如editor.chain().focus().toggleBold().toggleItalic().run()可以同时实现文本的粗体和斜体切换。

重要 :如果未使用run(),命令不会生效,开发时注意不要丢了run的调用。

简易的起始篇到此结束

相关推荐
bin915328 分钟前
DeepSeek 助力 Vue 开发:打造丝滑的复制到剪贴板(Copy to Clipboard)
前端·javascript·vue.js·ecmascript·deepseek
晴空万里藏片云2 小时前
elment Table多级表头固定列后,合计行错位显示问题解决
前端·javascript·vue.js
奶球不是球2 小时前
el-button按钮的loading状态设置
前端·javascript
无责任此方_修行中4 小时前
每周见闻分享:杂谈AI取代程序员
javascript·资讯
dorabighead5 小时前
JavaScript 高级程序设计 读书笔记(第三章)
开发语言·javascript·ecmascript
林的快手7 小时前
CSS列表属性
前端·javascript·css·ajax·firefox·html5·safari
bug总结7 小时前
新学一个JavaScript 的 classList API
开发语言·javascript·ecmascript
网络安全-老纪7 小时前
网络安全-js安全知识点与XSS常用payloads
javascript·安全·web安全
yqcoder8 小时前
Express + MongoDB 实现在筛选时间段中用户名的模糊查询
java·前端·javascript
十八朵郁金香8 小时前
通俗易懂的DOM1级标准介绍
开发语言·前端·javascript