Nuxt UI v4 FooterColumns 组件实战指南:用数据驱动构建页脚链接列
Nuxt UI v4 FooterColumns 组件实战指南:用数据驱动构建页脚链接列
FooterColumns 是 Nuxt UI v4 中用于在页脚区域渲染"多列链接列表"(典型如站点地图、社区链接、产品导航)的导航类组件。本指南将围绕 FooterColumns 官方文档 展开,结合仓库中的 组件源码、主题定义 与 测试用例,系统讲解它的定位、用法、数据模型、插槽体系与主题定制方式。读完本文,你将掌握如何用一份纯数据配置出一套完整、可访问、可深度定制的页脚链接列。
FooterColumns 是什么:为页脚而生的链接列容器
从官方文档的定位看,FooterColumns "renders a list of columns to display in your Footer",即渲染一组用于展示在页脚中的链接列。它属于 navigation 类别的组件,关键词包括 footer links、sitemap、columns,是搭建页脚导航(Footer + Sitemap)的常用载体。
在组件层级上,它并不是独立出现的,而是设计为配合 Footer 组件 使用——官方文档明确要求把它放进 Footer 组件的 top 插槽中。这一点在 Footer 源码 中可以看到印证:Footer 会在根节点下渲染 top、bottom 两个插槽区,以及内部的 right、center、left 三个布局区,FooterColumns 正是被设计用来填充 top 区域的。
基础用法:放进 Footer 的 top 插槽
官方文档给出的最小使用方式如下:
<template>
<UFooter>
<template #top>
<UContainer>
<UFooterColumns />
</UContainer>
</template>
</UFooter>
</template>
要点说明:
- 组件放在
UFooter的#top插槽内,外部通常再包一层UContainer以限制内容宽度、保持与页面上方内容区对齐。 - 从 Footer 主题 可以看到,
top插槽区默认带有py-8 lg:py-12的纵向内边距,因此FooterColumns自身无需再关心与页脚的间距问题。 FooterColumns默认渲染为nav语义元素(见源码中withDefaults(..., { as: 'nav' })),符合导航区域的语义化要求,无需额外包裹<nav>。
用 columns 数据驱动列与链接
FooterColumns 的核心设计是"数据驱动":通过 columns prop 传入结构化的列与链接数据,组件自动完成渲染。
FooterColumn:列的结构
columns 是一个对象数组,每项代表一列,类型定义在 FooterColumns 源码 中:
export interface FooterColumn<T extends FooterColumnLink = FooterColumnLink> {
label: string
children?: T[]
}
label: string(必填):列标题,渲染为<h3>标题元素。children?: FooterColumnLink[]:该列下的链接列表,可选。
FooterColumnLink:链接的结构
children 数组中的每个链接对象,其类型为 FooterColumnLink,支持以下属性:
export interface FooterColumnLink extends Omit<LinkProps, 'custom'> {
label: string
icon?: IconProps['name']
class?: any
ui?: Partial<Pick<FooterColumns['slots'], 'item' | 'link' | 'linkLabel' | 'linkLabelExternalIcon' | 'linkLeadingIcon'>>
}
label: string:链接文案。icon?: string:链接前导图标,使用 Iconify 图标名(源码中以@IconifyIcon标注,例如i-lucide-users)。class?: any:附加到该链接根元素的额外类名。ui?: object:针对单个链接的细粒度样式覆盖,可覆盖item、link、linkLabel、linkLabelExternalIcon、linkLeadingIcon五个主题插槽。
透传 Link 组件属性
官方文档特别说明:链接对象可以透传 Link 组件 的任何属性,例如 to、target、href、exact、active、rel、prefetch 等。这是因为 FooterColumnLink 类型本身继承了 Omit<LinkProps, 'custom'>,在渲染时源码通过 pickLinkProps(link) 从数据中精确提取出所有 Link 相关属性再绑定到 ULink 上(见 FooterColumns.vue),不会污染外层容器。
完整可运行示例
将列数据写在 script 中并绑定到组件上:
<script setup lang="ts">
import type { FooterColumn } from '#ui' // 或从 '@nuxt/ui' 导入类型
const columns: FooterColumn[] = [{
label: 'Community',
children: [{
label: 'Nuxters',
to: 'https://nuxters.nuxt.com',
target: '_blank'
}, {
label: 'Nuxt on GitHub',
to: 'https://github.com/nuxt',
target: '_blank',
icon: 'i-lucide-github'
}]
}, {
label: 'Enterprise',
children: [
{ label: 'Support' },
{ label: 'Agencies' },
{ label: 'Sponsors' }
]
}]
</script>
<template>
<UFooter>
<template #top>
<UContainer>
<UFooterColumns :columns="columns" />
</UContainer>
</template>
</UFooter>
</template>
上述示例中,没有 to 属性的链接会渲染为纯文本占位项;带有 to 与 target: '_blank' 的链接则具备完整的外部跳转行为。该结构与 测试用例 中构造的 Community / Enterprise / Solutions 三列数据模型完全一致,可以直接对照参考。
外部链接自动识别与 external 图标
一个值得注意的细节是:当链接的 target === '_blank' 时,组件会自动在链接文案尾部渲染一个"外部链接"小图标,提示用户该链接将在新窗口打开。相关逻辑位于 FooterColumns.vue:
<UIcon v-if="link.target === '_blank'" :name="appConfig.ui.icons.external" data-slot="linkLabelExternalIcon" ... />
- 图标名称取自全局
appConfig.ui.icons.external配置,而非硬编码,因此你可以在应用配置中全局替换该图标。 - 该图标通过
linkLabelExternalIcon主题插槽控制样式,默认渲染为右上角size-3的小图标(见 footer-columns.ts)。
插槽体系:从整体布局到单个链接的全面可定制
FooterColumns 提供了丰富的插槽体系,类型定义在源码 FooterColumnsSlots 中,可实现从整体布局到单个链接的逐级定制:
| 插槽 | 作用 | 插槽参数(slot props) |
|---|---|---|
left |
左侧区域,独立于列之外(如品牌区) | 无 |
default |
中心区域,默认渲染所有列;提供后可完全替换列渲染 | 无 |
right |
右侧区域(如订阅表单、社交按钮) | 无 |
column-label |
自定义单列标题 | { column: FooterColumn<T> } |
link |
完全自定义单个链接的整体渲染 | { link, active, ui } |
link-leading |
自定义链接前导内容(默认放图标) | { link, active, ui } |
link-label |
自定义链接文案部分 | { link, active } |
link-trailing |
自定义链接尾部内容 | { link, active } |
其中 active 表示当前链接是否与当前路由匹配(由内部 ULink 计算并透出),可用于实现"当前页面高亮"等交互效果。ui 参数则是合并后的主题对象,方便在插槽内继续复用主题类名。
一个实际应用是:在 right 插槽中放置订阅表单、在 column-label 中给特定列加徽标,仓库中的 FooterColumnsExample.vue 就演示了 right 插槽的用法(见下文完整示例)。
主题定制:slots 与 variants
默认主题结构
FooterColumns 的默认样式定义在 src/theme/footer-columns.ts,共包含 10 个插槽(slot)与一组 active 变体:
| 插槽 | 默认类 | 作用 |
|---|---|---|
root |
xl:grid xl:grid-cols-3 xl:gap-8 |
根容器,大屏下三栏网格 |
left |
mb-10 xl:mb-0 |
左侧区域 |
center |
flex flex-col lg:grid grid-flow-col auto-cols-fr gap-8 xl:col-span-2 |
列区域,移动端纵向堆叠、桌面端横向等分布局 |
right |
mt-10 xl:mt-0 |
右侧区域 |
label |
text-sm font-semibold |
列标题 <h3> |
list |
mt-6 space-y-4 |
链接列表 <ul>,垂直间距 |
item |
relative |
每个链接项 <li> |
link |
group text-sm flex items-center gap-1.5 rounded-sm outline-primary/25 focus-visible:outline-3 |
链接本体,含键盘焦点可见性样式 |
linkLeadingIcon |
size-5 shrink-0 |
前导图标 |
linkLabel |
truncate |
链接文案,超长截断 |
linkLabelExternalIcon |
size-3 absolute top-0 text-dimmed inline-block |
外部链接图标 |
active 变体
当链接匹配当前路由(active 为 true)时:
active: true→link变为text-primary font-medium(主题色加粗);active: false→link变为text-muted hover:text-default,并在启用theme.transitions时附加transition-colors过渡效果。
这一行为与 Link 组件源码 的激活状态计算机制一致:FooterColumns 内部通过 ULink 的 custom 模式拿到 active 状态,再把它传给 ui.link({ active }) 驱动变体切换。
定制方式
与 Nuxt UI 其他组件一致,FooterColumns 支持两种主题覆盖方式:
- 全局配置:在
app.config.ts中通过ui.footerColumns覆盖任意插槽或变体(源码通过useComponentProps与useAppConfig自动合并,见 FooterColumns.vue)。 - 组件级:通过
uiprop 覆盖,例如ui="{ list: 'lg:gap-1.5' }"(测试用例中即有此用法)。 - 单链接级:通过链接对象中的
ui属性覆盖(上文已介绍)。
源码实现解析:数据如何变成 DOM
从 FooterColumns.vue 的模板可以还原出它的渲染链路:
Primitive(:as="props.as") → 默认 <nav>
├── left 区域(存在 #left 插槽时渲染)
├── center 区域
│ ├── 默认插槽内容
│ │ └── 每个 column:<h3> 标题 + <ul>
│ │ └── 每个 link:ULink(custom) → ULinkBase
│ │ ├── link-leading(默认渲染 UIcon)
│ │ ├── link-label(文案 + 外部链接图标)
│ │ └── link-trailing
└── right 区域(存在 #right 插槽时渲染)
几个值得注意的实现细节:
- 组件根节点使用 Reka UI 的
Primitive,asprop 默认值为'nav',可切换为'section'、'div'等任意元素或组件(测试用例with as即验证了as: 'section'场景)。 - 链接渲染采用
ULink的custom模式 +ULinkBase组合:ULink负责路由匹配、预取(prefetch)、rel/target处理,ULinkBase负责实际的元素与样式输出。这意味着页脚链接继承了 Link 组件 的全部能力,如内部/外部链接自动识别、激活态计算、i18n locale 路径等。 pickLinkProps(link)保证只有 Link 相关属性被透传,数据中携带的class、ui则单独消费,避免属性冲突。
可访问性与测试保障
FooterColumns 的可访问性是有测试背书的。在 test/components/FooterColumns.spec.ts 中:
- 组件以
axe进行无障碍审计,toHaveNoViolations()断言通过; - 通过
renderEach对全部 props(columns、as、class、ui)与全部插槽(left、default、right、column-label、link、link-leading、link-label、link-trailing)逐一渲染快照,覆盖了本指南介绍的所有 API 面。
语义层面,列标题使用 <h3>、列表使用 <ul>/<li>、外层为 <nav>,默认就具备良好的文档结构与屏幕阅读器体验;链接的 focus-visible:outline-3 样式也保证了键盘可达性。
实战示例:页脚 + 多列导航 + 订阅表单
将上述能力组合起来,即可得到官网同款的页脚结构——三列导航数据 + 右侧订阅表单(参考仓库中的 FooterColumnsExample.vue):
<script setup lang="ts">
import type { FooterColumn } from '#ui'
const columns: FooterColumn[] = [{
label: 'Community',
children: [{
label: 'Nuxters',
to: 'https://nuxters.nuxt.com',
target: '_blank'
}, {
label: 'Video Courses',
to: 'https://masteringnuxt.com/nuxt3?ref=nuxt',
target: '_blank'
}, {
label: 'Nuxt on GitHub',
to: 'https://github.com/nuxt',
target: '_blank'
}]
}, {
label: 'Solutions',
children: [{
label: 'Nuxt Content',
to: 'https://content.nuxt.com/',
target: '_blank'
}, {
label: 'Nuxt DevTools',
to: 'https://devtools.nuxt.com/',
target: '_blank'
}, {
label: 'Nuxt Image',
to: 'https://image.nuxt.com/',
target: '_blank'
}, {
label: 'Nuxt UI',
to: 'https://ui.nuxt.com/',
target: '_blank'
}]
}]
</script>
<template>
<UFooter>
<template #top>
<UContainer>
<UFooterColumns :columns="columns">
<template #right>
<UFormField name="email" label="Subscribe to our newsletter" size="lg">
<UInput type="email" class="w-full">
<template #trailing>
<UButton type="submit" size="xs" color="neutral" label="Subscribe" />
</template>
</UInput>
</UFormField>
</template>
</UFooterColumns>
</UContainer>
</template>
</UFooter>
</template>
这个示例覆盖了本指南的全部知识点:columns 数据驱动、target="_blank" 外部链接与自动 external 图标、right 插槽扩展订阅表单,以及 UFooter + UContainer 的标准组合。将这份模板稍作修改即可接入你自己的站点地图与导航数据,快速搭建出专业、可访问、可深度定制的页脚区域。
