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

简易的起始篇到此结束

相关推荐
看到请催我学习13 分钟前
内存缓存和硬盘缓存
开发语言·前端·javascript·vue.js·缓存·ecmascript
XiaoYu20022 小时前
22.JS高级-ES6之Symbol类型与Set、Map数据结构
前端·javascript·代码规范
儒雅的烤地瓜2 小时前
JS | JS中判断数组的6种方法,你知道几个?
javascript·instanceof·判断数组·数组方法·isarray·isprototypeof
道爷我悟了2 小时前
Vue入门-指令学习-v-on
javascript·vue.js·学习
27669582922 小时前
京东e卡滑块 分析
java·javascript·python·node.js·go·滑块·京东
PleaSure乐事2 小时前
【Node.js】内置模块FileSystem的保姆级入门讲解
javascript·node.js·es6·filesystem
雷特IT2 小时前
Uncaught TypeError: 0 is not a function的解决方法
前端·javascript
awonw3 小时前
[前端][easyui]easyui select 默认值
前端·javascript·easyui
老齐谈电商3 小时前
Electron桌面应用打包现有的vue项目
javascript·vue.js·electron
柏箱4 小时前
使用JavaScript写一个网页端的四则运算器
前端·javascript·css