效果预览:
前言
不算实习,前端从业已经两年了。负责或参与过的平台/项目数量已经有十几个了,这些项目当中,多多少少都需要用到编辑器。
简单的后台form表单中,一个textarea就已经够用,稍微麻烦点的,比如需要能粘进来元素样式的,设置一下元素的 contenteditable="true"也能满足需求。
但是有些项目,比如知识管理(文章编辑)、埋点平台(编辑发送总结邮件)这种,就需要专业的富文本编辑器来进行一定定制来满足需要。
那么,我们要如何选择一个合适的富文本编辑器呢?
众所周知,富文本编辑器有所见即所得的特性,展示实际上需要html源码。
就我自己就用过:
ueditor:百度 fex 的
市面上开源的还有:
editor.js
wangeditor
TinyMCE
kindeditor
simditor
Quill
slatejs
prosemirror
真让人眼花缭乱,不过支持 Vue,自带功能足够全面(表格、图片等),支持写插件扩展,很方便就能协同编辑的就只有tiptap了。
tiptap基于prosemirror,用过tiptap V1 的我对其稳定性和丰富的api印象很深刻。
而tiptap V2 则大量改写了 V1 的 api,升级需要花费很多功夫,v1的插件很多都没办法在v2上继续用了,就需要逐一升级一下。
v2也解决了v1很多bug,比如v1,checklist和orderlist或者unorderlist互转就不支持,v2上就解决了这个问题。
另外v2对于多人协同编辑的支持程度也更高,围绕多人编辑有
新建组件项目
1 npm i @tiptap/vue-2 @tiptap/starter-kit @tiptap/extension-task-list @tiptap/extension-task-item @tiptap/extension-highlight @tiptap/extension-character-count
基于 vue 2 就使用 @tiptap/vue-2 这个依赖,starter-kit 则是包含了很多基本功能,比如 text、bold、italic、header、history 等等插件。
再装一下element-ui组件库。
npm i element-ui
因为我们要用组件库来定制装修编辑器。
接下来在我们创建好的组件仓库的入口vue文件中引入tiptap v2 的套件,并做初始化。
并且应用vue的响应式特性,对数据做一定监听处理。再设计下props,外部引用组件的时候就可以通过v-model双向绑定了。
<template>
<div
class="editor"
v-if="editor">
<editor-content class="editor__content" :editor="editor" />
</div>
</template>
<script>
import StarterKit from '@tiptap/starter-kit';
import { Editor, EditorContent } from '@tiptap/vue-2';
export default {
name: 'dudu-editor',
components: {
EditorContent
},
props: {
value: {
type: String,
default: ''
}
},
data() {
return {
editor: null
};
},
watch: {
value(v) {
const isSame = this.value === v;
if (isSame) return;
this.editor.commands.setContent(this.value, false);
}
},
mounted() {
this.editor = new Editor({
content: this.value,
extensions: [
StarterKit
],
onUpdate: () => {
// HTML
this.$emit('input', this.editor.getHTML());
// JSON
// this.$emit('input', this.editor.getJSON())
}
});
},
beforeDestroy() {
this.editor.destroy();
}
};
</script>
通过vue.use全局注册之后,接下来,在外部组件就可以使用我们的编辑器了。
<template>
<div class="main">
<div>dudu-editor 嘟嘟富文本编辑器</div>
<dudu-editor v-model="value">222</dudu-editor>
<div>渲染展示</div>
<div class="output ProseMirror" v-html="value"></div>
</div>
</template>
<script>
import './index.scss';
export default {
name: 'demo',
components: {
},
data() {
return {
value: ''
};
}
};
</script>
<style lang="scss" scoped>
@import '../components/main.scss';
.space {
height: 100px;
}
.output {
padding: 10px;
}
</style>