跳转到内容

富文本编辑器

刀刀
0字
0分钟
2026/8/16

Tinymce

注意事项

可安装对应的 Vue/React 组件直接作为组件使用,也可直接下载进行原生操作。

若报找不到文件错误,需将 tinymce 复制到 public 文件夹内。

安装

安装 Vue 模块的组件:

bash
pnpm i @tinymce/tinymce-vue

若为 React 项目则改为 @tinymce/tinymce-react

安装 tinymce 包:

bash
pnpm i tinymce

使用

引入富文本编辑器:

js
import tinymce from 'tinymce'

通过 tinymce.init() 方法的 selector 属性传入一个 id 选择器或类选择器,将富文本编辑器挂载到目标元素上。

注意

由于需获取元素,必须等待元素加载完毕。Vue2 项目中将方法写在 mounted 生命周期上,Vue3 项目将方法写在 onMounted 钩子上。

vue
<script setup>
import { onMounted } from 'vue'

onMounted(() => {
  tinymce.init({
    selector: '#mytinymce',
  })
})
</script>

<template>
  <div id="mytinymce"></div>
</template>

复制文件到 public 目录

编辑器加载后会请求其 JavaScript 文件和 CSS 文件,需将 node_modules 内 tinymce 的完整内容复制到 public 目录中,否则会报错。

项目打包上线后,同样需将这些资源放置到服务器的静态资源目录中。

常见界面需求

隐藏不需要的部分

  • 组件方式:

    vue
    <script setup>
    import { Editor } from '@tinymce/tinymce-vue'
    
    const initObj = {
      selector: '#mytinymce',
      menubar: false,
      toolbar: true,
      statusbar: true,
    }
    </script>
    
    <Editor :init="initObj" />
  • 直接使用 tinymce 方式:

    menubar 控制菜单,toolbar 控制工具栏,statusbar 控制状态栏。设为 false 即可隐藏对应部分。

    js
    onMounted(() => {
      tinymce.init({
        selector: '#mytinymce',
        menubar: false,
        toolbar: true,
        statusbar: true,
      })
    })

自定义样式

skin 控制皮肤,可通过 skin_url 导入自定义皮肤;通过 content_css 定义内容区域样式;通过 icons_url 导入图标。

皮肤位于 public 目录中 skin 文件夹下的 UI 子目录内,填写对应文件夹名称即可,本质是加载该目录下的 skin.css 文件。

自定义皮肤则在 public 下找到自定义文件夹(如示例中的 myskin),其中包含 content.min.cssskin.min.css 文件。

js
onMounted(() => {
  tinymce.init({
    selector: '#mytinymce',
    menubar: false,
    toolbar: true,
    statusbar: true,
    skin: 'oxide',
    skin_url: 'myskin',
  })
})

中文化

下载中文语言包,将 langs 文件夹解压到 public 目录内,在配置项中通过 language 属性指定语言文件名。

js
onMounted(() => {
  tinymce.init({
    selector: '#mytinymce',
    menubar: false,
    toolbar: true,
    statusbar: true,
    skin: 'oxide',
    skin_url: 'myskin',
    language: 'zh-Hans',
  })
})

进阶需求

获取内容

通过 tinymce.activeEditor.getContent() 获取 HTML 富文本内容。若需获取纯文本,传入 { format: 'text' }

设置内容

通过 tinymce.activeEditor.setContent() 设置内容,参数为 HTML 字符串,如 '<p>daodao</p>'

也可对选中内容执行替换:先选中目标内容,再调用 tinymce.activeEditor.selection.setContent('hello') 将其替换为指定内容。

二次开发

自带插件

安装目录下的 plugins 文件夹包含内置插件,如 imagelinkcodetable 等。

使用方式:在 plugins 属性中声明插件名称,在 toolbar 中添加对应按钮。

js
onMounted(() => {
  tinymce.init({
    selector: '#mytinymce',
    menubar: false,
    plugins: 'code image',
    toolbar:
      'undo redo | styles | bold italic | fontfamily fontsize fontcolor | code image',
    statusbar: true,
    skin: 'oxide',
    skin_url: 'myskin',
    language: 'zh-Hans',
  })
})

引入第三方插件时,将其复制到 plugins 文件夹下,并在 plugins 属性和 toolbar 属性中配置文件名即可。

开发自定义工具栏按钮

通过 setup 函数注册自定义按钮。该函数的形参为 Editor 对象,调用 editor.ui.registry.addButton 方法创建按钮。

该方法接收两个参数:

  • 参数一:按钮名称(字符串)
  • 参数二:配置对象,包含 icon(图标)、tooltip(提示文本)、onAction(点击回调函数)
js
onMounted(() => {
  tinymce.init({
    selector: '#mytinymce',
    menubar: false,
    plugins: 'code image',
    toolbar:
      'undo redo | styles | bold italic | fontfamily fontsize fontcolor | code image',
    statusbar: true,
    skin: 'oxide',
    skin_url: 'myskin',
    language: 'zh-Hans',
    setup(editor) {
      // 将选中内容标红
      editor.ui.registry.addButton('red', {
        icon: 'highlight-bg-color',
        tooltip: '字体标红',
        onAction: () => {
          const text = editor.selection.getContent()
          editor.selection.setContent(`<span class="red-text">${text}</span>`)
        },
      })
      // 将选中内容展示在页面中
      editor.ui.registry.addButton('show', {
        icon: 'preview',
        tooltip: '展示选中内容',
        onAction: () => {
          const text = editor.selection.getContent()
          popShow.value = true
          popValue.value = text
        },
      })
    },
    content_css: '/mycontent.css',
  })
})

大段 CSS 样式可在 public 文件夹中创建独立的样式文件进行定义。

vue-quill-editor

详细文档参考:vue-quill-editor

安装

bash
pnpm install vue-quill-editor

该插件基于 Quill,安装时会自动引入所需依赖,无需单独安装 Quill

引入

  • 全局引入:

    main.js 中引入插件:

    js
    import VueQuillEditor from 'vue-quill-editor'
    import 'quill/dist/quill.core.css'
    import 'quill/dist/quill.snow.css'
    import 'quill/dist/quill.bubble.css'
    
    Vue.use(VueQuillEditor)
  • 局部引入:

    js
    import 'quill/dist/quill.core.css'
    import 'quill/dist/quill.snow.css'
    import 'quill/dist/quill.bubble.css'
    import { quillEditor } from 'vue-quill-editor'
    
    export default {
      components: { quillEditor },
    }

全局引入使用默认导出的 VueQuillEditor,局部引入使用具名导出的 quillEditor。源码中同时提供了默认导出和包含 quillEditor 的具名导出。

使用

vue
<template>
  <div class="local-quill-editor">
    <quill-editor
      ref="myLQuillEditor"
      v-model="content"
      :options="editorOption"
      class="editor"
      @blur="onEditorBlur"
      @focus="onEditorFocus"
      @change="onEditorChange"
    />
  </div>
</template>

<script>
const toolbarOptions = [
  ['bold', 'italic', 'underline', 'strike'],
  ['blockquote', 'code-block'],
  [{ header: 1 }, { header: 2 }],
  [{ list: 'ordered' }, { list: 'bullet' }],
  [{ script: 'sub' }, { script: 'super' }],
  [{ indent: '-1' }, { indent: '+1' }],
  [{ direction: 'rtl' }],
  [{ size: ['small', false, 'large', 'huge'] }],
  [{ header: [1, 2, 3, 4, 5, 6, false] }],
  [{ color: [] }, { background: [] }],
  [{ font: [] }],
  [{ align: [] }],
  ['clean'],
  ['image', 'video'],
]

export default {
  name: 'LocalQuillEditor',
  data() {
    return {
      content: '',
      editorOption: {
        modules: {
          toolbar: toolbarOptions,
        },
        theme: 'snow',
        placeholder: '请输入正文',
      },
    }
  },
  methods: {
    onEditorBlur(e) {
      console.log('onEditorBlur: ', e)
    },
    onEditorFocus(e) {
      console.log('onEditorFocus: ', e)
    },
    onEditorChange(e) {
      console.log('onEditorChange: ', e)
    },
  },
}
</script>

<style scoped lang="scss">
.editor {
  height: 500px;
}
</style>

图片上传到服务器

默认情况下,富文本编辑器将图片转为 Base64 格式内联存储。单张图片转码后约 234KB,少量图片即可导致数据库字段溢出。因此需将图片上传至服务器,使用返回的 URL 作为渲染路径。

官方文档:quill-image-extend-module

安装

bash
pnpm install quill-image-extend-module --save-dev

导入

js
import { quillEditor, Quill } from 'vue-quill-editor'
import { container, ImageExtend, QuillWatch } from 'quill-image-extend-module'

Quill.register('modules/ImageExtend', ImageExtend)

使用

vue
<template>
  <div class="quill-wrap">
    <quill-editor
      v-model="content"
      ref="myQuillEditor"
      :options="editorOption"
    />
  </div>
</template>

<script>
import { quillEditor, Quill } from 'vue-quill-editor'
import { container, ImageExtend, QuillWatch } from 'quill-image-extend-module'

Quill.register('modules/ImageExtend', ImageExtend)

export default {
  components: { quillEditor },
  data() {
    return {
      content: '',
      editorOption: {
        modules: {
          ImageExtend: {
            loading: true,
            name: 'img',
            action: updateUrl,
            response: (res) => res.info,
          },
          toolbar: {
            container: container,
            handlers: {
              image: function () {
                QuillWatch.emit(this.quill.id)
              },
            },
          },
        },
      },
    }
  },
}
</script>

完整配置项

js
editorOption: {
  modules: {
    ImageExtend: {
      name: "img",        // 图片参数名
      size: 3,            // 图片大小限制,单位 MB
      action: updateUrl,  // 服务器上传地址,为空则使用 Base64
      response: (res) => res.info, // 返回图片地址
      headers: (xhr) => {},       // 设置请求头
      sizeError: () => {},        // 超出大小回调
      start: () => {},            // 开始上传回调
      end: () => {},              // 上传结束回调
      error: () => {},            // 上传失败回调
      success: () => {},          // 上传成功回调
      change: (xhr, formData) => {} // 每次选择图片触发
    },
    toolbar: {
      container: container,
      handlers: {
        image: function () {
          QuillWatch.emit(this.quill.id);
        },
      },
    },
  },
}

注意事项

由于不同服务器返回的数据结构不同,response 函数必须正确返回图片地址。

示例:若服务器返回格式为:

json
{
  "code": 200,
  "status": true,
  "result": {
    "img": "http://placehold.it/xx.jpg"
  }
}

则配置为:

js
response: (res) => res.result.img,

与其他模块配合使用(以 resize-module 为例)

vue
<template>
  <div class="quill-wrap">
    <quill-editor
      v-model="content"
      ref="myQuillEditor"
      :options="editorOption"
    />
  </div>
</template>

<script>
import { quillEditor, Quill } from 'vue-quill-editor'
import { container, ImageExtend, QuillWatch } from 'quill-image-extend-module'
import ImageResize from 'quill-image-resize-module'

Quill.register('modules/ImageExtend', ImageExtend)
Quill.register('modules/ImageResize', ImageResize)

export default {
  components: { quillEditor },
  data() {
    return {
      content: '',
      editorOption: {
        modules: {
          ImageResize: {},
          ImageExtend: {
            name: 'img',
            size: 2,
            action: updateUrl,
            headers: (xhr) => {},
            response: (res) => res.info,
          },
          toolbar: {
            container: container,
            handlers: {
              image: function () {
                QuillWatch.emit(this.quill.id)
              },
            },
          },
        },
      },
    }
  },
}
</script>

贡献者

The avatar of contributor named as 刀刀 刀刀
The avatar of contributor named as duyidao duyidao
The avatar of contributor named as v_duyilin v_duyilin
The avatar of contributor named as 平安喜乐 平安喜乐

页面历史

刀刀博客累计访客 人;文档累计访问量共