前言
我们前端工程师对富文本编辑器都不陌生,甚至用的十分频繁,但是网络上有各种各样的编辑器,我们到底该做出怎样的选择,首先是要取决于我们项目的功能需求,其次再取决于是否开源并且容易上手。
公司的项目需求首先是最基本的文本编辑功能,其次就是可以上传本地图片,可以添加表格,可以支持计数,所以我在网上查了很多并进行了对比的编辑器:
- 百度编辑器UEditor
-
优点 缺点 1、功能十分强大,其他没有的它基本都有;
2、可以支持本地图片上传,媒体上传等;
3、有全屏功能;1、一直没有人维护,有bug也没更新了;
2、图片上传需要后端支持; - wangEditor
-
优点 缺点 1、可以支持本地图片上传;
2、上传图片提供钩子函数进行自定义处理;1、没有提供全屏功能;
2、从word复制粘贴的代码,会有很多坑;
3、提供的功能都是最基本的功能; - TinyMCE
-
优点 缺点 1、功能十分强大,只有基础功能免费;
2、上传图片并提供钩子函数进行处理;1、仅开源部分功能,大部分需要收费;
2、只有英文文档,对英文一般的不太友好; - CKEditor
-
优点 缺点 1、可以上传本地图片;
2、支持全屏功能;上传图片需要后台支持;
前3个编辑器我都用过,由于ueditor自己都放弃了它自己的项目,所以我们也应该放弃,接下我大概讲讲wangEditor和tinyMCE在vue项目中的用法:
wangEditor
安装依赖
npm install wangeditor --save
//cnpm i wangeditor --save
引入依赖
// 引入富文本编辑器
import WangEditor from 'wangeditor'
创建实例
data(){
return {
editorId:'',
editor:null
}
},
method:{
//为了防止组件在同一个页面多次被调用,需要生成一个随机的不重复id,可以通过时间和随机数生成
randomId(){
let baseId = 'wang_editor';
let now = Date.now();
let num = Math.random().toFixed(4)*10000;
return `${baseId}_${now}_${num}`;
},
//初始化编辑器
initEditor(){
let _this = this;
_this.editorId = _this.randomId();//生成一个id
this.$nextTick(()=>{
//获取实例,wangEditor是被注册在window的
// 如果不明白,可以let wangEditor = window.wangEditor
let editor = new wangEditor('#'+_this.editorId);
_this.editor = editor;//将实例保存待调用其他api
editor.create();//开始创建编辑器;
})
}
}
配置编辑器
wangeditor给使用者提供了很多配置,可以配置编辑器使其更加满足我们的项目需求。
以下是常用的配置,还有其他需要的配置请去官网查看文档。
methods:{
setConfig(){
let setting = {
uploadImgShowBase64:true,// 是否允许上传base64位图片
pasteFilterStyle:true, // 是否过滤粘贴的样式
zIndex:100,//设置层叠位置
//菜单列表
menus:[
'head', // 标题
'bold', // 粗体
'fontSize', // 字号
'fontName', // 字体
'italic', // 斜体
'underline', // 下划线
'strikeThrough', // 删除线
'foreColor', // 文字颜色
'backColor', // 背景颜色
'link', // 插入链接
'list', // 列表
'justify', // 对齐方式
'quote', // 引用
'emoticon', // 表情
'image', // 插入图片
'table', // 表格
'video', // 插入视频
'code', // 插入代码
'undo', // 撤销
'redo' // 恢复
],
showLinkImg:false,//是否显示“网络图片”tab
//监听用户输入后的change事件
onchange:function(html){
console.log(html);
}
}
//配置给编辑器
this.editor.customConfig = Object.assign(this.editor.customConfig,setting)
}
}
配置完成后的效果:


