前言

半路接手的后台项目,里面的富文本编辑器使用的kindeditor,总感觉有些臃肿,而且之前的人在封装组件后,使用起来有些bug,于是从网上搜了一下,决定使用wangeditor进行重新封装,这里将使用笔记留下,供以后查看,并分享。

1.基本使用

这个没什么好说的了,去官网看文档就好了。
出现的几个小问题:

  1. 注意设置一下editor的z-index(editor.config.zIndex),因为他的默认z-index很大,很有可能遮挡住你的其他控件(如弹窗等)。
  2. 使用接口获取已经保存的富文本内容,并将内容v-bind到editor组件的时候,由于请求数据需要时间,很大可能造成富文本框组件渲染时props还没获取到富文本内容,无法在mounted事件中直接将内容回填到富文本框,这里我直接在editor组件中通过watch解决的。

2.图片上传

如果需要自己实现上传逻辑,可参考并配置editor.config.customUploadImg
我的情况是,我们有自己的图片上传组件,希望使用自己的组件进行上传,这时需要配置editor.config.uploadImgFromMedia方法,替换本地上传功能,在其中加入自己的逻辑。
记着上传结束后调用editor.cmd.do方法将图片插入富文本内容中。

3.视频上传

同上,我们也希望使用自己的组件替换原始的本地上传,可是文档中没有提供类似editor.config.uploadImgFromMedia的方法。
没办法,只能使用自定义扩展Button菜单的方式完成了。
思路如下:

  1. 配置中删除video菜单。
  2. 加入自定义扩展Button菜单。
  3. 增加配置一个editor.config.uploadVideoFromMedia函数,来实现我们自己的逻辑。

在自定义扩展Button菜单的类中:

  1. 自定义菜单沿用之前video菜单的样式,保证样式一致。
  2. 在自定义菜单中,设置菜单点击事件去调用我们自己配置的editor.config.uploadVideoFromMedia

这样,实现了视频上传中类似editor.config.uploadImgFromMedia的方法。

自定义扩展Button菜单的类的代码如下:

//wangeditor的自定义视频上传菜单
import E from 'wangeditor';

const BtnMenu = E.BtnMenu;

class NewVideoMenu extends BtnMenu{
    constructor(editor){
        const $elem = E.$(
            `<div class="w-e-menu" data-title="视频">
                <i class="w-e-icon-play"></i>
            </div>`
        );
        super($elem, editor);
    }

    clickHandler(){
        this.editor.config.uploadVideoFromMedia();
    }

    tryChangeActive(){}
}

export default NewVideoMenu;

通过控制自定义菜单在editor的配置数组editor.config.menus中的插入位置,来调整菜单的显示位置。

4.上传的视频在富文本框没有光标、无法删除的问题

视频上传结束时:

let videoHTML = '&nbsp;<video src="' + this.video_url + '" controls style="max-width:100%"></video>&nbsp;';
this.editor.cmd.do('insertHTML', videoHTML);

视频标签的两侧各加一个空格的转义字符&nbsp;,可以使光标在富文本的视频周围出现,并可以删除视频。测试环境中测试有效。

5.编辑时光标跳转到最后的问题(new~)

使用时发现了这样的bug,即每编辑一下,光标就会跳到文末。
问题来源于:每次编辑富文本会触发editor的onchange事件,onchange事件中,通过触发父组件的update:propName自定义事件,实现富文本内容prop由子组件向父组件的更新(参照vue文档.sync修饰符部分)。而这又会反过来触发子组件对富文本内容prop的watch监听(之前我们通过watch监听来实现父组件接口数据对editor组件富文本框的内容回填),最终导致每一下的编辑,都会让富文本内容重新回填到富文本框一次,出现了光标每次跳转到文末的问题。

解决方案:在子组件data中增加一项数据,用于判断当前是否手动进行了富文本框编辑,如果是,则watch监听中不再将传递来的绑定数据回填到富文本框中。

watch: {
    content(newVal, oldVal){
        if(!this.isChange){
            this.editor.txt.html(newVal);
        }
        this.isChange = false;
    }
},
...
...
mounted(){
    ...
    this.editor.config.onchange = html => {
        this.isChange = true;
        this.$emit('update:content', html);
    }
    ...
}

6.页面初始化时数据不向富文本框回填的问题(new~)

在上一部分中,我们通过一个值isChange来判断修改来源,并阻断watch监听中的由于手动编辑富文本框造成的数据二次回填问题。
然后,又又又出问题了。。。
修改过后,发现页面在初始化的时候,富文本数据无法正常回填了。。。
打断点,调试,最终发现了问题来源:
由于页面使用了v-if来切换渲染富文本框和其他组件,因此在editor.create之后,我添加了

    this.editor.txt.html(this.content);

来确保组件由于v-if而重新渲染时,能够正确回填内容到富文本框。

也因此导致了以下流程:

父组件用默认数据渲染editor组件 -> editor组件在create之后调用了this.editor.txt.html(父组件默认数据); -> 触发了editor的onchange事件 -> isChange变为true -> 父组件通过接口获取真实数据并传递给editor组件 -> editor组件watch监听到了数据变化想要回填 -> 但是isChange因为时true,回填失败。

解决方案:onchange事件中,通过判断数据是否发生了变化,来确定本次修改是否来源于真实修改(由this.editor.txt.html(this.content)触发的onchange事件,onchange事件的参数与this.content相同)

this.editor.config.onchange = html => {
    if(this.content == html){
        this.isChange == false
    }else{
        this.isChange = true;
    }
    this.$emit('update:content', html);
}

结语

大部分的需求下,我们不需要使用功能过于复杂的富文本控件,使用一个简洁轻量的富文本控件就好。wangEditor就是其中之一。除此之外,其可扩展性好,文档写的也不错,还是很推荐大家使用的。


wei4118268
88 声望1 粉丝

前端从业者