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

简易的起始篇到此结束

相关推荐
記億揺晃着的那天5 小时前
Vue + Element UI 表格自适应高度如何做?
javascript·vue.js·ui
GISer_Jing6 小时前
ByteDance——jy真题
前端·javascript·面试
真的想不出名儿6 小时前
Vue 中 props 传递数据的坑
前端·javascript·vue.js
阳光阴郁大boy6 小时前
星座运势网站技术解析:从零打造现代化Web应用
前端·javascript
sorryhc6 小时前
如何设计一个架构良好的前端请求库?
前端·javascript·架构
Queen_sy6 小时前
vue3 el-date-picker 日期选择器校验规则-选择日期范围不能超过七天
javascript·vue.js·elementui
lvchaoq7 小时前
react 修复403页面无法在首页跳转问题
前端·javascript·react.js
郝开7 小时前
6. React useState基础使用:useState修改状态的规则;useState修改对象状态的规则
前端·javascript·react.js
技术钱8 小时前
react+andDesign+vite+ts从零搭建后台管理系统(三)-Layout布局
javascript·react.js·ecmascript
DoraBigHead9 小时前
🧭 React 理念:让时间屈服于 UI —— 从同步到可中断的演化之路
前端·javascript·面试