使用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的调用。

简易的起始篇到此结束

相关推荐
图扑软件21 分钟前
中篇・运笔|统一 DataModel 底座,HT UI 组件万物同源
javascript·低代码·ui·性能优化·数据可视化
小小尚@2 小时前
WebGIS/ECharts 地图开发|MapLand 在线行政区划边界一键下载 GeoJSON 工具
前端·javascript·echarts
葡萄城技术团队2 小时前
工业数据可视化:使用活字格 AIcoding + Three.js 构建轻量化三维设备监控大屏
开发语言·javascript·信息可视化
默_笙2 小时前
🚍 一条 Todo 的奇幻漂流:TypeScript 全栈类型安全的"护照检查"
前端·javascript
weixin_422555423 小时前
el-menu子菜单点击就被激活,恢复之前的激活状态
javascript·vue.js·elementui
Tanjia_kiki6 小时前
谷歌浏览器中F12编辑并重发请求
开发语言·前端·javascript
honkun66 小时前
vue 表格组件 vxe-table 实现拖拽列字段自动生成透视表汇总
前端·javascript·vue.js
IMPYLH6 小时前
HTML 的 <p> 元素
前端·javascript·html
leoZ2316 小时前
第 4 篇:布局骨架——页面壳、栅格、卡片
前端·javascript·vue.js·opencv·计算机视觉·数据挖掘·语音识别