Vue3: 二次封装富文本编辑器-hook

初衷

基于WangEditor进行了一次特殊处理,使其更加易于使用和灵活。这个编辑器可以让你像写文字一样轻松地编辑富文本内容。不论你是要写一篇精彩的博客文章,还是设计一个华丽的网页,这个编辑器都能满足我们的需求。我使用了"Hook"的机制,可以自由地定制和控制编辑器的各种功能。

其实,主要是方便易用,在不同页面中,如果需要到富文本编辑器,如果这个时候我们把它封装成hook,那么我们就很轻易的复用某些功能点,可以省去很多重新编写逻辑的代码。

接下来我们先从WangEditor的使用文档做一下初步的了解

介绍

简洁易用,功能强大快速接入,配置简单,集成了几乎所有常见功能。在 Vue React 也可以快速接入,只需再做个hook就能更加灵活

如下几步以Vue3使用为例:

第一步:安装

bash 复制代码
yarn add @wangeditor/editor
# 或者 npm install @wangeditor/editor --save

yarn add @wangeditor/editor-for-vue@next
# 或者 npm install @wangeditor/editor-for-vue@next --save

第二步:使用模版

html 复制代码
<template>
    <div style="border: 1px solid #ccc">
      <Toolbar
        style="border-bottom: 1px solid #ccc"
        :editor="editorRef"
        :defaultConfig="toolbarConfig"
        :mode="mode"
      />
      <Editor
        style="height: 500px; overflow-y: hidden;"
        v-model="valueHtml"
        :defaultConfig="editorConfig"
        :mode="mode"
        @onCreated="handleCreated"
      />
    </div>
</template>

第三步:Script

js 复制代码
<script>
import '@wangeditor/editor/dist/css/style.css' // 引入 css

import { onBeforeUnmount, ref, shallowRef, onMounted } from 'vue'
import { Editor, Toolbar } from '@wangeditor/editor-for-vue'

export default {
  components: { Editor, Toolbar },
  setup() {
    // 编辑器实例,必须用 shallowRef
    const editorRef = shallowRef()

    // 内容 HTML
    const valueHtml = ref('<p>hello</p>')

    // 模拟 ajax 异步获取内容
    onMounted(() => {
        setTimeout(() => {
            valueHtml.value = '<p>模拟 Ajax 异步设置内容</p>'
        }, 1500)
    })

    const toolbarConfig = {}
    const editorConfig = { placeholder: '请输入内容...' }

    // 组件销毁时,也及时销毁编辑器
    onBeforeUnmount(() => {
        const editor = editorRef.value
        if (editor == null) return
        editor.destroy()
    })

    const handleCreated = (editor) => {
      editorRef.value = editor // 记录 editor 实例,重要!
    }

    return {
      editorRef,
      valueHtml,
      mode: 'default', // 或 'simple'
      toolbarConfig,
      editorConfig,
      handleCreated
    };
  }
}
</script>

配置

可通过 toolbarConfig 和 editorConfig 来修改菜单栏和编辑器的配置,详细文档参考

封装

依据介绍中的内容,进行二次封装成hook

步骤

封装wangEditor成hook,我是按照以下步骤进行:

1. 创建一个新的Hook组件

创建一个新的组件来封装wangEditor成为一个可复用的hook。你可以使用React的useState和useEffect等hook来管理编辑器的状态和生命周期。

2. 初始化编辑器:

在组件的useEffect钩子中,使用wangEditor的初始化方法来创建编辑器实例,并将其存储在状态中。

3. 添加事件监听:

使用wangEditor提供的方法,为编辑器添加事件监听器。这样可以捕获用户的输入、选择和其他操作。

4. 定义公共方法

在hook组件中定义一些公共方法,例如获取编辑器内容、插入内容等,以便在其他组件中使用。

5. 销毁编辑器:

使用wangEditor提供的销毁方法,在组件的清理函数中销毁编辑器实例,避免内存泄漏。

以上步骤只是一个大致的方案,具体实现可能会根据项目的需求和编辑器的配置有所差异。所以在封装过程中要注意参考wangEditor的官方文档和示例,以确保正确使用和配置编辑器。封装wangEditor成为一个hook后,你可以在其他组件中方便地使用它,简化并提高你的开发效率。

实践

那么我们就开始进行封装了

首先,创建一个editor.ts hook文件

暴露出这几个方法如下:

1.1. 编辑器容器

用于包裹编辑器工具栏和编辑器的容器

1.2. 编辑器实例

ts 复制代码
// 编辑器实例,必须用 shallowRef
  const editorRef = shallowRef();

当编辑器渲染完成之后,通过 editorRef.value 获取 editor 实例,即可调用它的 API

1.3. 工具栏配置

这个配置必不可少

可通过 toolbarConfig 和 editorConfig 来修改菜单栏和编辑器的配置,详细文档参考

那么,我们可以将其作为参数传入hook,便于灵活定制化,如下代码的编写:

