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

一、Vue3项目为啥优先选Quill做富文本?

terry 2周前 (10-04) 阅读数 57 #Vue
文章标签 Vue3 Quill

做前端项目时,富文本编辑需求太常见了!从简单的文章排版到复杂的富媒体嵌入,选对工具能省不少事儿,要是你用Vue3开发,想结合Quill实现富文本功能,肯定一堆疑问:选Quill有啥优势?咋快速集成?自定义工具栏、加拓展功能咋搞?性能和兼容性坑咋填?今天咱就把这些常见问题掰开揉碎,用白话讲明白~

先唠唠富文本编辑器的选型逻辑,市面上常见的有TinyMCE、Slate、Quill这些,Quill能在Vue3项目里“杀出重围”,核心是这几点:

开发体验友好,API灵活度高

Quill用Delta文档模型,所有编辑操作(加粗、插入图片)都会生成Delta格式的JSON数据,举个例子,一段“Hello Quill”的内容,Delta长这样:

{ "ops": [{"insert": "Hello", "attributes": {"bold": true}}, {"insert": " Quill"}]}

这种结构化的数据,在Vue3里处理双向绑定、做版本控制(比如撤销重做)特别顺手,而且Quill暴露的API很直观,像quill.insertText()quill.getContents()这些方法,哪怕是刚接触的前端同学,看文档也能快速上手。

可扩展性拉满,靠“模块”自由拼功能

Quill的Module机制是灵魂!想加代码高亮?装个quill-syntax模块;要图片能拖拽缩放?用quill-image-resize-module;甚至想做协同编辑,社区也有现成方案,Vue3的Composition API刚好能把这些模块的逻辑封装成可复用的函数(比如useQuillUpload处理图片上传),项目里多处用富文本时,不用重复写代码。

轻量+生态,中小项目“性价比”首选

Quill核心包体积很小,只做基础富文本编辑完全够用,要是项目需要复杂功能,社区插件能补上——比如数学公式用quill-mathjax、表格用quill-table,对比TinyMCE(功能全但体积大,适合大型CMS)、Slate(高度灵活但需要自己造很多轮子),Quill在“快速落地需求”和“定制性”之间平衡得很好,中小项目选它不踩坑。

Vue3里咋最快把Quill用起来?

很多同学一上来就想“快速跑通流程”,这部分直接给三步落地法,哪怕是Vue3新手也能跟着做:

步骤1:装依赖,选对社区库

Vue3生态里,最常用的Quill封装库是vue3-quill(注意不是老版本的vue-quill-editor,那是Vue2的),命令行装包:

npm i vue3-quill

步骤2:封装基础富文本组件

用Vue3的<script setup>语法,写个最简化的编辑器组件:

<template>
  <!-- QuillEditor是vue3-quill提供的组件 -->
  <QuillEditor 
    v-model="content" 
    :options="editorOptions" 
    @ready="onEditorReady"
  />
</template>
<script setup>
import { ref } from 'vue'
import { QuillEditor } from 'vue3-quill' // 引入组件
import 'vue3-quill/dist/style.css' // 引入默认样式
// 双向绑定的内容
const content = ref('') 
// 编辑器配置项
const editorOptions = {
  theme: 'snow', // snow是带工具栏的主题,bubble是气泡式(聚焦时显示工具栏)
  modules: {
    toolbar: [ // 配置默认显示哪些按钮
      ['bold', 'italic', 'underline'], // 格式类
      ['link', 'image'], // 媒体类
      [{ list: 'ordered' }, { list: 'bullet' }] // 列表类
    ]
  }
}
// 编辑器初始化完成后触发,能拿到Quill实例
const onEditorReady = (quill) => {
  console.log('Quill实例:', quill)
}
</script>

这段代码做了这些事:

  • v-model双向绑定(content变化时编辑器自动更新,编辑器内容变化时content也自动更新);
  • 通过options配置主题和工具栏;
  • 监听ready事件,拿到Quill实例后能调用API(比如quill.insertEmbed()插入图片)。

步骤3:验证效果,快速调试

启动Vue3项目(比如npm run dev),打开页面就能看到带工具栏的富文本编辑器,试试点“加粗”按钮,输入内容是否变粗;点“图片”按钮,能不能插入图片(默认是base64格式,后面会讲改成服务器上传),如果没渲染出来,先检查这几点:

  • 依赖是否装对(package.json里有vue3-quill);
  • 样式是否引入(漏了import 'vue3-quill/dist/style.css'会导致工具栏样式乱掉);
  • Vue3的组件引入是否正确(别把QuillEditor导入路径写错)。

Quill工具栏能自定义到啥程度?咋改?

默认工具栏满足不了需求?比如想加“插入签名”按钮、把图片上传改成走后端接口、自定义字体选项……这部分教你从基础配置到深度自定义

