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

vue2 里的 quill 是什么?

terry 2天前 阅读数 15 #Vue
文章标签 vue2;quill

做Vue2项目时,想给页面加个好用的富文本编辑器,很多人会想到Quill,但“vue quill vue2 怎么用”可不是一句话能说清的,从安装到自定义配置,再到解决实际开发里的坑,每一步都得踩稳,今天就把Vue2结合Quill的玩法拆碎了讲,不管是刚入门的新手,还是想优化富文本功能的老开发,都能找到能用的干货。

Quill本身是个现代的富文本编辑器,主打可扩展、API灵活,官网给的定位是“Powerful, modern WYSIWYG editor”,但Vue2项目不能直接用原生Quill,得靠**vue-quill-editor**这个封装库,它把Quill的核心能力适配成Vue组件,支持Vue2的响应式、组件化开发逻辑,让我们能通过Vue的语法(v-model`、`props`)去控制富文本编辑器的内容和配置。

简单说,vue-quill-editor是Vue2生态里对接Quill的“桥梁”,既保留了Quill本身的强大扩展性,又符合Vue2项目的开发习惯,比如你想做个带格式刷、自定义表情的编辑器,靠Quill的模块机制能实现,而vue-quill-editor让这些功能在Vue组件里更好整合。

为什么在vue2项目选quill?

不是所有富文本场景都得选Quill,但它的优势在某些需求下特别“香”:

  • 扩展性拉满:Quill的架构是基于“模块(Module) + 主题(Theme) + 插件(Plugin)”,比如想加个“一键生成目录”功能,或者自定义右键菜单,只要写对应的模块逻辑,就能嵌入到编辑器里,Vue2项目里通过vue-quill-editor的配置项,能很方便地把这些自定义模块挂上去。
  • API 灵活到“贴脸”:Quill暴露了从文本内容到光标位置的细粒度API,比如在Vue组件里,你可以通过ref拿到Quill实例,然后调用quill.insertText()插入指定文字,或者用quill.getSelection()获取光标位置,实现“输入特定字符自动触发格式变化”这类定制逻辑。
  • 社区资源够厚:这么多年下来,GitHub上围绕Quill的插件、主题一堆,比如quill-table支持表格编辑,quill-emoji实现表情插入,在Vue2项目里,只要把这些插件和vue-quill-editor结合好,能少写很多重复代码。

要是项目只需要“能输入文字、加个粗”这种极简需求,用textarea或者更轻量的编辑器也够,但如果涉及复杂格式、自定义交互,Quill的扩展性就是降维打击。

怎么在vue2项目里装quill?

第一步得把依赖装对,因为要同时装Quill核心和Vue的封装库:

  1. 装包:打开终端,在Vue2项目根目录执行
    npm install vue-quill-editor quill
    (如果用yarn就是yarn add vue-quill-editor quill
    这里vue-quill-editor是Vue2的封装层,quill是原生编辑器核心,两者都得装。

  2. 全局注册(或局部引入)
    想在整个项目里随时用<quill-editor>组件,就去main.js里全局注册:

    import Vue from 'vue'
    import QuillEditor from 'vue-quill-editor'
    // 引入Quill的默认样式,也可以自己改样式覆盖
    import 'quill/dist/quill.core.css'
    import 'quill/dist/quill.snow.css' // snow是默认主题,还有bubble主题可选
    import 'quill/dist/quill.bubble.css'
    Vue.use(QuillEditor)

    要是只在某个组件里用,就局部引入:

    import { quillEditor } from 'vue-quill-editor'
    import 'quill/dist/quill.snow.css' 
    export default {
      components: {
        quillEditor
      }
    }
  3. 样式引入的坑
    上面引入的是Quill官方默认样式,要是项目用了UI框架(比如Element UI),可能出现样式冲突(比如按钮样式被覆盖),这时候要么在全局样式里用!important覆盖,要么自己仿写Quill的工具栏、编辑器区域样式,把冲突的CSS属性重新定义。

基础用法:写个能输入的富文本组件

先看最简示例,在Vue组件里用v-model

<template>
  <div>
    <!-- quillEditor 是组件名,对应局部或全局注册的 -->
    <quill-editor 
      v-model="content" 
      :options="editorOption"
      @blur="handleBlur"
    ></quill-editor>
  </div>
</template>
<script>
import { quillEditor } from 'vue-quill-editor'
import 'quill/dist/quill.snow.css' 
export default {
  components: { quillEditor },
  data() {
    return {
      content: '<p>初始内容</p>', // 富文本内容,v-model双向绑定
      editorOption: {
        theme: 'snow', // 使用snow主题,界面带工具栏
        modules: {
          toolbar: [
            ['bold', 'italic', 'underline'], // 加粗、斜体、下划线
            [{ 'list': 'ordered' }, { 'list': 'unordered' }], // 有序、无序列表
            [{ 'image': true }] // 图片上传按钮(默认是base64,后面讲怎么改)
          ]
        }
      }
    }
  },
  methods: {
    handleBlur() {
      console.log('编辑器失去焦点,当前内容:', this.content)
    }
  }
}
</script>

这里有几个关键点:

  • v-model绑定的content,存的是富文本的HTML字符串,所以初始值可以是带标签的字符串,编辑器里修改内容会同步更新content
  • editorOption里的modules.toolbar是配置工具栏按钮的,数组里每个元素对应一组功能,官方支持的按钮类型参考Quill官网,比如'bold'对应加粗按钮,{ 'color': [] }对应颜色选择器。
  • 事件处理:除了@blur,还能监听@change变化时触发)、@focus(获取焦点时)等,参数里能拿到编辑器实例和内容变化信息。

自定义配置:让quill更贴合业务

自定义工具栏按钮

默认工具栏按钮不够用?比如想加个“插入视频”或者“格式刷”按钮,以“插入自定义模板”为例:

editorOption.modules.toolbar里加自定义按钮:

editorOption: {
  modules: {
    toolbar: {
      container: [
        ['bold'],
        // 自定义按钮组
        [{ header: [1, 2, false] }],
        ['myCustomButton'] // 自定义按钮的标识
      ],
      handlers: {
        // 给myCustomButton写点击逻辑
        myCustomButton: function() {
          // this 指向Quill实例
          const quill = this
          // 插入一段自定义HTML,比如带样式的文本
          quill.insertHTML('<span style="color:red;">这是自定义内容</span>')
          quill.setSelection(quill.getLength()) // 把光标移到插入内容后面
        }
      }
    }
  }
}

还得给自定义按钮加样式(因为Quill默认没这个按钮的UI),可以在CSS里写:

.ql-toolbar .ql-buttons button.ql-myCustomButton {
  /* 自定义图标或文字,比如用背景图 */
  background: url('./icon.png') no-repeat center;
  width: 24px;
  height: 24px;
  /* 隐藏默认文字(如果有的话) */
  color: transparent;
}

这样点击自定义按钮,就能插入预设内容,实现业务特有的交互。

替换图片上传逻辑(解决base64太大问题)

Quill默认把图片转成base64存到内容里,要是图片大,富文本内容会变得巨长,还影响性能,所以得改成“选图后上传到服务器,把服务器返回的图片URL插到编辑器里”。

步骤:

  • 先把toolbar里的image按钮保留,但重写它的点击逻辑(或者拦截图片粘贴、拖拽的事件)。
  • editorOption里配置图片处理逻辑:
editorOption: {
  modules: {
    toolbar: {
      container: [['image']],
      handlers: {
        image: function() {
          // 触发自己的文件选择框
          const input = document.createElement('input')
          input.setAttribute('type', 'file')
          input.setAttribute('accept', 'image/*')
          input.onchange = () => {
            const file = input.files[0]
            if (file) {
              // 这里调自己的上传接口,比如uploadImg是封装的axios请求
              this.$api.uploadImg(file).then(res => {
                const imgUrl = res.data.url
                // 获取当前光标位置,插入图片
                const quill = this
                const range = quill.getSelection()
                if (range) {
                  quill.insertEmbed(range.index, 'image', imgUrl)
                  quill.setSelection(range.index + 1) // 光标移到图片后
                }
              })
            }
          }
          input.click()
        }
      }
    }
  }
}

这样,点图片按钮时,会唤起系统文件选择框,选完图上传到服务器,再把服务器返回的URL插入编辑器,替代默认的base64逻辑。

自定义字体、字号

Quill默认字体选项有限,项目里要加“微软雅黑”“宋体”这类选项,得改配置:

editorOption: {
  modules: {
    toolbar: {
      container: [
        [{ 'font': ['微软雅黑', '宋体', 'Arial'] }], // 字体选项
        [{ 'size': ['small', false, 'large', 'huge'] }] // 字号
      ]
    },
    // 还得给Quill注册这些字体的样式,不然选了没效果
    // 可以在全局CSS里写:
    /* 
    .ql-font-微软雅黑 {
      font-family: "微软雅黑" !important;
    }
    .ql-font-宋体 {
      font-family: "宋体" !important;
    }
    */
  }
}

注意:字体名称要和CSS里的类名对应(ql-font-xxx),所以配置font选项时,数组里的名称要和CSS类名后缀一致,这样选字体时才能正确应用样式。

常见问题:这些坑怎么绕?

工具栏按钮样式乱了,和UI框架冲突

比如用Element UI时,Quill的按钮被改成Element的样式,导致图标显示异常,解决方法:

  • 先排查是否引入了Quill的默认样式(snow或bubble),如果引入了,看是否被UI框架的全局样式覆盖。
  • 给Quill的工具栏加独立的样式作用域,比如在外层包个class,然后用深度选择器(>>>/deep/)去覆盖:
    .my-quill-editor >>> .ql-toolbar .ql-button {
      /* 重新定义按钮样式,比如背景、边框 */
      background: #fff;
      border: 1px solid #dcdcdc;
    }

图片上传成功,但编辑器里不显示图片

大概率是上传后拿到的URL不对,或者Quill的insertEmbed调用有问题,检查:

  • 服务器返回的URL是否是完整的可访问地址(比如带http/https)。
  • quill.getSelection()是否拿到有效的range(比如用户没选任何位置时,range可能是null,这时候要处理默认插入位置)。

可以加个兜底逻辑:

if (range) {
  quill.insertEmbed(range.index, 'image', imgUrl)
} else {
  quill.insertEmbed(0, 'image', imgUrl) // 没选位置就插在开头
}

移动端(手机、平板)点击工具栏没反应

Quill的默认事件处理在移动端可能有兼容问题,尤其是自定义按钮,可以检查:

  • 是否给按钮加了touchstart事件支持(但Quill本身对移动端有基础支持,可能是自己写的handlers里没处理touch事件)。
  • 用Chrome的手机模拟模式测试,看控制台是否有事件绑定错误。
  • 有些情况下,给编辑器外层加user-select: auto(默认Quill可能设置了user-select: none影响交互)。

v-model回显不对

比如从接口拿到的富文本HTML,在编辑器里显示成纯文本,原因是Quill的内容需要是合法的Delta格式或者HTML,但有时候接口返回的HTML里标签被转义了(比如&lt;p&gt;变成&amp;lt;p&amp;gt;),解决:

  • 确保接口返回的HTML是未转义的,或者在赋值给content前,用DOMParser转义回来。
  • 初始化时,给content赋值前,先检查是否是正确的HTML字符串,必要时用this.content = this.content.replace(/&amp;/g, '&')这类操作还原。

进阶场景:把quill玩出花

结合表单提交 作为表单的一部分,和其他表单项一起提交,比如用Element UI的Form组件:

<el-form :model="form" label-width="100px">
  <el-form-item label="标题">
    <el-input v-model="form.title"></el-input>
  </el-form-item>
  <el-form-item label="内容">
    <quill-editor v-model="form.content" :options="editorOption"></quill-editor>
  </el-form-item>
  <el-form-item>
    <el-button type="primary" @click="submitForm">提交</el-button>
  </el-form-item>
</el-form>
<script>
export default {
  data() {
    return {
      form: { title: '', content: '' },
      editorOption: { /* 配置 */ }
    }
  },
  methods: {
    submitForm() {
      // 这里调接口,把form对象发出去
      this.$api.submitArticle(this.form).then(res => {
        if (res.code === 200) {
          this.$message.success('提交成功')
        }
      })
    }
  }
}
</script>

注意:富文本内容可能包含大量HTML,接口要支持接收大文本,必要时调整后端接口的请求体大小限制。

实现undo/redo(撤销/重做)

Quill本身内置了历史记录模块,只要在配置里开启:

editorOption: {
  modules: {
    history: {
      delay: 1000, // 记录间隔,毫秒
      maxStack: 100, // 最大记录数
      userOnly: false // 是否只记录用户操作(默认false,自动保存的操作也记录)
    }
  }
}

然后通过Quill实例调用quill.history.undo()quill.history.redo(),可以在Vue组件里加两个按钮,绑定方法:

<template>
  <div>
    <quill-editor ref="myQuill" v-model="content" :options="editorOption"></quill-editor>
    <el-button @click="handleUndo">撤销</el-button>
    <el-button @click="handleRedo">重做</el-button>
  </div>
</template>
<script>
export default {
  methods: {
    handleUndo() {
      const quill = this.$refs.myQuill.quill // 获取Quill实例
      quill.history.undo()
    },
    handleRedo() {
      const quill = this.$refs.myQuill.quill
      quill.history.redo()
    }
  }
}
</script>

权限控制:不同角色看到不同工具栏

比如普通用户只能用加粗、列表,管理员能上传图片、改字体,可以通过动态渲染editorOption.modules.toolbar.container实现:

data() {
  return {
    userRole: 'admin', // 从接口或全局状态拿角色
    editorOption: {
      modules: {
        toolbar: {
          container: []
        }
      }
    }
  }
},
mounted() {
  // 根据角色生成toolbar配置
  let toolbarConfig = []
  if (this.userRole === 'admin') {
    toolbarConfig = [
      ['bold', 'italic'],
      [{ 'image': true }],
      [{ 'font': ['微软雅黑'] }]

版权声明

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

发表评论:

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

热门