You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Tiptap Vue3菜单栏按钮激活异常:isActive方法Promise报错排查

Tiptap Vue3 富文本编辑器 isActive() Promise 错误修复

错误原因分析

  1. isActive() 误用异步:Tiptap的isActive()是同步方法,无需用async/await包裹,异步返回的Promise无法被Vue的:class绑定直接解析,导致报错。
  2. Editor 初始化时机问题:useEditor()返回的是异步初始化的Ref对象,组件渲染初期可能为undefined,此时调用editor.isActive()会触发undefined读取属性的错误。
  3. 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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.27 15:10:04