NavLink 导航项

导航列表中的一项,标示当前所在的页面。

<script setup lang="ts">
  import { BookOpen, Gamepad2, House, Library } from '@lucide/vue'
  import { NavLink, Stack } from '@hina-ui/vue'

  const items = [
    { icon: House, label: '首页', active: false },
    { icon: Gamepad2, label: 'Galgame', active: true },
    { icon: BookOpen, label: '轻小说', active: false },
    { icon: Library, label: '漫画', active: false },
  ]
</script>

<template>
  <Stack gap="none" as="nav" class="w-full max-w-56">
    <NavLink v-for="item in items" :key="item.label" href="#" :active="item.active">
      <template #icon><component :is="item.icon" /></template>
      {{ item.label }}
    </NavLink>
  </Stack>
</template>
vue

用法

import { NavLink } from '@hina-ui/vue'
ts

NavLink 是纵向导航列表中的一项,默认渲染为链接,整行都是点击区域。它自己不提供列表容器,外层用 Stack 或 nav 排布。文字只占一行,放不下时以省略号截断。

<script setup lang="ts">
  import { NavLink, Stack } from '@hina-ui/vue'
</script>

<template>
  <Stack gap="none" as="nav" class="w-full max-w-56">
    <NavLink href="#">概览</NavLink>
    <NavLink href="#">我的书架</NavLink>
    <NavLink href="#">收藏</NavLink>
  </Stack>
</template>
vue

它与 Link 的分工是明确的:Link 是正文行内的文字链接,随文字流动;NavLink 是导航结构中的一行,有固定行高与整行热区。正文里不要用 NavLink。

示例

当前项

active 标出读者当前所在的页面:字色回到正色并加重,背景同时浮现一层选中态底色。它还会给元素加上 aria-current="page",屏幕阅读器据此播报当前位置。

一组导航中同时只应有一项为 active。

<script setup lang="ts">
  import { NavLink, Stack } from '@hina-ui/vue'
</script>

<template>
  <Stack gap="none" as="nav" class="w-full max-w-56">
    <NavLink href="#">概览</NavLink>
    <NavLink href="#" active>我的书架</NavLink>
    <NavLink href="#">收藏</NavLink>
  </Stack>
</template>
vue

悬停于选中项时,悬停底色叠加在选中态底色之上,而非替换它,对比度只升不降,因此选中状态在悬停过程中不会丢失。

图标

icon 插槽放在文字之前,尺寸由组件统一为 4 个单位,不需要在图标上写尺寸类。图标是装饰,文字才是可读名称。

<script setup lang="ts">
  import { CircleUser, House, Settings } from '@lucide/vue'
  import { NavLink, Stack } from '@hina-ui/vue'
</script>

<template>
  <Stack gap="none" as="nav" class="w-full max-w-56">
    <NavLink href="#" active>
      <template #icon><House /></template>
      概览
    </NavLink>
    <NavLink href="#">
      <template #icon><CircleUser /></template>
      账号
    </NavLink>
    <NavLink href="#">
      <template #icon><Settings /></template>
      设置
    </NavLink>
  </Stack>
</template>
vue

尾部内容

trailing 插槽放在文字之后并贴在行末,用于页数、计数或快捷键。标题放不下时只截断标题,尾部内容保持完整;侧栏收起为 rail 时与文字一起隐藏。

<script setup lang="ts">
  import { NavLink, Stack, Text } from '@hina-ui/vue'

  const chapters = [
    { title: '序章', pages: '3 页' },
    { title: '第一章 雨停之后,站台上只剩下我们两个人', pages: '28 页', active: true },
    { title: '第二章 夏祭', pages: '19 页' },
    { title: '终章', pages: '6 页' },
  ]
</script>

<template>
  <Stack gap="none" as="nav" class="w-full max-w-56">
    <NavLink v-for="chapter in chapters" :key="chapter.title" href="#" :active="chapter.active">
      {{ chapter.title }}
      <template #trailing>
        <Text as="span" size="sm" tone="faint" class="tabular-nums">{{ chapter.pages }}</Text>
      </template>
    </NavLink>
  </Stack>
</template>
vue

路由链接

as 指定渲染为何种元素,默认为 a。传入路由组件即可获得客户端跳转,to 等属性会透传过去。当前项由调用方根据路由判断后传入 active,组件不自行匹配路由。

<script setup lang="ts">
  import { NuxtLink } from '#components'
  import { NavLink, Stack } from '@hina-ui/vue'

  const route = useRoute()
  const links = [
    { to: '/components/breadcrumb', label: 'Breadcrumb' },
    { to: '/components/nav-link', label: 'NavLink' },
    { to: '/components/splitter', label: 'Splitter' },
  ]
  const localePath = useLocalePath()
</script>

<template>
  <Stack gap="none" as="nav" class="w-full max-w-56">
    <NavLink
      v-for="link in links"
      :key="link.to"
      :as="NuxtLink"
      :to="localePath(link.to)"
      :active="route.path === localePath(link.to)"
    >
      {{ link.label }}
    </NavLink>
  </Stack>
</template>
vue

不可用

disabled 用于暂时不可进入的入口:该项降到半透明、不接受鼠标点击、退出键盘 tab 序列,并带上 aria-disabled="true"。

导航项长期不可用时应当直接移除,而不是保留一个不可用的入口。

<script setup lang="ts">
  import { NavLink, Stack } from '@hina-ui/vue'
</script>

<template>
  <Stack gap="none" as="nav" class="w-full max-w-56">
    <NavLink href="#" active>我的书架</NavLink>
    <NavLink href="#">收藏</NavLink>
    <NavLink href="#" disabled>创作中心</NavLink>
  </Stack>
</template>
vue

收起态

NavLink 放进 Sidebar 后会感知侧栏状态。侧栏收成 rail(只剩图标)时,文字淡出,label 的内容改由悬停提示给出,同时作为该项的无障碍名。

因此放进 Sidebar 的 NavLink 都应当写 label,否则收起后这一项既没有可见文字,也没有可读名称。脱离 Sidebar 时 label 不起作用,也不会渲染提示。用法见 Sidebar 的文档。

收起时导航文字较早淡出,展开时延后淡入。自定义页眉和页脚文字可用 SidebarLabel 保持相同过渡。

无障碍

  • 当前项带 aria-current="page",位置信息不依赖颜色。
  • 不可用项带 aria-disabled="true" 并退出 tab 序列。
  • 图标由组件标为装饰,不进入朗读序列;可读名称来自默认插槽的文字。
  • 收起成 rail 时,文字虽然不可见,但 label 会作为 aria-label 保留,朗读不受影响。
  • 组件本身不渲染 nav 地标,外层容器需要自行提供。

API

Props

属性
类型
默认值
说明
active
boolean
false
是否为当前页面
disabled
boolean
false
是否不可用
label
string
—
侧栏收起为 rail 时的提示文字与无障碍名
as
string | Component
'a'
渲染成的元素或组件
asChild
boolean
false
由插槽根元素承担渲染
class
string
—
追加到根元素的类

其余属性透传到实际渲染的元素上,例如 href 或路由组件的 to。

Slots

插槽
说明
default
项的文字,也是可读名称
icon
文字之前的图标
trailing
文字之后、贴在行末的内容