最近接手老项目,有一个小需求变更——添加富文本功能,老项目组件库里有wangEditor,为了便捷性,干脆直接用。
结果问题来了,控制台飘红,解决之后,又飘红,再次解决,再次出现新的飘红,所以就很无奈。(严重怀疑这个组件是某某练习的半成品小demo...)
人在遇到报错、无脑改bug的时候,思维其实已经僵化了,哪怕一开始路线就是错的,也会不觉,一条路走到黑。
后面干脆照着文档,自己封一个组件,用起来还不错,对于正在做富文本的战友们,如果下面我这套和你适用,可以直接cv。
另外我会针对之前走的弯路,各种千奇百怪的bug,做个完整复盘,把修复方式统统放在下面,共勉!
先放两个较为重点的报错,也是很多人经常遇到的:
第一种:
通常情况下,富文本会放在弹窗类似组件中,进行显示or隐藏,出现这个问题的原因,就是因为弹窗关了,但富文本没被销毁+初始化,所以为了强制执行,你可以在弹框上加个v-show,后面的开关,可以和弹窗保持一致,用它的就行。
注意,这里为什么不写v-if?(会引出下一个bug),富文本经常报错的原因,就是操作不当,导致是否注销、执行先后问题、初始化等等状况频出。
第二个bug来了:
看了一圈解决方式,我在思考,搞个富文本,难道我还得继续去看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,把我的代码放一下,下图为富文本示意图:
对于右下角最大字数限制的问题,我想说一下,千万别截取,网上部分博客都给人带跑偏了,但凡认真看一看官方文档,都不会这么做。
截取是可以,但最后搞不好,就会出现上面的问题。
代码奉上(中间我会把我的思路写上)
注释部分,左边都会有箭头,便于大家查看
<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>