vue 中我是这么封装 wangEditor 的

4,066 阅读1分钟

前言

wangEditor 是一款简洁、轻量级的富文本编辑器,但是在 vue 中使用时,如何把 wangEditor 封装成一个支持双向绑定(v-model)的组件对于一些同学来说还是比较茫然的,那么今天就来讲讲具体实现

一、理解 v-model 原理

首先我们来看下官方文档是怎么描述自定义 v-model 的:

如果还是不怎么明白,那么接下来我们来做一个对比实验:

从对比实验中得知,我们是不是可以粗浅的理解为 v-model="html" 等效于 v-bind:value="html" + v-on:input="(e) => { this.html = e.target.value }", 现在是不是好理解多了。

二、在组件中实现 v-model

虽然明白了 v-model 的原理,但是具体怎么封装呢?要点如下:

  • 在自定义组件中:
    • 我们需要在 props 中定义一个 value 来接受父组件传入的值
    • 然后我们需要使用 $emit('input', newValue) 将新值发送给父组件
  • 在父组件中:
    • 使用 v-model 进行双向绑定

/components/wang-editor.vue

第一步:按正常的使用步骤,在 .vue 文件中初始化使用 wangEditor

<template>
    <div ref="wang"></div>
</template>

<script>
import wangEditor from 'wangeditor'

export default {
    data() {
        return {
            editor: null
        }
    },
    mounted() {
        this.editor = new wangEditor(this.$refs.wang)
        this.editor.create()
    },
    beforeDestroy() {
        if (this.editor) {
            this.editor.destroy()
        }
    }
}
</script>

第二步:定义 props.value 接受父组件传值

export default {
    props: {
    	value: {
            type: String,
            default: ''
        }
    },
    mounted() {
        this.editor = new wangEditor(this.$refs.wang)
        this.editor.create()
        
        // 如果父组件传入了值,将值赋值给编辑器
        if (this.value.length) {
            this.editor.txt.html(this.value)
        }
    },
}

第三步:使用 $emit('input', newVal) 将新值发送到父组件

export default {
    mounted() {
        this.editor = new wangEditor(this.$refs.wang)

        // v-model 双向绑定:把值发送到父组件
        this.editor.txt.eventHooks.changeEvents.push(() => {
            this.$emit('input', this.editor.$textElem.html())
        })

        this.editor.create()
        
        // 如果父组件传入了值,将值赋值给编辑器
        if (this.value.length) {
            this.editor.txt.html(this.value)
        }
    },
}

三、最终代码

v-model 基础上继续封装,然后我们看一下比较完善的代码

/components/wang-editor.vue

<template>
    <div ref="wang"></div>
</template>

<script>
import wangEditor from 'wangeditor'

export default {
    props: {
        // wangEditor 的配置
        option: {
            type: Object,
            default() {
                return {}
            }
        },
        // v-model 双向绑定:接受父组件的值
        // https://cn.vuejs.org/v2/guide/components-custom-events.html#%E8%87%AA%E5%AE%9A%E4%B9%89%E7%BB%84%E4%BB%B6%E7%9A%84-v-model
        value: {
            type: String,
            default: ''
        }
    },
    watch: {
        // 监听父组件传值,将值赋值给编辑器
        value(n) {
            if (this.editor && n !== this.temp) {
                this.editor.$textElem.html(n)
            }
        }
    },
    data() {
        return {
            temp: '',   // 缓存当前的编辑器中的值,用于与父组件传入值进行对比
            editor: null
        }
    },
    mounted() {
        this.editor = new wangEditor(this.$refs.wang)

        // 合并配置项
        Object.assign(this.editor.config, this.option)

        // v-model 双向绑定:把值发送到父组件(不占用用户的 onchange 配置)
        this.editor.txt.eventHooks.changeEvents.push(() => {
            this.temp = this.editor.$textElem.html()
            this.$emit('input', this.temp)
        })

        this.editor.create()
        
        if (this.value.length) {
            this.editor.txt.html(this.value)
        }
    },
    beforeDestroy() {
        if (this.editor) {
            this.editor.destroy()
        }
    }
}
</script>

四、wang-editor 组件使用

<template>
  <div id="app">
    <wang-editor :option="option" v-model="html"></wang-editor>
  </div>
</template>

<script>
import WangEditor from './components/wang-editor.vue'

export default {
  name: 'App',
  components: {
    WangEditor
  },
  data() {
    return {
      option: {
        height: 500
      },
      html: '<p>like</p>'
    }
  },
}
</script>