用于 Vue React

如果是第一次使用,请先通过 快速开始 了解基本使用。

注意

本文只介绍各个框架的组件接入,实际使用时还需要很多配置、API 。
所以,阅读下文代码时,如遇到 工具栏配置 编辑器配置 菜单配置 editor API 等字眼,请继续参考其他文档:

Vue2

可参考博客 50 行代码 Vue2.6 中使用富文本编辑器open in new window

安装

需安装 @wangeditor/editor@wangeditor/editor-for-vue,可参考这里

基本使用

模板

<template>
    <div style="border: 1px solid #ccc;">
        <Toolbar
            style="border-bottom: 1px solid #ccc"
            :editorId="editorId"
            :defaultConfig="toolbarConfig"
            :mode="mode"
        />
        <Editor
            style="height: 500px"
            :editorId="editorId"
            :defaultConfig="editorConfig"
            :defaultContent="getDefaultContent"
            :defaultHtml="defaultHtml"
            :mode="mode"
        />
        <!-- 注意: defaultContent (JSON 格式) 和 defaultHtml (HTML 格式),二选一 -->
    </div>
</template>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

script

<script>
import Vue from 'vue'
import '@wangeditor/editor/dist/css/style.css'
import { Editor, Toolbar, getEditor, removeEditor } from '@wangeditor/editor-for-vue'
import cloneDeep from 'lodash.clonedeep'

export default Vue.extend({
    components: { Editor, Toolbar },
    data() {
        return {
            editorId: `w-e-${Math.random().toString().slice(-5)}`, //【注意】编辑器 id ,要全局唯一
            toolbarConfig: {},
            editorConfig: { placeholder: '请输入内容...' },
            mode: 'default', // or 'simple'

            // defaultContent (JSON 格式) 和 defaultHtml(HTML 格式)二选一
            defaultContent: [
                { type: 'paragraph', children: [{ text: '一行文字' }] }
            ],
            defaultHtml: '<p>hello</p>',
        }
    },
    computed: {
        getDefaultContent() {
            return cloneDeep(this.defaultContent) //【注意】深度拷贝 defaultContent ,否则会报错
        }
    },
    beforeDestroy() {
        const editor = getEditor(this.editorId)
        if (editor == null) return

        // 【注意】组件销毁时,及时销毁编辑器
        editor.destroy()
        removeEditor(this.editorId)
    }
})
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37

TIP

  • editorId 要全局唯一,不可重复
  • defaultContent(JSON 格式) 和 defaultHtml(HTML 格式),二选一
  • 如果使用 defaultContent ,要使用 computed 和深拷贝,否则会报错
  • 组件销毁时,要及时销毁编辑器

记得引入 style

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

异步设置内容

例如,Ajax 异步获取内容,然后设置到编辑器中。注意,不可以直接修改 defaultContentdefaultHtml ,而是要异步渲染组件

data 中定义一个属性 isEditorShow: false,在 Ajax 结束时设置为 true

data() {
    return {
        // 省略其他属性...
        isEditorShow: false
    }
},
mounted() {
    // 模拟 ajax 请求,异步渲染编辑器
    setTimeout(() => {
        // defaultContent (JSON 格式) 和 defaultHtml(HTML 格式)二选一
        this.defaultContent = [
            { type: 'paragraph', children: [{ text: 'ajax 异步获取的内容' }] }
        ]
        this.defaultHtml = '<p>ajax&nbsp;异步获取的内容</p>'

        this.isEditorShow = true
    }, 1000)
},
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

模板中,根据 isEditorShow 来渲染组件

<template>
    <div>
        <div v-if="isEditorShow" style="border: 1px solid #ccc;">
            <Toolbar ... />
            <Editor ... />
        </div>
        <p v-else>loading...</p>
    </div>
</template>
1
2
3
4
5
6
7
8
9

配置

可通过 toolbarConfigeditorConfig 来修改菜单栏和编辑器的配置,详细文档参考

