关于wangEditor的那些使用问题

763 阅读6分钟

最近接手老项目,有一个小需求变更——添加富文本功能,老项目组件库里有wangEditor,为了便捷性,干脆直接用。

结果问题来了,控制台飘红,解决之后,又飘红,再次解决,再次出现新的飘红,所以就很无奈。(严重怀疑这个组件是某某练习的半成品小demo...)

人在遇到报错、无脑改bug的时候,思维其实已经僵化了,哪怕一开始路线就是错的,也会不觉,一条路走到黑。

后面干脆照着文档,自己封一个组件,用起来还不错,对于正在做富文本的战友们,如果下面我这套和你适用,可以直接cv。

另外我会针对之前走的弯路,各种千奇百怪的bug,做个完整复盘,把修复方式统统放在下面,共勉!

微信图片_20241120171512.gif

先放两个较为重点的报错,也是很多人经常遇到的:

第一种:

image.png

通常情况下,富文本会放在弹窗类似组件中,进行显示or隐藏,出现这个问题的原因,就是因为弹窗关了,但富文本没被销毁+初始化,所以为了强制执行,你可以在弹框上加个v-show,后面的开关,可以和弹窗保持一致,用它的就行。

注意,这里为什么不写v-if?(会引出下一个bug),富文本经常报错的原因,就是操作不当,导致是否注销、执行先后问题、初始化等等状况频出。

第二个bug来了:

image.png

看了一圈解决方式,我在思考,搞个富文本,难道我还得继续去看Slate.js吗?实际上不用。

遇到这种情况,请把v-if变成v-show,如果还是不好使,在非“过分复杂性”富文本的情况下,那就说明使用不当,所以不建议继续“硬啃”了(后面我会把代码贴出来,并实际捋一捋)

当楼内出现问题,实在解决不了,如果时间允许,不妨拆一层,重新盖一下,这样会很通透。

对于v-if和v-show的区别,就不赘述了,引用一段话,如下:

v-if 本质是通过操纵dom元素来进行切换显示

表达式的值为true的时候元素存在于dom树中,为false的时候从dom树中移除

v-show

原理是修改元素的的CSS属性(display)来决定实现显示还是隐藏

指令后面的内容最终都会解析为布尔值

值为真(true)的时候元素显示,值为假(false)的时候元素隐藏

数据改变之后呢对应的元素的显示状态也是会同步更新的

v-show指令:元素始终被渲染到HTML,它只是简单的伪元素设置css的style属性,当不满足条件的元素被设置

style=“display:none”的样,是通过修改元素的的CSS属性(display)来决定实现显示还是隐藏

v-if指令:满足条件是会渲染到html中,不满足条件时是不会渲染到html中的,是通过操纵dom元素来进行切换显示

除了上述bug,把我的代码放一下,下图为富文本示意图:

image.png

对于右下角最大字数限制的问题,我想说一下,千万别截取,网上部分博客都给人带跑偏了,但凡认真看一看官方文档,都不会这么做。

截取是可以,但最后搞不好,就会出现上面的问题。

代码奉上(中间我会把我的思路写上)

注释部分,左边都会有箭头,便于大家查看

<template>
  <div>
    <div class="wang-editor">
    
    
    注释:toolbar固定格式,抄就完事了
      <toolbar
        :editor="editor"
        :default-config="toolbarConfig"
        :mode="mode"
        style="border-bottom: 1px solid #ccc"
      />
      
      
    注释:editor这一块,看你需求,可适当加回调
          onCreated用来做初始化操作
          onChange是富文本change事件,用来传值
          onMaxLength用来设置最大字数限制(不用截取!!!)
          keydown.enter.native这一块产品提优化再做,主要是为了应对“超过最大字数,依旧能回车的情况,为了美观,阻止一下就可以”
          default-config是你富文本的一些配置
          其它没什么了,细节可以看文档
      <editor
        :key="topicDetail"
        v-model="html"
        style="height: 500px; overflow-y: hidden"
        :default-config="editorConfig"
        :mode="mode"
        @onCreated="onCreated"
        @onChange="onChange"
        @onMaxLength="onMaxLength"
        @keydown.enter.native="keyDown"
      />
      
      
    </div>
  </div>
</template>

<script>
import '@wangeditor/editor/dist/css/style.css'
import { Editor, Toolbar } from '@wangeditor/editor-for-vue'