常用的API
- 获取html代码/填充html代码
//类似jquery的用法
//如果是直接调用,不传递参数,就是获取
this.editor.txt.html();
//如果传递参数,就是讲html替换为参数内容
this.editor.txt.html('<p>这是我替换的内容</p>');
- 获取除了标签的文本内容
this.editor.txt.text()
- 使用js添加内容
this.editor.txt.append("<p>这是我用js代码添加的代码</p>")
- 启用和禁用编辑器
this.editor.$textElem.attr('contenteditable',true/false)
完整代码
wang-editor.vue组件
<template>
<div :id="editorId"></div>
</template>
<script>
// 引入富文本编辑器
import wangEditor from 'wangeditor';
export default {
props:{
value:{
required:true
},
disabled:{
type:Boolean,
default:false
}
},
data () {
return {
editor:'',
editorId:'',
};
},
watch:{
value(newval){
if(this.editor){
this.editor.txt.html(newval)
}
}
},
methods: {
//生成一个随机不重复id,可以通过时间和随机数生成
randomId(){
let baseId = 'wang_editor';
let now = Date.now();
let num = Math.random().toFixed(2)*100;
return `${baseId}_${now}_${num}`
},
//初始化编辑器
initEditor(){
let _this = this;
_this.editorId = _this.randomId();//生成一个id
this.$nextTick(()=>{
//获取实例,wangEditor是被注册在window的
let editor = new wangEditor('#'+_this.editorId);
_this.editor = editor;//将实例保存待调用其他api
_this.setConfig();
editor.create();//开始创建编辑器;
_this.editor.txt.html(this.value)
// 设置是否可编辑
if(this.disabled!=='undefined'){
this.editor.$textElem.attr('contenteditable',!this.disabled)
}
})
},
// 创建富文本编辑器
setConfig(){
var _this = this
// 开始创建
let setting = {
uploadImgShowBase64:true,// 是否允许上传base64位图片
pasteFilterStyle:true, // 是否过滤粘贴的样式
zIndex:100,//设置层叠位置
//菜单列表
menus:[
'head', // 标题
'bold', // 粗体
'fontSize', // 字号
'fontName', // 字体
'italic', // 斜体
'underline', // 下划线
'strikeThrough', // 删除线
'foreColor', // 文字颜色
'backColor', // 背景颜色
'link', // 插入链接
'list', // 列表
'justify', // 对齐方式
'quote', // 引用
'emoticon', // 表情
'image', // 插入图片
'table', // 表格
'video', // 插入视频
'code', // 插入代码
'undo', // 撤销
'redo' // 恢复
],
showLinkImg:false,//是否显示“网络图片”tab
//监听用户输入后的change事件
onchange:function(html){
_this.$emit('input',html)
}
}
//配置给编辑器
_this.editor.customConfig = Object.assign(_this.editor.customConfig,setting)
},
},
created(){
// 创建editor实例
this.initEditor();
}
}
</script>
test.vue调用
<template>
<div>
<wang-editor
style='height:300px;'
v-model='wangValue'
:disabled='wangDisabled'
></wang-editor>
<h5>双向绑定的源代码</h5>
<div>{{wangValue}}</div>
<h5>双向绑定的页面效果</h5>
<div v-html='wangEditor'></div>
</div>
</template>
<script>
import wangEditor from './wang-editor';
export default {
components:{wangEditor},
data(){
return {
wangValue:'',
wangDisabled:false
}
}
}
</script>
TinyMCE
本来是拒绝使用这个编辑器的,虽然这个编辑器的功能十分强大,但是大部分的功能都是要收费的,还有文档特别多,而且还全是英文的。可是我使用的wangEditor有一个很大的坑,就是从word复制粘贴的时候,会莫名的失去一些内容或者会自动替换一些内容,导致用户使用起来非常不方便,所以我被迫换掉了wangEditor。
安装依赖
cnpm i tinymce --save
cnpm i @tinymce/tinymce-vue --save
引入依赖
import tinymce from 'tinymce/tinymce';
import Editor from '@tinymce/tinymce-vue';
引入插件
// 更多插件参考:https://www.tiny.cloud/docs/plugins/
import 'tinymce/plugins/image';// 插入上传图片插件
import 'tinymce/plugins/media';// 插入视频插件
import 'tinymce/plugins/table';// 插入表格插件
import 'tinymce/plugins/lists';// 列表插件
import 'tinymce/plugins/wordcount';// 字数统计插件
import 'tinymce/plugins/paste';//复制粘贴图片
import 'tinymce/plugins/fullscreen';//全屏插件
复制皮肤包
注意:这个步骤必须做,否则编辑器是显示不出来的,就相当于一个人,不然别人穿衣服出去裸奔。
从node_modules中找到tinymce下的skins文件夹,复制到static目录下。
中文包
这个看个人需求,如果需要的话就去官网进行下载,下载地址。

