在后台管理系统或 CMS 中,富文本编辑器是必不可少的一环。为了更好地控制交互细节、接口集成和样式定制,我们将基于 Vue 3 + TypeScript 封装一个可复用的富文本组件 —— JEditor
,并使用 WangEditor 官方提供的 Vue 适配包。
安装依赖
yarn add @wangeditor/editor-for-vue@next
# 或者 npm install @wangeditor/editor-for-vue@next --save
你也可以参考官方文档了解更多配置及插件信息:https://www.wangeditor.com/
完整组件源码
<script setup lang="ts">
import { Editor, Toolbar } from '@wangeditor/editor-for-vue'
import '@wangeditor/editor/dist/css/style.css'
import { IDomEditor, IEditorConfig, IToolbarConfig } from '@wangeditor/editor'
import { toolbarConfigDefault } from './defaultConfig'
import { ref, watch, shallowRef, onBeforeUnmount } from 'vue'
defineOptions({
name: 'JEditor',
inheritAttrs: false
})
type InsertFnType = (url: string, alt: string, href: string) => void
const props = defineProps({
editorId: {
type: String,
default: 'j-editor'
},
modelValue: {
type: String,
required: true
},
height: {
type: [String, Number],
default: '400px'
},
readonly: {
type: Boolean,
default: false
},
toolbarConfig: { type: Object as () => IToolbarConfig, default: () => ({}) }, // 工具栏配置
editorConfig: { type: Object as () => IEditorConfig, default: () => ({}) } // 编辑器配置
})
const emit = defineEmits(['update:modelValue', 'change'])
// 编辑器实例,必须用 shallowRef
const editorRef = shallowRef<IDomEditor | null>(null)
const valueHtml = ref('')
// 编辑器配置
const mergedConfig = {
placeholder: '请输入内容...',
readOnly: props.readonly,
MENU_CONF: {
// 上传图片配置
uploadImage: {
server: 'https://xxxxxxxx', //服务端地址
maxFileSize: 10 * 1024 * 1024, // 10M
// 自定义上传参数。参数会被添加到 formData 中,一起上传到服务端。
meta: {},
// 自定义增加 http header
headers: {
Accept: '*',
authorization: 'Bearer xxxxxxxx'
},
// 超时时间
timeout: 5 * 1000,
// form-data fieldName,后端接口参数名称,默认值wangeditor-uploaded-image
fieldName: 'file',
// 上传之前触发
onBeforeUpload(file: File) {
return file
},
// 自定义插入图片
customInsert(res: any, insertFn: InsertFnType) {
let d = res.data
// url(图片 src ,必须) alt(图片描述文字,非必须) href(图片的链接,非必须)
insertFn(d.fileUrl, d.fileName, d.fileUrl)
}
},
// 上传视频配置
uploadVideo: {}
},
...props.editorConfig
}
const toolbarConfig = computed(() => Object.assign({}, toolbarConfigDefault, props.toolbarConfig))
const editorStyle = computed(() => {
return {
height: isNumber(props.height) ? `${props.height}px` : props.height
}
})
// 初始化编辑器实例
function initEditor(editorInstance: IDomEditor) {
editorRef.value = editorInstance
}
// 监听modelValue变化
watch(
() => props.modelValue,
(newVal: string) => {
if (newVal !== valueHtml.value) {
valueHtml.value = newVal
}
},
{
immediate: true
}
)
watch(
() => valueHtml.value,
(val: string) => {
emit('update:modelValue', val)
}
)
// 编辑器内容变化回调
const handleChange = (editor) => {
emit('change', editor)
}
// 销毁编辑器
onBeforeUnmount(() => {
const editor = editorRef.value
if (editor == null) return
editor.destroy()
})
const isNumber = (val: any) => {
return typeof val === 'number' && !isNaN(val)
}
</script>
<template>
<div class="jMdEditor">
<Toolbar
:editorId="editorId"
:editor="editorRef"
:defaultConfig="toolbarConfig"
v-bind="$attrs"
mode="default"
/>
<Editor
:editorId="editorId"
v-model="valueHtml"
:defaultConfig="mergedConfig"
mode="default"
@onCreated="initEditor"
@onChange="handleChange"
:style="editorStyle"
/>
</div>
</template>
<style scoped lang="scss">
.jMdEditor {
border: 1px solid #ccc;
border-radius: 4px;
}
</style>
export const toolbarConfigDefault = {
toolbarKeys: [
'headerSelect', // 标题选择
'bold', // 加粗
'italic', // 斜体
'through', // 删除线
'underline', // 下划线
'justifyCenter', // 居中对齐
'justifyJustify', // 两端对齐
'justifyLeft', // 左对齐
'justifyRight', // 右对齐
'bulletedList', // 无序列表
'numberedList', // 有序列表
'color', // 文字颜色
'insertLink', // 插入链接
'fontSize', // 字体大小
'lineHeight', // 行高
'delIndent', // 缩进
'indent', // 增进
'divider', // 分割线
'insertTable', // 插入表格
'undo', // 撤销
'redo', // 重做
'clearStyle', // 清除格式
'fullScreen', // 全屏
'blockquote', // 引用
'codeBlock', // 代码块
'insertImage', // 插入图片
'uploadImage', // 上传图片
'insertVideo' // 插入视频
]
}
核心亮点解析
-
双向绑定与回显
使用v-model
+watch
保证外部modelValue
与内部valueHtml
同步,组件内容更改后自动向父组件派发更新事件。 -
编辑器实例管理
通过shallowRef
存储editorRef
,并在onBeforeUnmount
中销毁实例,避免内存泄漏。 -
高度可配置
-
props.editorConfig
/props.toolbarConfig
支持用户自定义全部配置。 -
图片、视频上传功能已集成,示例展示了自定义
customInsert
的典型用法。
-
-
样式灵活
height
属性既可接收数字(自动追加px
),也可传入字符串(如%
、rem
等),满足多种布局需求。
富文本编辑器的可扩展性非常强,你可以在此基础上进一步添加:
-
Markdown 支持
-
公式与图表插入
-
代码高亮插件
希望这篇文章能帮助你快速搭建一个可复用的富文本组件,让你的项目编辑体验更加专业、高效!