Vue3组合式API中Tiptap编辑器循环渲染失效问题求助
解决Tiptap编辑器循环渲染仅显示一个的问题
你遇到的核心问题是所有<editor-content>组件复用了同一个Editor实例。Tiptap的Editor实例会和单个DOM节点绑定,复用实例只会让最后一个(或第一个)组件渲染出编辑器,其他组件无法正常显示。必须给每个教育经历项创建独立的Editor实例,下面提供两种可行方案:
方案1:封装独立编辑器子组件(推荐)
这种方式符合Vue组件化思想,便于维护和复用:
1.1 创建EduEditor.vue子组件
<template> <editor-content class="editor" :editor="editor" /> </template> <script setup> import { ref, watch } from 'vue' import { useEditor, EditorContent } from '@tiptap/vue-3' import StarterKit from '@tiptap/starter-kit' import Underline from '@tiptap/extension-underline' import TextAlign from '@tiptap/extension-text-align' import Document from '@tiptap/extension-document' // 接收外部传入的内容和更新事件 const props = defineProps({ modelValue: { type: String, default: '' } }) const emit = defineEmits(['update:modelValue']) // 创建当前组件专属的Editor实例 const editor = useEditor({ content: props.modelValue, extensions: [ StarterKit, Underline, Document, TextAlign.configure({ types: ['heading', 'paragraph'], }) ], // 编辑器内容更新时同步到父组件 onUpdate: ({ editor }) => { emit('update:modelValue', editor.getHTML()) } }) // 监听父组件内容变化,同步到编辑器 watch(() => props.modelValue, (newVal) => { if (newVal !== editor.value?.getHTML()) { editor.value?.commands.setContent(newVal) } }) </script>
1.2 父组件中使用子组件
修改循环渲染部分,替换为子组件,并使用唯一标识作为key(避免用index,防止列表渲染异常):
<!-- 父组件模板 --> <div v-for="item in education.fields" :key="item.id"> <edu-editor v-model="item.desc" /> </div>
1.3 修改添加字段函数(生成唯一ID)
const addingEduField = () => { education.value.fields.push({ id: Date.now().toString(), // 生成唯一标识 edu: "", school: "", city: "", startDate: { month: "", year: "" }, endDate: { month: "", year: "", present: false }, desc: '', key: true, }); };
方案2:父组件内动态创建编辑器实例
如果不想封装子组件,可以直接在父组件中为每个字段创建独立实例:
2.1 修改父组件逻辑
import { ref, onBeforeUnmount } from 'vue' import { useEditor, EditorContent } from '@tiptap/vue-3' // 其他导入保持不变 const education = ref({ inputText: "", menu: false, self: false, fields: [], }); // 为单个字段创建编辑器实例 const createEditor = (field) => { const editor = useEditor({ content: field.desc, extensions: [ StarterKit, Underline, Document, TextAlign.configure({ types: ['heading', 'paragraph'], }) ], onUpdate: ({ editor }) => { field.desc = editor.getHTML() } }) field.editorInstance = editor return editor } // 添加字段时同时创建编辑器 const addingEduField = () => { const newField = { id: Date.now().toString(), edu: "", school: "", city: "", startDate: { month: "", year: "" }, endDate: { month: "", year: "", present: false }, desc: '', key: true, } createEditor(newField) education.value.fields.push(newField) } // 组件销毁前销毁所有编辑器,避免内存泄漏 onBeforeUnmount(() => { education.value.fields.forEach(field => { field.editorInstance?.value?.destroy() }) })
2.2 父组件模板修改
<div v-for="item in education.fields" :key="item.id"> <editor-content class="editor" :editor="item.editorInstance" /> </div>
核心原理说明
Tiptap的Editor实例与DOM节点是一一绑定的,当多个EditorContent组件使用同一个实例时,实例只会关联最后完成挂载的DOM节点,导致其他组件无法渲染编辑器。给每个教育项分配独立实例,才能保证每个编辑器正常工作。
内容的提问来源于stack exchange,提问作者abd elmalik abd elghafar
相关产品推荐
相关产品推荐

