Tiptap Vue3菜单栏按钮激活异常:isActive方法Promise报错排查
Tiptap Vue3 富文本编辑器
isActive() Promise 错误修复 错误原因分析
isActive()误用异步:Tiptap的isActive()是同步方法,无需用async/await包裹,异步返回的Promise无法被Vue的:class绑定直接解析,导致报错。- Editor 初始化时机问题:
useEditor()返回的是异步初始化的Ref对象,组件渲染初期可能为undefined,此时调用editor.isActive()会触发undefined读取属性的错误。 - MenuItem 复用性缺陷:原代码中
isActive()硬编码检查'bold',导致Italic按钮也会判断Bold状态,不符合需求。
修复方案
1. 修改 MenuItem.vue,支持动态检查类型并处理Editor初始化状态
<template> <button class="menu-item" :class="{ 'is-active bg-zinc-800 rounded-sm': isActive }" @click="(e) => { e.preventDefault(); action(); }" :title="title" > <i :class="`ri-${icon} ri-fw`"></i> </button> </template> <script> import 'remixicon/fonts/remixicon.css'; export default { props: { icon: { type: String, required: true, }, title: { type: String, required: true, }, action: { type: Function, required: true, }, editor: { type: Object, required: false, // 允许初始为undefined }, type: { // 添加type属性,支持动态检查不同格式 type: String, required: true, } }, computed: { isActive() { // 先判断editor是否存在,再调用isActive return this.editor?.isActive(this.type) || false; } } } </script>
2. 更新 MenuBar.vue,传递格式类型给MenuItem
<template> <div class="text-xl"> <template v-for="(item, index) in items"> <span class="divider" v-if="item.type === 'divider'" :key="`divider${index}`" > | </span> <!-- 仅在editor存在时渲染MenuItem,避免初始undefined传递 --> <menu-item class="p-1" :editor="editor" v-else :key="index" v-bind="item" v-if="editor" /> </template> </div> </template> <script> import MenuItem from "@/components/new_text_editor/MenuItem.vue"; export default { components: { MenuItemItem, }, props: { editor: { type: Object, required: true } }, data() { return { items: [ { icon: "bold", title: "Bold", type: "bold", // 添加type字段 action: () => this.editor.chain().focus().toggleBold().run(), }, { icon: "italic", title: "Italic", type: "italic", // 添加type字段 action: () => this.editor.chain().focus().toggleItalic().run(), }, ] } }, } </script>
3. 优化 Editor.vue,确保Editor初始化完成后再渲染菜单栏
<template> <!-- 用v-if="editor"等待Editor初始化完成 --> <menu-bar :editor="editor" v-if="editor"></menu-bar> <editor-content class="border rounded-md" :editor="editor" v-if="editor" /> </template> <script setup> import {EditorContent, useEditor} from '@tiptap/vue-3' import StarterKit from '@tiptap/starter-kit' import TableHeader from "@tiptap/extension-table-header"; import Link from "@tiptap/extension-link"; import MenuBar from "@/components/new_text_editor/MenuBar.vue"; import BulletList from "@tiptap/extension-bullet-list"; import Bold from "@tiptap/extension-bold"; const CustomBulletList = BulletList.extend({ addCommands() { return { ...this.parent?.(), toggleBulletClass: (className) => ({ commands }) => { return commands.updateAttributes("bulletList", { class: className }); }, }; }, }); const CustomTableHeader = TableHeader.extend({ addAttributes() { return { ...this.parent?.(), colwidth: { default: null, parseHTML: (element) => { const colwidth = element.getAttribute("colwidth"); return colwidth ? [parseInt(colwidth, 10)] : null; }, renderHTML: (attributes) => { return { colwidth: attributes.colwidth, style: attributes.colwidth ? `width: ${attributes.colwidth}px` : null, }; }, }, }; }, }); const CustomLink = Link.extend({ addAttributes() { return { ...this.parent?.(), rel: { default: null, }, }; }, }); const editor = useEditor({ content: '', extensions: [ StarterKit, CustomBulletList, CustomTableHeader, CustomLink, Bold, ], editorProps: { attributes: { class: 'prose dark:prose-invert prose-sm sm:prose-base lg:prose-lg xl:prose-2xl m-5 focus:outline-none' } } }) </script>
关键修复点总结
- 移除
isActive()的async/await,改用同步计算属性判断状态 - 添加
type属性让MenuItem支持多种格式的状态检查 - 在父组件中用
v-if="editor"确保Editor初始化完成后再渲染子组件 - 使用可选链操作符
?.避免undefined读取属性的错误
内容的提问来源于stack exchange,提问作者code writer 3000
相关产品推荐
相关产品推荐

