Appearance
vue中不要给外层组件定义事件名叫change
最近在做编辑器的小项目,一个简单的项目现在做的是遍地都是坑,一个接一个,真是让人目不暇接。
今天又遇到一个邪门的,因为CodeMirror
直接用的话代码比较多,所以进行一个简单的封装是必要的。
js
// CodeMirrorEditor组件
this.currentEditor = CodeMirror.fromTextArea(this.$refs.textarea, {
mode: "markdown",
...
});
this.currentEditor.on('change', (cm) => {
const editorValue = cm.getValue();
this.$emit('change', editorValue);
});
html
// 父级
<code-mirror-editor @change="handleChangeValue"></code-mirror-editor>
想法也非常简单,就是当数据变化的时候把编辑的内容向父级抛一下,然后通过内容解析插件把markdown解析成HTML代码。
就是这么简单的两段代码,让我排查了一上午的问题。
问题
编辑器在数据变化的时候,change事件持续被触发。但是当编辑器失去焦点的时候,change事件莫名其妙也被触发了一次。
最后触发的这次传递的editorValue
值是 textarea 这个DOM,直接导致父级解析HTML的插件报错。
那么问题到底出现在了哪儿?
排查
CodeMirrorEditor组件
在失去焦点的时候,绑定的change事件会不会触发。- 父级
handleChangeValue
方法在失去焦点的时候为什么会触发。
最后的解决方案非常简单,将emit
绑定的事件名从change修改为editorChange以后就ok了。
解决方案
- 不要给外层绑定change事件,会造成莫名其妙的错误。
- CodeMirror插件渲染的时候是基于
textarea
进行的渲染,而textarea
绑定change事件在修改的时候不触发,只有失去焦点的时候触发。(建议绑定input事件) - 给CodeMirror绑定
change
的时候可以通过判断的方式排除回传DOM的情况。
js
// CodeMirrorEditor组件
this.currentEditor = CodeMirror.fromTextArea(this.$refs.textarea, {
mode: "markdown",
...
});
this.currentEditor.on('change', (cm, change) => {
// 判断是否是内容变化(而不是 blur/事件触发)
if (!change || !change.from || !change.to) {
return; // 非内容变化,直接返回
}
const editorValue = cm.getValue();
this.$emit('editorChange', editorValue);
});
html
// 父级
<code-mirror-editor @editorChange="handleChangeValue"></code-mirror-editor>