#8211·naive-ui

[2.45.x 回归] 自闭合 <n-tab-pane tab="xxx" /> 的标签文字不显示

Author: Barry-FlynnCreated Sep 2, 2026Updated Sep 2, 2026
Labelsuntriaged

描述错误

[2.45.x 回归] 自闭合 的标签文字不显示: vue-jsx-vapor/vdom.mjs 的 normalizeSlots 对 undefined 生成了幽灵 default 插槽

(英文备用:[Regression] Label text of self-closing <n-tab-pane tab="..."> is not rendered in 2.45.x — vendored normalizeSlots in vue-jsx-vapor/vdom.mjs creates a phantom default slot from undefined

问题描述

使用文档推荐的自闭合写法给 tab 传文字时,标签栏渲染出来了、选中态与内容面板 也正常,但标签文字是空的:

xml
<n-tabs>
  <n-tab-pane name="all" tab="全部" />
  <n-tab-pane name="normal" tab="正常" />
</n-tabs>

实际 DOM(data-name 正常,label 里只有空注释):

xml
<div data-name="all" class="n-tabs-tab n-tabs-tab--active">
  <span class="n-tabs-tab__label"><!----></span>
</div>

复现步骤

根因分析

完整链路(以下行号均为发布的 es 构建产物中的位置):

  1. es/tabs/src/Tabs.mjs:10normalizeSlots 不是从 vue 导入的, 而是从 ../../vue-jsx-vapor/vdom.mjs(为 Vapor 模式 vendored 的运行时 shim)导入,且非 Vapor 模式也走这条路。
  2. Tabs.mjs:742 对每个 pane 生成 NTab vnode 时传入 normalizeSlots(tabPaneVNode.children ? { default: tabPaneVNode.children.tab } : void 0); 自闭合 pane 没有 children,等价于 normalizeSlots(undefined)
  3. vendored 版本(vue-jsx-vapor/vdom.mjs:33-37)与 Vue 官方语义不一致: 对非函数、非对象的输入(含 undefined)统一返回 { default: withCtx(() => [normalizeVNode(() => slots)]) } —— 一个渲染空注释 <!----> 的"幽灵" default 插槽。 Vue 官方行为下,children 为 undefined 的 vnode 没有 default 插槽 ($slots.default === undefined)。
  4. es/tabs/src/Tab.mjs:125-127 的 label 渲染分支是 defaultSlot ? 渲染插槽 : render(mergedTab ?? name); 由于幽灵插槽存在,走了渲染插槽的分支,tab prop 的文字被空注释取代。

顺带提醒:这个 vendored normalizeSlots 与 Vue 官方 normalizeObjectSlots 对 undefined 的语义不一致,其他组件中类似对 undefined 的调用可能也有 同类隐患,建议一并排查。

预期行为

标签栏显示 tab prop 的文字("全部"、"正常")。

实际行为

标签栏渲染但 label 为空(<!---->)。

复现步骤

  1. pnpm create vite repro --template vue-ts,安装 [email protected][email protected]
  2. 任意组件中使用:
xml
<script setup lang="ts">
import { NTabs, NTabPane } from "naive-ui";
</script>

<template>
  <n-tabs>
    <n-tab-pane name="all" tab="全部" />
    <n-tab-pane name="normal" tab="正常" />
  </n-tabs>
</template>
  1. 打开页面:两个 tab 都渲染但没有文字;DevTools 中 .n-tabs-tab__label 内是 <!---->
  2. 把自闭合 pane 换成 <n-tab name="all">全部</n-tab>,文字正常显示。

临时绕过方案

<n-tab name="xxx">文字</n-tab> 替代自闭合 n-tab-paneTabs.mjs:745-750 的 tabChildren 分支直接渲染 vnode,不经过 normalizeSlots),或给 pane 加默认内容。

建议修复方向

让 vendored normalizeSlots 与 Vue 官方语义对齐(undefined 返回空对象), 或在 Tabs.mjs 调用处显式判空,不要在 children 缺失时传入 undefined。

最小复现链接

系统信息

bash
- naive-ui:2.45.3
- vue:3.5.42
- 构建:Vite + `<script setup>`,普通 Vue 3 应用(未启用 Vapor 模式)

使用的包管理器

pnpm

验证