基础配置:用数组/对象改默认按钮

Quill的toolbar支持两种配置方式:

  • 数组形式:适合快速开关功能,比如只保留加粗、链接:

    modules: {
      toolbar: [['bold'], ['link']]
    }
  • 对象形式:适合精细控制,比如标题只保留1、2级,隐藏3级及以上:

    modules: {
      toolbar: {
        container: [
          [{ header: [1, 2, false] }] // false表示“不显示标题”选项
        ]
      }
    }

进阶:加自定义按钮(插入签名”)

想加个点击后插入“【个人签名】”的按钮,步骤分三步:

第一步:在toolbar里声明自定义按钮

container加一个自定义标识(比如'custom-sign'):

modules: {
  toolbar: {
    container: [
      ['bold', 'italic'],
      ['custom-sign'] // 新增自定义按钮
    ]
  }
}

第二步:给按钮绑定点击逻辑(handlers)

handlers配置按钮的点击事件,逻辑里调用Quill API插入内容:

modules: {
  toolbar: {
    container: [...],
    handlers: {
      'custom-sign': function() {
        // this.quill 是当前Quill实例
        const quill = this.quill
        // 获取光标位置,插入文本
        const cursorPos = quill.getSelection().index
        quill.insertText(cursorPos, '【个人签名】', 'bold', true)
      }
    }
  }
}

第三步:给按钮加样式(图标/文字)

Quill的工具栏按钮默认是文字,想改成图标?用CSS的伪元素:

/* 找到自定义按钮的类:.ql- + 自定义标识 */
.ql-custom-sign::before {
  content: '✒️'; /* 用Unicode图标,也可以用图片url */
  font-size: 16px;
}

高阶:改造图片上传(从base64到服务器上传)

Quill默认插入图片是转base64,要是项目里图片要存服务器,得重写图片处理逻辑:

第一步:拦截toolbar的“image”按钮事件

handlers里重写image的逻辑,改成触发文件选择框:

modules: {
  toolbar: {
    container: [..., ['image']],
    handlers: {
      image: function() {
        // 创建隐藏的文件输入框
        const input = document.createElement('input')
        input.type = 'file'
        input.accept = 'image/*' // 只允许图片
        // 选择文件后触发上传
        input.addEventListener('change', (e) => {
          const file = e.target.files[0]
          // 调后端上传接口(这里用axios举例)
          uploadImage(file).then(res => {
            const imgUrl = res.data.url // 假设接口返回图片URL
            // 插入图片到编辑器
            this.quill.insertEmbed(
              this.quill.getSelection().index, 
              'image', 
              imgUrl
            )
          })
        })
        // 触发文件选择框
        input.click()
      }
    }
  }
}

第二步:处理粘贴图片(用户从剪贴板粘贴图片)

除了点击按钮上传,还要处理“粘贴图片”的场景,监听Quill的paste事件,拦截粘贴内容:

// 在onEditorReady里绑定事件
const onEditorReady = (quill) => {
  quill.on('paste', (e) => {
    e.preventDefault() // 阻止默认粘贴行为(默认是转base64)
    const clipboardData = e.clipboardData || window.clipboardData
    const files = clipboardData.files
    if (files.length > 0 && files[0].type.startsWith('image/')) {
      // 同样调上传接口,把图片URL插入
      uploadImage(files[0]).then(res => {
        quill.insertEmbed(quill.getSelection().index, 'image', res.data.url)
      })
    }
  })
}

Quill在Vue3里咋拓展复杂功能?

项目需求一复杂,只靠默认功能肯定不够,这里举代码高亮、数学公式、版本控制三个典型场景,教你用Quill的模块机制拓展:

场景1:代码块高亮(结合PrismJS)

想让编辑器里的代码块自动高亮?步骤如下:

第一步:装PrismJS和样式

npm i prismjs

然后引入默认样式(也可以选其他主题):

import 'prismjs/themes/prism.css'

第二步:自定义高亮逻辑

写个函数,遍历编辑器里的代码块,用PrismJS高亮:

import Prism from 'prismjs'
const highlightCode = (quill) => {
  // 找到所有代码块(Quill里代码块是<pre class="ql-syntax">)
  const codeBlocks = quill.root.querySelectorAll('pre.ql-syntax')
  codeBlocks.forEach(block => {
    const codeEl = block.querySelector('code')
    // 从class里提取语言(比如class="language-js" → 语言是js)
    const lang = codeEl.className.match(/language-(\w+)/)?.[1] || 'javascript'
    // 调用Prism高亮
    Prism.highlightElement(codeEl, Prism.languages[lang])
  })
}

第三步:绑定编辑器事件

onEditorReady里,监听text-change变化时触发高亮):

