在过去的几天里,我一直在做一个带有导航栏的项目,用TypeScript和React(Next.js)实现。到目前为止,我一直使用单级导航,但我正在向多级导航菜单迈进,我需要一些帮助。
初始代码:单级菜单
我最初写的代码是一个单级菜单,即一个简单的导航栏没有子菜单,只包含顶层的链接。代码如下:
1)型号/类型定义
// @/models/NavLinks.d.ts
export interface NavItem {
title: string;
link: string;
ariaLabel?: string;
}
export interface NavItemCollection {
title?: string; // Titles are used in Sidebar, not in main Navbar in this example
items: Array<NavItem>;
}
export type NavItemCollections = NavItemCollection[];
字符串
2)数据:
// @/data/MainNav.ts
import { NavItemCollections } from "@/models/NavLinks";
export const NavLinks: NavItemCollections = [
{
items: [
{ title: "About", link: "/about" },
{ title: "Security", link: "/security" },
{ title: "Blog", link: "/blog" },
],
},
];
型
3)用法:
// @/components/site/Navbar.tsx;
import Link from "next/link";
import { NavItemCollections } from "@/models/NavLinks";
import styles from "@/styles/Navbar.module.scss";
export interface NavbarProps {
links: NavItemCollections;
}
export const Navbar = (props: NavbarProps) => {
return (
<>
<nav>
{props.links.map((collection) =>
collection.items.map((item, index) => (
<Link href={item.link} key={index}>
<a className={styles.link}>{item.title}</a>
</Link>
))
)}
</nav>
</>
);
}
型
.然后使用指定的prop传递链接。即父组件中的<Navbar links={links} />
。
P.S.请记住,Next.js要求我将<a>
Package 在<Link>
中。
我的目标:多级菜单
我个人的目标是重构整个菜单,这样我(和其他人)就有一种统一的方法来实现一个结构良好、类型良好的导航菜单,可以在多个地方使用。
为了做到这一点,我从VuePress 2借用了一些代码,它在默认主题中使用了类似的东西。
1)型号/类型定义
// @/models/NavLinks.d.ts
/**
* Base nav item, displayed as text
*/
export interface NavItem {
text: string;
ariaLabel?: string;
}
/**
* Base nav group, has nav items children
*/
export interface NavGroup<T> extends NavItem {
children: T[];
}
/**
* NavLink, i.e. inherited from NavItem, but extended with link properties.
*/
export interface NavLink extends NavItem {
link: string;
rel?: string;
target?: string;
}
/**
* Navbar Contents
*/
export type NavbarItem = NavLink;
export type NavbarGroup = NavGroup<NavbarGroup | NavbarItem | string>;
export type NavbarContents = (NavbarItem | NavbarGroup | string)[];
型
2)数据
在我们的新模型中,我们可以创建一个多级菜单,比如带子菜单的菜单项,带子菜单的菜单项和可选的子菜单项,等等。换句话说:一个嵌套的导航结构。酷。
// @/data/MainNav.ts
import { NavbarContents } from "@/models/NavLinks";
export const NavLinks: NavbarContents = [
{ text: "About", link: "/about" },
{ text: "Security", link: "/security" },
{ text: "Blog", link: "/blog" },
{
text: "Professional",
children: [
{ text: "Individual", link: "/professional" },
{ text: "Business", link: "/business" },
],
},
];
型
3)用法:
这就是我卡住的地方。如果我使用与前面所示相同的.map()
方法,我会得到以下错误:
Property 'map' does not exist on type 'string | NavLink | NavbarGroup'.
Property 'map' does not exist on type 'string'.ts(2339)
型
这个错误是很明显的,因为你不能在string
类型上使用.map
,但是我应该用什么来代替呢?我试图使用typeof
来判断它是否是一个字符串。然后只要呈现字符串就可以了。
<nav className={styles.navlinks}>
{props.links.map((collection) =>
typeof (collection) === "string" ?
<span className="string">{collection}</span>
: (...)
型
剩下的不是NavLink
就是NavbarGroup
,不知道怎么实现。
问题是:我如何使用上面的类型定义和数据来拥有一个嵌套菜单?
1条答案
按热度按时间4nkexdtk1#
需要在它要求的地方添加类型信息。你可以使用递归方法来呈现菜单,如下所示。
字符串
你可以根据你的菜单项类型来扩展这个代码。当你这样做的时候,它将被设置为开关情况,而不是
<a href={menuItem?.link}>{menuItem?.text}</a>
,它适用于NavbarItem
。代码沙盒=> https://codesandbox.io/s/elated-smoke-zzxt4?file=/src/App.tsx