[2.45.x 回归] 自闭合 <n-tab-pane tab="xxx" /> 的标签文字不显示
描述错误
[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 传文字时,标签栏渲染出来了、选中态与内容面板 也正常,但标签文字是空的:
<n-tabs>
<n-tab-pane name="all" tab="全部" />
<n-tab-pane name="normal" tab="正常" />
</n-tabs>实际 DOM(data-name 正常,label 里只有空注释):
<div data-name="all" class="n-tabs-tab n-tabs-tab--active">
<span class="n-tabs-tab__label"><!----></span>
</div>复现步骤
根因分析
完整链路(以下行号均为发布的 es 构建产物中的位置):
es/tabs/src/Tabs.mjs:10的normalizeSlots不是从vue导入的, 而是从../../vue-jsx-vapor/vdom.mjs(为 Vapor 模式 vendored 的运行时 shim)导入,且非 Vapor 模式也走这条路。Tabs.mjs:742对每个 pane 生成 NTab vnode 时传入normalizeSlots(tabPaneVNode.children ? { default: tabPaneVNode.children.tab } : void 0); 自闭合 pane 没有 children,等价于normalizeSlots(undefined)。- vendored 版本(
vue-jsx-vapor/vdom.mjs:33-37)与 Vue 官方语义不一致: 对非函数、非对象的输入(含undefined)统一返回{ default: withCtx(() => [normalizeVNode(() => slots)]) }—— 一个渲染空注释<!---->的"幽灵" default 插槽。 Vue 官方行为下,children 为 undefined 的 vnode 没有 default 插槽 ($slots.default === undefined)。 es/tabs/src/Tab.mjs:125-127的 label 渲染分支是defaultSlot ? 渲染插槽 : render(mergedTab ?? name); 由于幽灵插槽存在,走了渲染插槽的分支,tabprop 的文字被空注释取代。
顺带提醒:这个 vendored normalizeSlots 与 Vue 官方 normalizeObjectSlots
对 undefined 的语义不一致,其他组件中类似对 undefined 的调用可能也有
同类隐患,建议一并排查。
预期行为
标签栏显示 tab prop 的文字("全部"、"正常")。
实际行为
标签栏渲染但 label 为空(<!---->)。
复现步骤
pnpm create vite repro --template vue-ts,安装[email protected]、[email protected];- 任意组件中使用:
<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>- 打开页面:两个 tab 都渲染但没有文字;DevTools 中
.n-tabs-tab__label内是<!---->; - 把自闭合 pane 换成
<n-tab name="all">全部</n-tab>,文字正常显示。
临时绕过方案
用 <n-tab name="xxx">文字</n-tab> 替代自闭合 n-tab-pane
(Tabs.mjs:745-750 的 tabChildren 分支直接渲染 vnode,不经过
normalizeSlots),或给 pane 加默认内容。
建议修复方向
让 vendored normalizeSlots 与 Vue 官方语义对齐(undefined 返回空对象),
或在 Tabs.mjs 调用处显式判空,不要在 children 缺失时传入 undefined。
最小复现链接
无
系统信息
- naive-ui:2.45.3
- vue:3.5.42
- 构建:Vite + `<script setup>`,普通 Vue 3 应用(未启用 Vapor 模式)使用的包管理器
pnpm
验证
Source: tusen-ai/naive-ui