源代码
废话不多说,直接上源代码吧,基本上都添加了注释。
<template>
<div class="tinymce-editor">
<editor
v-model="myValue"
:init="init"
:disabled="disabled"
@onClick="onClick">
</editor>
</div>
</template>
<script>
import tinymce from 'tinymce/tinymce'
import Editor from '@tinymce/tinymce-vue'
import 'tinymce/themes/silver/theme'
// 更多插件参考:https://www.tiny.cloud/docs/plugins/
import 'tinymce/plugins/image'// 插入上传图片插件
// import 'tinymce/plugins/media'// 插入视频插件
import 'tinymce/plugins/table'// 插入表格插件
// import 'tinymce/plugins/lists'// 列表插件
import 'tinymce/plugins/wordcount'// 字数统计插件
import 'tinymce/plugins/paste';//复制粘贴图片
import 'tinymce/plugins/fullscreen';//全屏插件
export default {
components: {
Editor
},
props: {
//传入的默认值
value: {
type: String,
default: ''
},
//是否禁用
disabled: {
type: Boolean,
default: false
},
//插件
plugins: {
type: [String, Array],
default: 'image table wordcount paste fullscreen'
},
//菜单
toolbar: {
type: [String, Array],
//如果显示的内容和配置的不符,表明插件未引入。需要去引入插件;
default: 'undo redo | styleselect | bold italic | alignleft aligncenter alignright alignjustify | bullist numlist outdent indent | link image | print preview media fullpage | forecolor backcolor emoticons | help'
},
//编辑器高度
height:{
default:300
}
},
data () {
let _this = this;
return {
init: {
language_url: '/static/tinymce/lang/zh_CN.js', //中文包路径地址
language: 'zh_CN',
skin_url: '/static/tinymce/skins/ui/oxide', //皮肤包路径地址
height: this.height,//编辑器高度
max_height:570,//resize为true时,控制编辑器的最大高度
plugins: this.disabled?'':this.plugins, // 插件
toolbar: this.disabled?'':this.toolbar, // 工具栏
images_upload_url: '', //上传路径
elementpath:false,//标签路径是否显示
menubar:false,//顶部菜单
resize:this.disabled?false:true,//页面大小拖动
paste_data_images: true,//可以粘贴图片,需要引入paste插件
content_style:'div,p{margin:5px 0;}'+'img{max-width:100%;}'+'*::-webkit-scrollbar{width:6px;height:6px;background:transparent;}'+'*::-webkit-scrollbar-thumb{border-radius: 3px;background: #bac3d9;}',
// 此处为图片上传处理函数,这个直接用了base64的图片形式上传图片
// 直接使用base64处理,最好还是上传到服务器,因为base64存储到服务器数据库太庞大了
images_upload_handler: (blobInfo, success, failure) => {
const img = 'data:image/jpeg;base64,' + blobInfo.base64()
success(img)
},
init_instance_callback : function(editor) {
_this.editorId = editor.id;
},
branding:false
},
myValue: this.value,
editorId:null
}
},
mounted () {
tinymce.init({})
},
methods: {
onClick (e) {
this.$emit('onClick', e, tinymce)
}
},
watch: {
value (newValue) {
this.myValue = newValue
},
myValue (newValue) {
this.$emit('input', newValue)
}
}
}
</script>
tinymce这个编辑器虽然很多功能是要收费的,但是他提供的基本功能都能满足大部分用户的需求,而且UI设计上也很美观,有需要的您可以试一试。