Vue 3 富文本编辑器组件详解

8,471次阅读
没有评论

共计 7841 个字符,预计需要花费 20 分钟才能阅读完成。

引言

在 Web 开发中,富文本编辑器是一个常见的需求,特别是在内容管理系统(CMS)或博客平台中。本文将详细解析一个基于 Vue 3 和 @wangeditor/editor-for-vue 的富文本编辑器组件的实现,重点介绍其关键功能及其背后的设计思路。通过阅读本文,你将学会如何创建一个支持图片上传、双向数据绑定、异步图片加载的富文本编辑器组件。

组件概述

本文所述的 Vue 组件旨在提供一个可高度自定义的富文本编辑器。这个组件包含以下主要关键点:

  • 富文本内容编辑
  • 工具栏配置
  • 自定义图片上传与插入
  • 通过 v-model 实现双向数据绑定
  • 异步加载并渲染图片

组件的核心功能实现

1. 管理编辑器实例:为什么选择 shallowRef

背景

在 Vue 3 中,refshallowRef 是用来创建响应式数据的工具。通常情况下,ref会让它内部的所有对象都响应式地更新,但有时候我们并不需要对所有内部对象进行追踪。这时,shallowRef就是一个更好的选择。

解释

ref vs shallowRef

  • ref:会递归地让内部所有对象都变成响应式,这对于简单数据类型很好用,但如果是复杂对象(例如编辑器实例),它可能会带来性能问题。
  • shallowRef:只会让最外层的引用是响应式的,内部对象不会变成响应式,这样可以避免不必要的性能开销。

在组件中的应用

在我们的富文本编辑器组件中,我们使用 shallowRef 来管理编辑器实例,这样我们只需要追踪编辑器实例的创建和销毁,而不用关心编辑器内部的复杂数据结构。

代码示例:

const editorRef = shallowRef(); // 只让 editorRef 是响应式的,而不是它内部的所有对象