const onEditorReady = (quill) => {
  quill.on('text-change', (delta, oldDelta, source) => {
    if (source === 'user') { // 只处理用户主动修改的情况
      highlightCode(quill)
    }
  })
}

场景2:数学公式编辑(结合Katex)

想在富文本里插入数学公式($E=mc^2$$),用Katex渲染:

第一步:装Katex和Quill插件

npm i katex quill-mathjax

第二步:配置Quill的mathjax模块

editorOptions里加mathjax配置,让工具栏显示公式按钮:

import 'katex/dist/katex.css'
import MathJax from 'quill-mathjax'
const editorOptions = {
  modules: {
    mathjax: {
      config: (() => {
        // Katex配置,比如默认行内公式、块级公式
        window.MathJax = {
          tex: {
            inlineMath: [['$', '$']],
            displayMath: [['$$', '$$']]
          }
        }
        return window.MathJax
      })(),
      editor: MathJax
    },
    toolbar: [['mathjax']] // 工具栏加公式按钮
  }
}

点击“公式”按钮后,会弹出输入框,输入LaTeX语法(比如E=mc^2),编辑器会自动渲染成公式。

场景3:版本控制(撤销/重做)

Quill内置history模块,能记录内容变化,实现撤销(undo)、重做(redo),配置起来很简单:

const editorOptions = {
  modules: {
    history: {
      delay: 2000, // 每2秒记录一次历史(避免太频繁)
      maxStack: 50, // 最多存50步历史
      userOnly: true // 只记录用户主动操作,忽略程序自动修改
    },
    toolbar: [['undo', 'redo']] // 工具栏加撤销/重做按钮
  }
}

要是想在Vue组件里自己加按钮控制撤销/重做,拿到Quill实例后调用API:

// 假设按钮的点击事件
const handleUndo = () => {
  quill.value.history.undo() // quill.value是保存的Quill实例
}
const handleRedo = () => {
  quill.value.history.redo()
}

Vue3下Quill性能咋优化?

多了(比如几千字+大量图片),容易出现卡顿,这部分给三个实战优化思路

大数据量:防抖+Delta优化

当用户快速输入时,Quill的text-change事件会频繁触发,导致性能下降,用防抖限制事件处理频率:

import { debounce } from 'lodash-es'
const onEditorReady = (quill) => {
  // 防抖处理高亮/上传等逻辑
  const debouncedHighlight = debounce(() => {
    highlightCode(quill)
  }, 300) // 300毫秒内只执行一次
  quill.on('text-change', debouncedHighlight)
}

尽量用Quill的Delta API做“增量更新”,而不是全量替换内容,比如要修改一段文字,用quill.updateContents(delta)而不是quill.setContents()

模块懒加载:按需加载大依赖

像PrismJS、Katex这些库体积不小,要是页面初始化时就加载,会拖慢首屏,用动态导入(import())实现懒加载:

const onEditorReady = (quill) => {
  // 需要代码高亮时再加载Prism
  const loadPrism = async () => {
    const { default: Prism } = await import('prismjs')
    // 初始化高亮逻辑...
  }
  // 比如用户点击“代码块”按钮后加载
  quill.getModule('toolbar').addHandler('code-block', () => {
    loadPrism().then(() => {
      // 加载完成后插入代码块
      quill.insertText(quill.getSelection().index, '```js\n// 请输入代码\n```', 'code-block')
    })
  })
}

组件销毁:及时清理Quill实例

Vue3组件销毁时,要是没销毁Quill实例,会导致内存泄漏(尤其是单页面应用,组件频繁切换时),在onUnmounted钩子处理:

<script setup>
import { onUnmounted, ref } from 'vue'
import { QuillEditor } from 'vue3-quill'
const quillInstance = ref(null)
const onEditorReady = (quill) => {
  quillInstance.value = quill
}
onUnmounted(() => {
  if (quillInstance.value) {
    quillInstance.value.destroy() // 销毁Quill实例
    quillInstance.value = null
  }
})
</script>

集成Quill时那些“踩过的坑”咋解决?

实际开发中,样式乱、移动端适配差、粘贴内容脏这些问题很常见,分享四个高频坑的解决方案:

坑1:Quill样式和项目CSS冲突

Quill的snow主题自带样式,和项目全局CSS(比如Element Plus、Ant Design Vue)容易冲突,解决方法:

  • 用深度选择器覆盖:Vue3中用::v-deep(或>>>)修改Quill的样式,比如把工具栏按钮改成圆角:

    ::v-deep .ql-toolbar .ql-button {
      border-radius: 4px;
    }
  • 自定义主题:如果冲突太多,直接复制Quill的默认样式文件,改成自己的类名,避免全局污染。

坑2:移动端工具栏按钮太小,点击难触发

手机上Quill的toolbar按钮默认尺寸小,用户点

版权声明

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

发表评论:

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

热门