【注意】,编辑器配置中 onXxx 格式的生命周期函数,必须通过 Vue 事件来传递,不可以放在 editorConfig,例如:

<template>
    <div style="border: 1px solid #ccc;">
        <Toolbar ... />
        <Editor
            @onCreated="onCreated"
            @onChange="onChange"
            @onDestroyed="onDestroyed"
            @onMaxLength="onMaxLength"
            @onFocus="onFocus"
            @onBlur="onBlur"
            @customAlert="customAlert"
            @customPaste="customPaste"
        />
    </div>
</template>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
methods: {
    onCreated(editor) { console.log('onCreated', editor) },
    onChange(editor) { console.log('onChange', editor.children) },
    onDestroyed(editor) { console.log('onDestroyed', editor) },
    onMaxLength(editor) { console.log('onMaxLength', editor) },
    onFocus(editor) { console.log('onFocus', editor) },
    onBlur(editor) { console.log('onBlur', editor) },
    customAlert(info: string, type: string) { window.alert(`customAlert in Vue demo\n${type}:\n${info}`) },
    customPaste(editor, event, callback) {
        console.log('ClipboardEvent 粘贴事件对象', event)

        // 自定义插入内容
        editor.insertText('xxx')

        // 返回值(注意,vue 事件的返回值,不能用 return)
        callback(false) // 返回 false ,阻止默认粘贴行为
        // callback(true) // 返回 true ,继续默认的粘贴行为
    },
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

调用 API

当编辑器渲染完成之后,通过 getEditor(this.editorId) 获取 editor 实例,即可调用它的 API 。参考 编辑器 API

<template>
    <div>
        <button @click="insertText">insert text</button>
        <div style="border: 1px solid #ccc;">
            <Toolbar .../>
            <Editor .../>
        </div>
    </div>
</template>
1
2
3
4
5
6
7
8
9
methods: {
    insertText() {
        const editor = getEditor(this.editorId) // 获取 editor 实例(必须等它渲染完成)
        if (editor == null) return

        // 调用 editor 属性和 API
        editor.insertText('一段文字')
        console.log(editor.children)
    },
},
mounted() {
    this.$nextTick(() => {
        const editor = getEditor(this.editorId) // 获取 editor 实例(必须等它渲染完成)
        if (editor == null) return
        console.log('getEditor on mounted', editor)
    })
},
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

Vue3

可参考博客50 行代码 Vue3 中使用富文本编辑器open in new window

安装

需安装 @wangeditor/editor@wangeditor/editor-for-vue@next,可参考这里

基本使用

模板

<template>
    <div style="border: 1px solid #ccc">
      <Toolbar
        :editorId="editorId"
        :defaultConfig="toolbarConfig"
        :mode="mode"
        style="border-bottom: 1px solid #ccc"
      />
      <Editor
        :editorId="editorId"
        :defaultConfig="editorConfig"
        :defaultContent="getDefaultContent"
        :defaultHtml="defaultHtml"
        :mode="mode"
        style="height: 500px"
      />
      <!-- 注意: defaultContent (JSON 格式) 和 defaultHtml (HTML 格式) ,二选一 -->
    </div>
</template>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

script

<script>
import { computed, onBeforeUnmount, ref } from 'vue'
import { Editor, Toolbar, getEditor, removeEditor } from '@wangeditor/editor-for-vue'
import cloneDeep from 'lodash.clonedeep'

export default {
  components: { Editor, Toolbar },
  setup() {
    const editorId = `w-e-${Math.random().toString().slice(-5)}` //【注意】编辑器 id ,要全局唯一

    // defaultContent (JSON 格式) 和 defaultHtml (HTML 格式) ,二选一
    const defaultHtml = '一行文字'
    const defaultContent = [
        { type: 'paragraph', children: [{ text: '一行文字' }] }
    ]
    const getDefaultContent = computed(() => cloneDeep(defaultContent)) // 注意,要深拷贝 defaultContent ,否则报错

    const toolbarConfig = {}
    const editorConfig = { placeholder: '请输入内容...' }

    // 组件销毁时,也及时销毁编辑器
    onBeforeUnmount(() => {
        const editor = getEditor(editorId)
        if (editor == null) return

        editor.destroy()
        removeEditor(editorId)
    })

    return {
      editorId,
      mode: 'default',
      defaultHtml,
      getDefaultContent,
      toolbarConfig,
      editorConfig,
    };
  }
}
</script>    
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40

TIP

  • editorId 要全局唯一,不可重复
  • defaultContent (JSON 格式) 和 defaultHtml (HTML 格式) ,二选一
  • 如果选择了 defaultContent ,要使用 computed 和深拷贝,否则会报错
  • 组件销毁时,要及时销毁编辑器

记得引入 style

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

异步设置内容

例如,Ajax 异步获取内容,然后设置到编辑器中。注意,不可以直接修改 defaultContentdefaultHtml ,而是要异步渲染组件

可以使用 Vue3 ref 定义一个响应式变量 isEditorShow = false,在 Ajax 结束时设置为 true

// const defaultHtml = '一行文字'
const defaultHtml = ref('')

// const defaultContent = []
// const getDefaultContent = computed(() => cloneDeep(defaultContent))
const defaultContent = ref([])
const getDefaultContent = computed(() => cloneDeep(defaultContent.value))

const isEditorShow = ref(false)

// 模拟 ajax 异步获取内容
setTimeout(() => {
    // defaultContent (JSON 格式) 和 defaultHtml (HTML 格式) ,二选一
    defaultHtml.value = 'ajax&nbsp;异步获取的内容'
    defaultContent.value =  [
        { type: "paragraph", children: [{ text: "ajax 异步获取的内容" }] },
    ]

    isEditorShow.value = true
}, 1000)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

然后 template 根据 isEditorShow 异步渲染编辑器

<template>
    <div>
        <div v-if="isEditorShow" style="border: 1px solid #ccc">
            <Toolbar ... />
            <Editor ... />
        </div>
        <p v-else>loading</p>
    </div>
</template>
1
2
3
4
5
6
7
8
9

配置

可通过 toolbarConfigeditorConfig 来修改菜单栏和编辑器的配置,详细文档参考

【注意】,编辑器配置中 onXxx 格式的生命周期函数,必须通过 Vue 事件来传递,不可以放在 editorConfig,例如:

<template>
    <div style="border: 1px solid #ccc">
      <Toolbar ... />
      <Editor
        @onCreated="handleCreated"
        @onChange="handleChange"
        @onDestroyed="handleDestroyed"
        @onFocus="handleFocus"
        @onBlur="handleBlur"
        @customAlert="customAlert"
        @customPaste="customPaste"
      />
    </div>
</template>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
const handleCreated = (editor) => { console.log('created', editor) }
const handleChange = (editor) => { console.log('change:', editor.children) }
const handleDestroyed = (editor) => { console.log('destroyed', editor) }
const handleFocus = (editor) => { console.log('focus', editor) }
const handleBlur = (editor) => { console.log('blur', editor) }
const customAlert = (info, type) => { alert(`【自定义提示】${type} - ${info}`) }
const customPaste = (editor, event, callback) => {
    console.log('ClipboardEvent 粘贴事件对象', event)

    // 自定义插入内容
    editor.insertText('xxx')

    // 返回值(注意,vue 事件的返回值,不能用 return)
    callback(false) // 返回 false ,阻止默认粘贴行为
    // callback(true) // 返回 true ,继续默认的粘贴行为
}

return {
    // 省略其他 ...

    handleCreated,
    handleChange,
    handleDestroyed,
    handleFocus,
    handleBlur,
    customAlert,
    customPaste
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28

调用 API

当编辑器渲染完成之后,通过 getEditor(editorId) 获取 editor 实例,即可调用它的 API 。参考 编辑器 API

<template>
    <div>
        <button @click="insertText">insert text</button>
        <div style="border: 1px solid #ccc">
            <Toolbar ... />
            <Editor ... />
        </div>
    </div>
</template>
1
2
3
4
5
6
7
8
9
const insertText = () => {
    const editor = getEditor(editorId) // 获取 editor ,必须等待它渲染完之后
    if (editor == null) return

    editor.insertText('hello world') // 执行 editor API
}

return {
    // 省略其他 ...

    insertText
}
1
2
3
4
5
6
7
8
9
10
11
12

React

可参考博客 50 行代码 React Hooks 中使用富文本编辑器open in new window

安装

需安装 @wangeditor/editor@wangeditor/editor-for-react,可参考这里

基本使用

以下代码使用 React Hooks 。如使用 React class 组件,可参考这里open in new window

import React, { useState, useEffect } from 'react'
import '@wangeditor/editor/dist/css/style.css'
import { Editor, Toolbar } from '@wangeditor/editor-for-react'

function MyEditor() {
    const [editor, setEditor] = useState(null) // 存储 editor 实例

    // `defaultContent` (JSON 格式) 和 `defaultHtml` (HTML 格式) 二选一
    const defaultContent = [
        { type: "paragraph", children: [{ text: "一行文字" }], }
    ]
    // const defaultHtml = '<p>一行文字</p>'

    const toolbarConfig = { }
    const editorConfig = {
        placeholder: '请输入内容...',
        onCreated(editor) { setEditor(editor) } // 记录下 editor 实例,重要!
    }

    // 及时销毁 editor ,重要!
    useEffect(() => {
        return () => {
            if (editor == null) return
            editor.destroy()
            setEditor(null)
        }
    }, [editor])

    return (
        <>
            <div style={{ border: '1px solid #ccc', zIndex: 100}}>
                <Toolbar
                    editor={editor}
                    defaultConfig={toolbarConfig}
                    mode="default"
                    style={{ borderBottom: '1px solid #ccc' }}
                />
                <Editor
                    defaultConfig={editorConfig}
                    defaultContent={defaultContent}
                    // defaultHtml={defaultHtml}
                    mode="default"
                    style={{ height: '500px' }}
                />
            </div>
        </>
    )
}

export default MyEditor
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50

异步设置内容

例如,Ajax 异步获取内容,然后设置到编辑器中。注意,不可以直接修改 defaultContentdefaultHtml ,而是要异步渲染组件

可定义一个 state isEditorShow = false ,等 Ajax 结束时设置为 true

// `defaultContent` (JSON 格式) 和 `defaultHtml` (HTML 格式) 二选一
const [defaultContent, setDefaultContent] = useState([])
// const [defaultHtml, setDefaultHtml] = useState('')

const [isEditorShow, setIsEditorShow] = useState(false)

// 模拟 ajax 异步请求
setTimeout(() => {
    setDefaultContent([
        { type: "paragraph", children: [{ text: "ajax 异步获取的内容" }] }
    ])
    // setDefaultHtml('<p>ajax&nbsp;异步获取的内容</p>')

    setIsEditorShow(true)
}, 1000)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

JSX 中根据 isEditorShow 异步渲染组件

return (
    <>
        {isEditorShow && <div style={{ border: '1px solid #ccc', zIndex: 100}}>
            <Toolbar ... />
            <Editor ... />
        </div>}
        {!isEditorShow && <p>loading</p>}
    </>
)
1
2
3
4
5
6
7
8
9

配置

可通过 toolbarConfigeditorConfig 来修改菜单栏和编辑器的配置,详细文档参考

调用 API

当编辑器渲染完成之后,即可调用它的 API 。参考 编辑器 API

function insertText() {
    if (editor == null) return
    console.log(editor.insertText('hello'))
}

return (
    <>
        <button onClick={insertText}>insert text</button>
        <div style={{ border: '1px solid #ccc', zIndex: 100}}>
            <Toolbar ... />
            <Editor ... />
        </div>
    </>
)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
Last Updated:
Contributors: 王福朋