创建一个函数,提供额外的参数配置

javascript 复制代码
export default function useEditor(
  options: EditorOptions = {
    config: {},
    toolbarConfig: {},
  },
) 

编写toolbarConfig 和 editorConfig 合并传进参数的方法

ini 复制代码
const editorToolbarConfig = merge(
		{
			excludeKeys: ['codeBlock', '|'],
		},
		options.toolbarConfig || {},
	);

将传入的toolbarConfig,与hook编写的公共默认参数部分合并。

1.4. 编辑器配置

editorConfig的配置

js 复制代码
const editorConfig = merge(
    {
        placeholder: '请输入正文,可插入图片、附件',
        MENU_CONF: {
            uploadImage: {
                // 自定义上传
                async customUpload(file: File, insertFn) {
                    // 限制大小
                    const maxFileSize = 10 * 1024 * 1024; // 10M
                    if (file.size > maxFileSize) {
                        await MessagePlugin.warning({ content: '请选择小于10M的图片' });
                        return;
                    }

                    const loading = LoadingPlugin({
                        attach: () => editorContainerRef.value,
                        showOverlay: true,
                        size: '20px',
                    });

                    try {
                        const result = (await upload(file)) as PutObjectResult;
                        insertFn(result.url, result.name, result.url);
                        loading.hide();
                    } catch (e) {
                        console.log(e);
                        loading.hide();
                    }
                },
            },
    },
    EXTEND_CONF: {
            mentionConfig: {
                // eslint-disable-next-line @typescript-eslint/no-empty-function
                showModal: () => {},
                // eslint-disable-next-line @typescript-eslint/no-empty-function
                hideModal: () => {},
            },
        },
    },
    options.config || {},
);

通过合并多个配置对象生成的editorConfig,代码中的图片上传方法,也是在此处进行配置好,

uploadImage 自定义上传的配置。在这个自定义上传配置中,首先对文件大小进行了限制,限制为10M以下的图片。如果超过了限制大小,会显示一个警告提示给用户。然后,它创建了一个loading对象,并在上传文件时显示loading动画。接着,通过upload函数上传文件,并将返回的结果(包括URL、名称等)传递给insertFn函数,这样就能将图片插入到编辑器中。最后,无论上传成功或失败,都会隐藏loading动画。

当然这里只是对图片上传做了配置,还可以根据wangEditor的相关文档,做深入的配置方法覆盖,以适配我们自己的后端服务接口以及业务逻辑。

当然官方文档也给有一个例子:

还有其他的自定义功能,这里就不作多的叙述

1.5. 销毁编辑器

在hook文件editor.ts的useEditor方法体里面写该销毁方法了

ini 复制代码
	// 组件销毁时,也及时销毁编辑器
	onBeforeUnmount(() => {
		const editor = editorRef.value;
		if (editor == null) return;
		editor.destroy();
	});

其次,使用useEditor

如图:

调用useEditor hook,并将配置参数传递给它。其中,toolbarConfig用于配置编辑器工具栏的显示和顺序,insertKeys指定了fontFamily工具的位置,excludeKeys指定了需要排除的工具项。config用于设置编辑器的占位符文本。

最后,将useEditor hook返回的结果解构赋值给之前定义的变量,以获取相关引用和配置信息, 代码:

js 复制代码
// 富文本
const { editorContainerRef, editorRef, editorToolbarConfig, editorConfig, editorCreated } = useEditor({
	toolbarConfig: {
		insertKeys: {
			index: 4,
			keys: ['fontFamily'],
		},
		excludeKeys: ['insertVideo', 'insertTable', 'fullScreen', 'blockquote', 'codeBlock', 'todo', '|'],
	},
	config: {
		placeholder: '请输入内容',
	},
});

最后,看效果:需要完整代码请在评论区留言,仅做参考

相关推荐
腾讯TNTWeb前端团队5 小时前
helux v5 发布了,像pinia一样优雅地管理你的react状态吧
前端·javascript·react.js
范文杰8 小时前
AI 时代如何更高效开发前端组件?21st.dev 给了一种答案
前端·ai编程
拉不动的猪9 小时前
刷刷题50(常见的js数据通信与渲染问题)
前端·javascript·面试
拉不动的猪9 小时前
JS多线程Webworks中的几种实战场景演示
前端·javascript·面试
FreeCultureBoy9 小时前
macOS 命令行 原生挂载 webdav 方法
前端
uhakadotcom10 小时前
Astro 框架:快速构建内容驱动型网站的利器
前端·javascript·面试
uhakadotcom10 小时前
了解Nest.js和Next.js:如何选择合适的框架
前端·javascript·面试
uhakadotcom10 小时前
React与Next.js:基础知识及应用场景
前端·面试·github
uhakadotcom10 小时前
Remix 框架:性能与易用性的完美结合
前端·javascript·面试
uhakadotcom10 小时前
Node.js 包管理器:npm vs pnpm
前端·javascript·面试