封装 wangEditor5 组件,实现 v-model 指令

夹谷飞龙
2023-12-01

1. 目标

目标:在 wangEditor5 编辑器的基础上,封装成自己的编辑器组件,实现 v-model 指令。

2. 准备工作

  1. 下载并安装 wangEditor
npm i @wangeditor/editor @wangeditor/editor-for-vue -s
  1. 在组件 components 中添加 MyEditor.vue 文件,并初始化文件:
// MyEditor.vue 文件
<template>
    <div style="border: 1px solid #ccc;">
        <Toolbar
            style="border-bottom: 1px solid #ccc"
            :editor="editor"
            :defaultConfig="toolbarConfig"
            :mode="mode"
        />
        <Editor
            style="height: 500px; overflow-y: hidden;"
            v-model="html"
            :defaultConfig="editorConfig"
            :mode="mode"
            @onCreated="onCreated"
        />
    </div>
</template>

<script>
import Vue from 'vue'
import { Editor, Toolbar } from '@wangeditor/editor-for-vue'

export default Vue.extend({
    components: { Editor, Toolbar },
    data() {
        return {
            editor: null,
            html: '<p>hello</p>',
            toolbarConfig: { },
            editorConfig: { placeholder: '请输入内容...' },
            mode: 'default', // or 'simple'
        }
    },
    methods: {
        onCreated(editor) {
            this.editor = Object.seal(editor) // 一定要用 Object.seal() ,否则会报错
        },
    },
    beforeDestroy() {
        const editor = this.editor
        if (editor == null) return
        editor.destroy() // 组件销毁时,及时销毁编辑器
    }
})
</script>

<style src="@wangeditor/editor/dist/css/style.css"></style>

  1. 添加至父组件,并在自定义组件 my-editor 上添加 v-model 指令
// 父组件
<template>
  <div class="page">
        <my-editor v-model="msg"></my-editor>
  </div>
</template>

<script>
import MyEditor from '../components/Editor.vue'
export default {
    data(){
        return {
            msg:'andy'
        }
    },
    methods:{
    },
    components:{
        MyEditor
    }
}
</script>

以上完成了准备工作,但是 v-model 指令直接用在 自定义组件上是无法生效的,需要进一步处理。

3. 知识点

1). 语法糖:指的是在计算机语言中添加的某种语法,这种语法对语言的编译结果和功能并没有实际影响, 但是却能更方便程序员使用该语言。使用语法糖能够减少代码量、增加程序的可读性,从而减少程序代码出错的机会。

2). v-model 是一种语法糖,会被解析为一个名为 value 的自定义属性 :value 和一个名为 input 的自定义事件 @input

// 以下两行代码功能相同,实现了数据的双向绑定
<input type="text" v-model="msg">
<input type="text" :value="msg" @input="">

4. 业务实现

  1. 所以 my-editor 组件可以通过父传子的数据传输方式通过 props 接收 value 这个自定义属性
// MyEditor.vue 文件
props:{
        value:{
            type:String
        }
    },
  1. 通过 watch 侦听器监听 value 的变化,并赋值给 editor 编辑器
// MyEditor.vue 文件
// 父组件传过来的值通过 watch 进行监听,变化,就重新赋给编辑器
watch:{
    value(val) {
         const context = `<p>${val}</p>`
         this.editor.setHtml(context);
         // this.html = context; 和上一行代码实现效果一样
        }
    },
  1. 编辑器通过 onChange() 函数监听内容值的变化,发生变化,通过子传父的数据传输方式,将最新值传递给父组件中的 msg
    ( 子组件只需要通过 this.$emit() 将编辑器最新内容传递过去即可,父组件中 v-model 会自动接收@input 自定义事件,不需要额外添加 @input 事件)
// MyEditor.vue 文件
// onChange()函数: 当编辑器内容、选区变化时的回调函数 
// 将变化的数据通过 editor.getText() 得到,通过 子传父 this.$emit 传给父组件
onChange(editor) {
      const text = editor.getText();
      this.$emit('input',text);
},
  1. 为编辑器赋初值,一进页面,编辑器内容展现父组件传过来的值
// MyEditor.vue 文件
// 方法1 在 created 生命周期中赋初值
created() {
     // 赋初值
     this.html = `<p>${this.value}</p>`;
},

// 方法2 在 onCreate 函数中赋初值
onCreated(editor) {
        this.editor = Object.seal(editor) // 一定要用 Object.seal() ,否则会报错
        // onCreated 编辑器创建完毕时的回调函数。赋 父组件给的初值
        this.editor.setHtml(`<p>${this.value}</p>`);
},

5. 总结

在 wangEditor5 的基础上,添加 v-model 指令,封装为自定义组件。

其实在于对 v-model 的理解,v-model 可以看成 :value 和 @input ,子组件 my-editor 通过父传子 props 接收 value子传父 this.$emit( ) 传递最新值到 父组件 @input 。v-model 语法糖简化了父组件代码的书写。

6. 完整实现代码

// 父组件
<template>
  <div class="page">
    <input type="text" v-model="msg">
        <my-editor v-model="msg"></my-editor>
  </div>
</template>

<script>
import MyEditor from '../components/Editor.vue'
export default {
    data(){
        return {
            msg:'xindy'
        }
    },
    methods:{
    },
    components:{
        MyEditor
    }
}
</script>

// MyEditor.vue 文件
<template>
    <div style="border: 1px solid #ccc;">
        <Toolbar
            style="border-bottom: 1px solid #ccc"
            :editor="editor"
            :defaultConfig="toolbarConfig"
            :mode="mode"
        />
        <Editor
            style="height: 500px; overflow-y: hidden;"
            v-model="html"
            :defaultConfig="editorConfig"
            :mode="mode"
            @onCreated="onCreated"
            @onChange="onChange"
        />
    </div>
</template>


<script>
import Vue from 'vue'
import { Editor, Toolbar } from '@wangeditor/editor-for-vue'

export default Vue.extend({
    components: { Editor, Toolbar },
    data() {
        return {
            editor: null,
            html: '',
            toolbarConfig: { },
            editorConfig: { placeholder: '请输入内容...' },
            mode: 'default', // or 'simple'
        }
    },
    props:{
        value:{
            type:String
        }
    },
    methods: {
        onCreated(editor) {
            this.editor = Object.seal(editor) // 一定要用 Object.seal() ,否则会报错
            // onCreated 编辑器创建完毕时的回调函数。赋 父组件给的初值
             this.editor.setHtml(`<p>${this.value}</p>`);

        },
        // onChange 编辑器内容、选区变化时的回调函数 将变化的数据通过 editor.getText() 得到,通过 子传父 this.$emit 传给父组件
        onChange(editor) {
            const text = editor.getText();
            this.$emit('input',text);
        },
    },
    // 父组件传过来的值通过 watch 进行监听,变化,就重新赋给 编辑器
    watch:{
        value(val) {
            const context = `<p>${val}</p>`
            this.editor.setHtml(context);
            // this.html = context;
        }
    },
    created() {
        // 赋初值
        // this.html = `<p>${this.value}</p>`;
    },
    beforeDestroy() {
        const editor = this.editor
        if (editor == null) return
        editor.destroy() // 组件销毁时,及时销毁编辑器
    }
})
</script>

<style src="@wangeditor/editor/dist/css/style.css"></style>
 类似资料: