Code前端首页关于Code前端联系我们

一、Vue3 项目里怎么快速引入 Quill 富文本编辑器?

terry 2周前 (10-03) 阅读数 109 #Vue
文章标签 Vue3;Quill

做前端项目时,富文本编辑是很常见的需求,像博客发布、商品描述、后台配置这些场景都得用到,Vue3 项目里想快速搞懂 Quill 富文本编辑器的用法?这篇从基础集成到进阶扩展,把常见问题和实操步骤拆明白,新手也能跟着一步步搭起来~

想在 Vue3 里用 Quill,得先选对适配的封装库,现在社区里常用的是 vue-quill-editor-next,它专门适配 Vue3 生态,能和 Composition API、Teleport 这些新特性友好配合,步骤分三步:

装依赖

打开终端,切到自己 Vue3 项目的根目录,执行安装命令:

npm install vue-quill-editor-next quill

这里要装两个包:vue-quill-editor-next 是 Vue3 组件封装层,quill 是富文本编辑器的核心库,缺一不可。

注册组件

可以全局注册,也可以局部在组件里引入,推荐局部引入,减少打包体积,比如在需要用的组件里:

<script setup>
import { QuillEditor } from 'vue-quill-editor-next'
import 'quill/dist/quill.snow.css' // 引入默认主题样式(snow主题,还有bubble主题可选)
</script>
<template>
  <QuillEditor v-model="content" />
</template>
<script>
export default {
  data() {
    return {
      content: '' // 初始内容,支持HTML字符串或Delta格式(Quill的结构化数据格式)
    }
  }
}
</script>

这里注意 v-model 绑定的 content,默认是 HTML 格式的字符串,如果想改成 Delta 格式(更适合复杂操作和协作),可以加个 type="delta" 的 prop。

基础配置(可选)

刚引入的编辑器用的是默认配置,比如工具栏按钮、默认样式,如果想改基础配置,通过 options prop 传参:

<QuillEditor 
  v-model="content" 
  :options="{
    theme: 'snow', // 主题,snow是带工具栏,bubble是气泡式(选中文本才显示工具)
    placeholder: '请输入内容...', // 占位符
    toolbar: [['bold', 'italic']] // 只保留加粗、斜体按钮,自定义工具栏
  }" 
/>

这样就能快速把 Quill 加到项目里,页面上会出现带基础格式的富文本编辑器~

怎么自定义 Quill 的工具栏?默认按钮不够用啊!

Quill 的工具栏是「可配置化」的,默认的按钮(比如加粗、列表)满足不了需求时,像加「格式刷」「清除格式」「自定义颜色选择器」这些,得自己改配置 + 写逻辑,分两步搞:

配置 toolbar 结构

Quill 的 toolbar 配置是个数组,每个子数组代表「一行工具」,数组元素是工具名称('bold')或自定义对象,举个例子,想加「清除格式」按钮:

<QuillEditor 
  :options="{
    toolbar: [
      ['bold', 'italic', 'underline'], // 第一行:加粗、斜体、下划线
      ['clean'] // 第二行:清除格式(Quill 内置的 clean 工具)
    ]
  }" 
/>

但如果是完全自定义的按钮(插入我的名片」),得用对象形式配置,还要绑定点击事件:

<QuillEditor 
  :options="{
    toolbar: {
      container: [
        ['bold'],
        [{ header: [1, 2, false] }], // 多级标题,false 代表普通文本
        ['custom-button'] // 自定义按钮的标识
      ],
      handlers: { // 给自定义按钮写点击逻辑
        'custom-button': function() {
          // this 指向 Quill 实例
          const quill = this
          // 比如插入一段自定义内容
          quill.insertText(quill.getSelection().index, '我的名片:XXX', 'link', 'https://xxx.com')
        }
      }
    }
  }" 
/>

这里 handlers 里的键要和 container 里的自定义按钮标识对应,函数里的 this 是 Quill 实例,能调它的 API(insertText getSelection)。

改工具栏样式

默认工具栏样式可能和项目 UI 不搭,得自己写 CSS,Quill 的工具栏 DOM 结构有固定类名,.ql-toolbar 是外层,.ql-formats 是每个工具组,可以直接覆盖样式:

/* 隐藏默认的边框 */
.ql-toolbar {
  border: none !important;
}
/* 自定义按钮 hover 样式 */
.ql-toolbar button:hover {
  background-color: #f0f0f0;
}

要是用了 UI 库(Element Plus)的图标,还能把工具栏按钮换成图标:

<QuillEditor 
  :options="{
    toolbar: {
      container: [
        [{ icon: 'Bold' }] // 假设用 Element Plus 的 Icon 组件,需要自己封装替换
      ]
    }
  }" 
/>

这种情况得自定义工具栏组件,把 Quill 的默认按钮换成 UI 库的图标,稍微复杂点,核心思路是「用自定义 DOM 代替默认工具栏,再通过 Quill API 触发格式变化」~

图片直接转base64太占空间,怎么改成上传到服务器?

Quill 默认把图片转成 base64 存到内容里,大图片会让内容体积爆炸,还不利于服务器存储,得改成「选图后上传到自己服务器,再把返回的 URL 插入编辑器」,步骤如下:

禁用默认图片处理逻辑

Quill 处理图片的逻辑在 image 模块里,先把默认逻辑关了:

<QuillEditor 
  :options="{
    modules: {
      image: false // 关闭默认image模块
    }
  }" 
/>

自定义图片上传逻辑

得自己监听「粘贴图片」或「点击工具栏图片按钮选图」的事件,然后上传,分两种场景:

场景1:点击工具栏按钮选图

先给工具栏加个「图片」按钮(自定义的),然后绑定点击事件打开文件选择框:

<QuillEditor 
  :options="{
    toolbar: {
      container: [['image']],
      handlers: {
        image: function() {
          // 触发文件选择框
          const input = document.createElement('input')
          input.type = 'file'
          input.accept = 'image/*'
          input.onchange = (e) => {
            const file = e.target.files[0]
            if (file) {
              uploadImage(file) // 自己写的上传函数
            }
          }
          input.click()
        }
      }
    }
  }" 
/>

场景2:粘贴图片

Quill 支持监听 paste 事件,在组件里用 @paste 捕获:

<QuillEditor 
  @paste="handlePaste" 
/>
<script setup>
function handlePaste(e) {
  const items = e.clipboardData.items
  for (let i = 0; i < items.length; i++) {
    if (items[i].type.startsWith('image/')) {
      const file = items[i].getAsFile()
      if (file) {
        uploadImage(file)
        e.preventDefault() // 阻止默认粘贴(否则会插入base64)
      }
    }
  }
}
</script>

实现上传函数 & 插入图片

uploadImage 函数里调后端接口,拿到图片 URL 后插入编辑器:

function uploadImage(file) {
  const formData = new FormData()
  formData.append('file', file)
  // 调自己的上传接口,假设返回 { data: { url: 'https://xxx.com/xxx.jpg' } }
  axios.post('/api/upload', formData).then(res => {
    const url = res.data.url
    // 获取 Quill 实例,插入图片
    const quill = quillEditorRef.value.quill // 假设用ref获取组件实例
    const selection = quill.getSelection()
    if (selection) {
      quill.insertEmbed(selection.index, 'image', url)
      quill.setSelection(selection.index + 1) // 光标移到图片后
    }
  })
}

这里要注意:得用 ref 拿到 QuillEditor 组件实例,才能拿到内部的 Quill 核心对象(quillEditorRef.value.quill),上传失败时要给用户提示,比如用 ElMessage 弹个错误~

想给代码块加高亮,Quill 能实现吗?

Quill 自带的代码块样式很朴素,就一个灰色背景,想实现像技术博客那样的语法高亮(JS、CSS 代码着色),得结合「语法高亮库 + 自定义 Quill 渲染逻辑」,推荐用 prism.js,步骤如下:

装依赖 & 引入样式

先装 prismjs 和对应的语言包(prismjs/components/prism-javascript):

npm install prismjs

然后在项目入口(main.js)引入基础样式和语言:

import 'prismjs/themes/prism.css' // 基础语法高亮样式
import 'prismjs/components/prism-javascript' // JS 语法支持
import 'prismjs/components/prism-css' // CSS 语法支持

自定义 Quill 的代码块渲染

Quill 里的代码块是通过 code 格式渲染的,我们要重写它的渲染逻辑,用 Prism 高亮,需要自定义一个「模块」或者改写 Quill.import('formats/code')

import Quill from 'quill'
import Prism from 'prismjs'
// 改写默认的 code 格式渲染
const Code = Quill.import('formats/code')
Code.prototype.domNode = function() {
  const node = super.domNode()
  // 给 code 标签加 prism 的高亮 class
  node.classList.add('language-javascript') // 假设默认高亮JS,也可以动态判断语言
  Prism.highlightElement(node) // 触发Prism高亮
  return node
}
Quill.register(Code, true)

但这样只能固定语言,实际场景中用户可能选不同语言(HTML、Python),所以更灵活的方式是「让用户选语言,再给代码块加对应 class」:

结合工具栏选语言

给工具栏加个「代码语言选择」的下拉框,选完后给代码块加 class:

<QuillEditor 
  :options="{
    toolbar: {
      container: [
        ['code-block'],
        [{ code: ['javascript', 'css', 'html'] }] // 语言选择下拉
      ]
    }
  }" 
/>

然后监听代码块格式变化,动态加 Prism 的语言 class:

const quill = quillEditorRef.value.quill
quill.on('text-change', (delta, oldDelta, source) => {
  if (source === 'user') {
    const codeBlocks = quill.root.querySelectorAll('pre.ql-syntax')
    codeBlocks.forEach(block => {
      const language = block.getAttribute('data-language') || 'javascript'
      block.classList.add(`language-${language}`)
      Prism.highlightElement(block)
    })
  }
})

这样用户插入代码块后,选语言,文本变化时触发 Prism 高亮,注意要给 Quill 的 pre 标签加 ql-syntax 类(默认代码块的类),确保样式不冲突~

Quill 和 Vue3 的表单怎么结合?比如和 ElForm 一起用?

Vue3 里用 UI 库(Element Plus)的表单时,Quill 作为表单项得处理「数据绑定 + 表单校验」,核心是把 Quill 的内容和表单的 model 绑定,步骤如下:

绑定 v-model 到表单项

假设用 Element Plus 的 ElForm,Quill 组件作为 ElFormItem 的内容:

<template>
  <ElForm :model="form" label-width="120px">
    <ElFormItem label="文章内容" prop="content">
      <QuillEditor v-model="form.content" />
    </ElFormItem>
    <ElFormItem>
      <ElButton type="primary" @click="handleSubmit">提交</ElButton>
    </ElFormItem>
  </ElForm>
</template>
<script setup>
import { reactive } from 'vue'
const form = reactive({
  content: ''
})
function handleSubmit() {
  // 这里可以调表单校验
  // ElForm 会自动校验 prop="content" 的规则
}
</script>

配置表单校验规则

如果要必填校验,给 ElFormItemrules

<ElFormItem 
  label="文章内容" 
  prop="content" 
  :rules="[{ required: true, message: '请填写内容', trigger: 'change' }]"
>
  <QuillEditor v-model="form.content" @update:model-value="onContentChange" />
</ElFormItem>
<script setup>
const onContentChange = (val) => {
  // 触发表单校验(因为 ElForm 监听的是 form 的属性变化,Quill 的 v-model 变化会触发 form.content 变化,所以可能不需要额外处理)
  // 如果没触发,手动调 form.validateField('content')
}
</script>

处理复杂场景(比如富文本为空的判断)

Quill 的内容是 HTML 字符串,空内容可能是 '<p><br></p>' 这种(因为默认有个空行),所以表单校验时要处理这种情况:

const form = reactive({
  content: ''
})
const rules = {
  content: [
    { 
      validator: (rule, value, callback) => {
        // 去掉所有标签和空格,判断是否真有内容
        const text = value.replace(/<[^>]+>/g, '').trim()
        if (text === '') {
          callback(new Error('请填写内容'))
        } else {
          callback()
        }
      },
      trigger: 'change'
    }
  ]
}

这样就能精准判断富文本是否真的有内容,避免空标签导致校验失效~

移动端用 Quill 编辑,怎么适配样式和交互?

Quill 默认是给 PC 端设计的,移动端(手机、平板)用的时候会有「工具栏按钮太小、键盘弹出顶布局、内容编辑区域滑动不流畅」这些问题,得针对性改样式和交互:

调整工具栏样式

移动端屏幕窄,工具栏按钮容易挤在一起,可以用媒体查询,让工具栏换行或变成滚动式:

@media (max-width: 768px) {
  .ql-toolbar {
    flex-wrap: wrap; // 让按钮自动换行
  }
  .ql-formats {
    margin-bottom: 8px; // 每行之间留空隙
  }
}

要是按钮还是太多,改成「底部固定工具栏 + 折叠菜单」,比如把不常用的按钮放到下拉菜单里,点击后展开,这需要自定义工具栏结构,用 UI 库的 PopupDropdown 组件实现。

处理键盘弹出问题

手机输入时键盘弹出会把页面顶起来,导致编辑器位置偏移,可以固定编辑器高度,或禁止页面缩放:

<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">

然后给编辑器容器加固定高度,配合 overflow-y: auto

.ql-editor {
  height: 300px; // 固定高度,根据需求调整
  overflow-y: auto;
}

优化触摸交互

移动端触摸选中文本时,Quill 的气泡工具栏(bubble theme)可能显示异常,可以调整气泡工具栏的位置,或者强制用 snow 主题(带固定工具栏),禁止用户缩放页面(上面的 viewport 配置),避免双指缩放影响编辑。

简化功能

移动端场景下,用户很少用复杂格式(比如多级列表、代码块),可以把工具栏按钮精简,只保留「加粗、图片、链接、撤销」这些高频操作,减少用户学习成本~

回显时样式乱了,怎么解决?

富文本编辑完,把内容(HTML 字符串)放到页面上展示时,经常出现「字体不对、排版错乱、代码块没高亮」这些问题,核心原因是「回显容器没加载 Quill 的样式 + 自定义样式没同步」,解决步骤:

引入 Quill 核心样式

回显的 HTML 里包含 Quill

版权声明

本文仅代表作者观点,不代表Code前端网立场。
本文系作者Code前端网发表,如需转载,请注明页面地址。

发表评论:

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。

热门