export default {
  name: 'customEditor',
  components: {
    Editor,
    Toolbar
  },



  注释:这里,双向绑定指定名字(非固定写法,你父组件用v-model也行,或者父子传值都可以)
  model: {
    prop: 'value',
    event: 'change'
  },
  
  
  
  props: {
    editorVisible: {
      type: Boolean,
      default: false
    },
    
    
    
    注释:这个是我把值,传给了富文本,起到打开弹框,就能初始化显示内容的操作
    value: {
      type: String,
      default: ''
    },
    
    
    
    
    注释:height这个可以不要,纯css改也行
    height: {
      type: [String, Number],
      default: 300
    }
  },
  
  
  
  
  注释:这一块就是业务代码,看你实际需求,其实就是监听一下,然后做个赋值操作,this.html是富文本的内容
  watch: {
    value: {
      handler (newValue, oldValue) {
        console.log('监听', newValue)
        this.html = newValue
      },
      immediate: true,
      deep: true
    },
    
    
    
    
    
    注释:这个就是上述bug的点,我当初把弹框的开关传进来,用以显示隐藏,不要也罢,没什么用
    // wangEditor内置问题解决(销毁实例)
    editorVisible (val) {
      // if (val && this.editor) {
      //   this.editor.setHtml(this.value)
      // }
      // if (val == false) {
      //   console.log('关闭了')
      //   this.topicDetail++
      // }
    }
  },
  
  
  
  
  data () {
    return {
    
    
    
    
    注释:定义的一些变量
      flag: false,
      editor: null,
      html: '',
      toolbarConfig: {
        excludeKeys: [
          'group-image',
          'group-video',
          'emotion',
          'insertLink',
          'codeBlock'
        ]
      },
      topicDetail: 0,
      
      
      
      
      注释:富文本的相关定制部分,maxLength就是右下角的字数限制,其它看你需求去官网cv一下
      editorConfig: {
        maxLength: 1000,
        placeholder: '请输入内容',
        MENU_CONF: {
          uploadImage: {
            customUpload: (resultFile, insertImgFn) => {
              const formData = new FormData()
              formData.append('file', resultFile)
              fileApi.postFileUpload(formData).then(res => {
                insertImgFn(res.data, '', res.data)
              })
            }
          },
          uploadVideo: {
            customUpload: (resultFile, insertImgFn) => {
              const formData = new FormData()
              formData.append('file', resultFile)
              fileApi.postFileUpload(formData).then(res => {
                insertImgFn(res.data, '', res.data)
              })
            }
          }
        }
      },
      mode: 'default', // or 'simple'
      useLen: 0
    }
  },
  methods: {
  
  
  
  
  注释:这个回调,当你右下角字数到了,就会启动,你在这里可以针对“字数溢出的情况,做固定需求”
    // 最大长度回调
    onMaxLength (editor) {
      console.log('文字溢出')
    },
    
    
    
    
    // 阻止回车(优化)
    注释:这一块就是阻止回车部分,可用可不用,看你细节度
    keyDown (e) {
      if (this.useLen >= this.editorConfig.maxLength - 1) {
        if (e.code == 'Enter') {
          e.preventDefault()
          return false
        }
      }
    },
    
    
    
    
    // 初始化编辑器
    注释:onCreated很多情况下,富文本dom出现了,但是slate.js没解析到,说到底,就是执行先后顺序问题,你可以先判断是否有实例,然后注销再初始化,稳妥一点(也就是我写注释的部分)
    onCreated (editor) {
      console.log('初始化')
      // 判断实例
      // if (this.editor) {
      //   this.editor.destroy()
      // }
      this.editor = Object.seal(editor)
      // 设置初始内容
      this.editor.setHtml(this.value)
    },
    
    
    
    
    
    注释:这一块就是change事件,实时获取富文本里的内容,然后父子传值,扔过去就行,但一定要注意,不要把大量逻辑写在change事件里面,出bug最多的点,就在这里!!!!!!!!!!!!!!(一百个叹号飘过!!!!)
    onChange (editor) {
      console.log('this.html', this.html)
      console.log('this.value')
      // this.html = this.value
      this.$emit('change', this.html)
    }
  },
  
  
  
  
  
  注释:这一步多余了,所以我给注释掉
  // mounted () {
  //   // 异步渲染编辑器
  //   this.$nextTick(() => {
  //     this.html = this.value
  //   })
  // },
  
  
  
  
  注释:这一步就是销毁富文本操作,属于官方文档的典型例子,你可以直接cv
  beforeDestroy () {
    const editor = this.editor
    if (editor == null) return
    editor.destroy()
  }
}
</script>




注释:再往下的css部分,就不解释啦,没啥太多东西
<style scoped>
.wang-editor {
  border: 1px solid #ccc;
  z-index: 3;
  position: relative;
}
.w-e-toolbar {
  z-index: 2 !important;
  border: solid 1px #ccc !important;
  border-top-left-radius: 6px;
  border-top-right-radius: 6px;
}
.w-e-text-container {
  z-index: 1 !important;
  border: solid 1px #ccc !important;
  border-top: none !important;
  border-bottom-left-radius: 6px;
  border-bottom-right-radius: 6px;
}
.useful-num {
  position: absolute;
  right: 6px;
  bottom: 10px;
  z-index: 99999;
  font-size: 12px;
  background: #fff;
  padding: 0 6px;
  height: 28px;
  line-height: 28px;
}
</style>

最后把调用方式写在下面

<wangEditor
   v-model="formItem.content"
   :editorVisible="modalVisible"
   @changeContent="changeContent"
></wangEditor>