Vue3 + wangeditor v5 实战:从零封装一个可复用的富文本编辑器组件
·
Vue3 + wangeditor v5 工程化实践:打造企业级富文本编辑器组件
在当今内容驱动的Web应用中,富文本编辑器已成为不可或缺的核心组件。不同于简单的文本输入框,一个成熟的富文本解决方案需要兼顾功能丰富性、性能稳定性和开发便捷性。本文将带你从工程化角度,基于Vue3的Composition API和wangeditor v5,构建一个可复用、易维护的企业级编辑器组件。
1. 项目架构设计与环境搭建
1.1 技术选型考量
在选择富文本编辑器时,我们需要权衡多个关键因素:
- 功能完整性 :支持常见的文本格式、图片上传、表格插入等
- 体积控制 :生产环境下的包大小影响
- API设计 :是否提供清晰的扩展接口
- 社区生态 :问题解决和持续维护的可能性
wangeditor v5作为国内主流开源编辑器,在这些方面表现出色。其模块化设计特别适合与Vue3的组合式API配合使用。
1.2 初始化项目结构
推荐采用monorepo方式组织代码,便于后续发布为独立npm包:
/editor-component
├── /packages
│ ├── /core # 编辑器核心逻辑
│ ├── /ui # 视图组件
│ └── /utils # 公共工具
├── /playground # 开发调试环境
├── vite.config.ts # 构建配置
└── package.json
安装核心依赖:
yarn add @wangeditor/editor @wangeditor/editor-for-vue
2. 核心逻辑封装与Composition API实践
2.1 创建可复用的编辑器逻辑
利用Composition API将编辑器核心逻辑抽离为独立hook:
// core/useEditor.ts
import { shallowRef, onBeforeUnmount } from 'vue'
import { IDomEditor } from '@wangeditor/editor'
export function useEditor() {
const editorRef = shallowRef<IDomEditor | null>(null)
const htmlContent = ref('')
const initEditor = (config: Partial<IEditorConfig> = {}) => {
// 初始化编辑器配置
}
const destroyEditor = () => {
editorRef.value?.destroy()
editorRef.value = null
}
onBeforeUnmount(destroyEditor)
return {
editorRef,
htmlContent,
initEditor,
destroyEditor
}
}
2.2 实现高度可配置的工具栏
通过props实现工具栏的动态配置:
interface ToolbarConfig {
excludeKeys?: string[]
insertKeys?: Array<{
key: string
title: string
icon: string
action: (editor: IDomEditor) => void
}>
}
const props = defineProps<{
toolbar: ToolbarConfig
uploadImage?: (file: File) => Promise<string>
}>()
3. 性能优化与内存管理
3.1 编辑器实例的生命周期控制
采用工厂模式管理编辑器实例:
class EditorManager {
private static instance: EditorManager
private editors: Map<string, IDomEditor> = new Map()
static getInstance() {
if (!EditorManager.instance) {
EditorManager.instance = new EditorManager()
}
return EditorManager.instance
}
register(id: string, editor: IDomEditor) {
this.editors.set(id, editor)
}
destroy(id: string) {
const editor = this.editors.get(id)
editor?.destroy()
this.editors.delete(id)
}
}
3.2 防抖处理与异步更新
对内容变化事件进行优化处理:
const handleChange = debounce((editor: IDomEditor) => {
htmlContent.value = editor.getHtml()
emit('update:modelValue', htmlContent.value)
}, 300)
4. 企业级功能扩展实践
4.1 实现自定义图片上传
封装通用的上传处理逻辑:
const handleImageUpload = async (file: File) => {
try {
const loadingKey = editorRef.value?.insertLoading(file.name)
const url = await props.uploadImage(file)
editorRef.value?.insertImage(url)
editorRef.value?.removeLoading(loadingKey)
} catch (error) {
editorRef.value?.alert('图片上传失败', 'error')
}
}
4.2 内容版本控制与撤销管理
集成操作历史管理:
const historyStack = ref<string[]>([])
const currentIndex = ref(-1)
watch(htmlContent, (newVal) => {
if (currentIndex.value < historyStack.value.length - 1) {
historyStack.value = historyStack.value.slice(0, currentIndex.value + 1)
}
historyStack.value.push(newVal)
currentIndex.value++
}, { deep: true })
const undo = () => {
if (currentIndex.value > 0) {
currentIndex.value--
editorRef.value?.setHtml(historyStack.value[currentIndex.value])
}
}
5. 组件发布与多项目复用
5.1 构建配置优化
配置vite打包输出多种模块格式:
// vite.config.ts
export default defineConfig({
build: {
lib: {
entry: 'src/index.ts',
name: 'VueWangeditor',
formats: ['es', 'umd', 'cjs'],
fileName: (format) => `vue-wangeditor.${format}.js`
}
}
})
5.2 自动化文档生成
使用TypeDoc生成API文档:
// typedoc.json
{
"entryPoints": ["src/index.ts"],
"out": "docs",
"excludeExternals": true,
"hideGenerator": true
}
6. 实际项目集成案例
6.1 与状态管理库协同工作
示例与Pinia的集成:
// stores/editor.ts
export const useEditorStore = defineStore('editor', {
state: () => ({
contentMap: new Map<string, string>()
}),
actions: {
saveContent(id: string, content: string) {
this.contentMap.set(id, content)
}
}
})
6.2 多语言支持实现
通过i18n实现工具栏国际化:
const { t } = useI18n()
const toolbarConfig = computed(() => ({
toolbarKeys: [
{ key: 'bold', title: t('editor.bold') },
{ key: 'italic', title: t('editor.italic') }
]
}))
在开发过程中发现,将编辑器实例的管理与组件生命周期解耦,可以显著提升在多tab应用中的性能表现。通过自定义hook封装核心逻辑,使得同一页面中的多个编辑器实例能够共享基础配置,同时保持各自独立的状态管理。
更多推荐
所有评论(0)