const handleCreated = (editor) => {editorRef.value = editor; // 当编辑器实例创建时,将其保存到 editorRef};

2. 数据同步:理解 v-model 和 update 事件

背景

在 Vue 3 中,v-model 是用来实现双向数据绑定的工具,它允许父组件和子组件之间轻松地同步数据。我们可以通过自定义事件来更好地控制这个数据同步过程。

解释

v-model的默认行为:

默认情况下,v-model 绑定的数据是modelValue,它通过 update:modelValue 事件来同步数据。

自定义事件:

我们可以通过 defineEmits 来声明和触发自定义事件,例如 update:modelText,用于同步其他类型的数据(如纯文本内容)。

在组件中的应用

在这个组件中,我们通过 v-model 实现了 HTML 内容的同步,同时通过 update:modelText 事件来同步编辑器中的纯文本内容。

代码示例:

const emit = defineEmits(['update:modelValue', 'update:modelText']); // 声明将会触发的事件

const htmlContent = ref('');

watch(() => props.modelValue, (val) => {if (val === htmlContent.value) return;
   
  htmlContent.value = val;
    
  nextTick(() => {emit('update:modelText', editorRef.value.getText().trim()); // 当内容变化时,同步文本内容
  });
});
​
watch(htmlContent, (val) => {emit('update:modelValue', val); // 同步 HTML 内容
  emit('update:modelText', editorRef.value.getText().trim());  // 同步纯文本内容
});

3. 处理模态框:如何自定义编辑器中的模态框样式?

背景

在富文本编辑器中,模态框(如图片上传、链接插入等)是常见的交互元素。为了让这些模态框符合我们的 UI 风格,我们通常需要自定义它们的样式和位置。

解释

  • 模态框的样式控制:
    • 我们可以监听编辑器的 modalOrPanelShow 事件,在模态框显示时,动态设置它的样式和位置,使其在屏幕上居中显示。

在组件中的应用

在编辑器实例创建后,我们通过监听 modalOrPanelShow 事件,自定义设置模态框的样式和位置。

代码示例:

const handleCreated = (editor) => {

  editorRef.value = editor;
​
  // 当模态框显示时,调整其样式
  editorRef.value.on('modalOrPanelShow', (modalOrPanel) => {if (modalOrPanel.type !== 'modal') return;
​
    const {$elem} = modalOrPanel;
    const width = $elem.width();
    const height = $elem.height();
​
    $elem.css({
      left: '50%',
      top: '50%',
      marginLeft: `-${width / 2}px`,
      marginTop: `-${height / 2}px`,
      zIndex: 1000,
      position: 'fixed',
      height: 'fit-content',
    });
  });
};

4. 图片上传与插入:如何自定义图片上传逻辑?

背景

图片上传是富文本编辑器中的重要功能。通常,我们需要自定义上传逻辑,以便将图片上传到服务器,并将服务器返回的图片信息插入到编辑器中。

解释

自定义图片上传:

  • MENU_CONF 是一个配置对象,用于自定义富文本编辑器中的菜单行为。这里的配置项 ['uploadImage'] 针对的是图片上传功能,其中 customUpload 是编辑器提供的方法,用于自定义图片上传的流程,当用户在编辑器中选择上传图片时,这个方法会被调用。
  • 这里的 file 参数表示用户选择上传的图片文件。

构建 FormData 对象:

  • FormData 是用于构建上传文件请求体的标准接口,它能够以键值对的形式存储文件和数据,用于文件上传。
  • formData.append('file', file) 将图片文件添加到 FormData 对象中,键名为 file

异步上传文件:

  • 使用 await upload(formData) 将构建好的 FormData 对象发送给服务器进行文件上传。
  • upload 是自定义的一个 API 方法,用于将文件上传到服务器。上传完成后,服务器会返回一个响应数据(res),其中包含了上传成功后的图片信息,如 id 和 sign

插入图片:

  • 上传成功后,通过 insertFn 方法将图片插入到编辑器中。
  • insertFn 是编辑器提供的一个函数,用于在编辑器中插入图片或其他内容。通过调用 insertFn,你可以控制图片的插入行为。

insertFn 方法的参数含义

insertFn 是编辑器传递给 customUpload 方法的一个回调函数,用于在上传成功后将图片插入到编辑器中。它通常接受三个参数:

insertFn(url, altText, href)

url (第一个参数):

  • 图片的实际 URL 地址。通常情况下,这是图片在服务器上存储的地址,用于在编辑器中渲染图片。
  • 在代码中,这个参数被传入一个空字符串 '',意思是不直接使用 URL 来展示图片,而是通过后面的 id 和 sign 来渲染。

altText (第二个参数):

  • 图片的替代文本(alt text),当图片无法加载时显示的文字。
  • 这里也是传入空字符串 '',表示不设置替代文本。

href (第三个参数):

  • 图片的链接地址。通常用于点击图片时跳转的目标 URL。
  • 在代码中,传入的是一个包含图片 id 和 sign 的字符串。这是用于在渲染时通过 id 和 sign 来向服务器端请求图片资源。

在组件中的应用

在这个组件中,我们通过自定义 uploadImage 配置,来实现图片的上传和插入逻辑。

代码示例:

const editorConfig = {
  MENU_CONF: {['uploadImage']: {async customUpload(file, insertFn) {const formData = new FormData();
        formData.append('file', file);
        const res = await upload(formData); // 上传图片到服务器
        insertFn('','', `${res.data.id}&${res.data.sign}`); // 插入图片,将 id 和 sign 传入
      },
    },
  },
};

5. 延迟加载图片:如何确保图片正确显示?

背景

在富文本编辑器中,图片可能不会直接以 URL 的形式插入,而是以某种标识符(如id 和 sign)的形式存在。为了正确显示这些图片,我们需要在渲染时动态加载它们的实际 URL。

解释

参数检查:

  • handleImage 接受一个 dom 参数,并首先检查这个参数是否为 Element 类型。
  • 如果不是 Element,则输出警告信息 dom is not Element,提示传入的参数不正确。

查找图片元素:

  • 如果 dom 是有效的 Element,函数会调用 dom.querySelectorAll('img'),遍历所有图片()元素。
  • 这些图片元素通常是从编辑器中插入的,它们可能还没有被正确渲染出来。

处理 data-href 属性:

  • 对于每个图片元素,函数检查它的 data-href 属性。这个属性包含图片的标识信息,比如 id 和 sign,它们通过 & 符号连接。
  • data-href 属性在图片插入时被设置,用于标识图片的存储位置或其他元数据。

提取 id 和 sign

  • 如果 data-href 属性存在,并且包含 & 符号,函数会将其拆分成 id 和 sign 两部分。
  • id 和 sign 是服务器识别和验证图片的关键信息。

获取图片 URL:

  • 函数调用 getImage({id, sign}),这是一个异步操作,会向服务器请求,通过 id 和 sign 获取图片的实际 URL。
  • getImage 函数返回一个 Promise,成功时会解析出图片的 URL。

设置图片 src

  • 当 URL 获取成功后,函数将该 URL 设置为图片元素的 src 属性,使图片可以正确显示。

在组件中的应用

handleImage 函数遍历编辑器中的所有图片元素,根据 data-href 属性中的标识符,从服务器获取图片的实际 URL,并将其应用到图片元素上。

代码示例:

const handleImage = (dom) => {if (dom instanceof Element) {dom.querySelectorAll('img').forEach((element) => {const href = element.getAttribute('data-href');
      if (href && href.includes('&')) {const [id, sign] = href.split('&');
        if (id && sign) {getImage({ id, sign}).then((url) => {element.src = url; // 设置图片的 src 为实际 URL});
        }
      }
    });
  } else {console.warn('dom is not Element');
  }
};

完整组件代码

最后,让我们把所有的关键点结合起来,看看这个富文本编辑器组件的完整实现:

结语

通过对该 Vue 组件的深入解析,我们看到了它如何利用 Vue 3 的特性和 @wangeditor/editor-for-vue 库实现一个功能丰富的富文本编辑器。组件采用了 shallowRef 来管理编辑器实例,使用 v-model 机制和自定义事件实现了双向数据绑定,并通过异步操作和事件监听实现了图片上传和展示的自定义处理。

这种实现方式不仅满足了基本的文本编辑需求,还提供了高度的可定制性,适用于各种复杂的 Web 应用场景。如果你正在开发一个需要富文本编辑器的项目,可以参考本文的实现思路,结合实际需求进行扩展和优化。

原文地址: Vue 3 富文本编辑器组件详解

    正文完
     0
    Yojack
    版权声明:本篇文章由 Yojack 于2024-11-04发表,共计7841字。
    转载说明:
    1 本网站名称:优杰开发笔记
    2 本站永久网址:https://yojack.cn
    3 本网站的文章部分内容可能来源于网络,仅供大家学习与参考,如有侵权,请联系站长进行删除处理。
    4 本站一切资源不代表本站立场,并不代表本站赞同其观点和对其真实性负责。
    5 本站所有内容均可转载及分享, 但请注明出处
    6 我们始终尊重原创作者的版权,所有文章在发布时,均尽可能注明出处与作者。
    7 站长邮箱:laylwenl@gmail.com
    评论(没有评论)