笔记来源:编程导航
基础布局
Next.js 支持全局根布局(每个页面都会生效)以及嵌套布局(可以只对部分页面生效),详情可 参考文档。
在 src 下新建 layouts 目录,用于存放项目中的各种布局。在该目录下新建一个布局 BasicLayout
, 是一个文件夹,包括 index.tsx 页面和 index.css 样式文件。
可以直接使用 Ant Design Procomponents 的布局组件 快速实现包含导航栏、内容、底部栏的响应式布局。
在 app 目录下的 layout.tsx 全局布局文件(可以理解为页面入口)中引入 BasicLayout:
export default function RootLayout({
children,
}: Readonly<{
children: React.ReactNode;
}>) {
return (
<html lang="zh">
<body>
<AntdRegistry>
<BasicLayout>{children}</BasicLayout>
</AntdRegistry>
</body>
</html>
);
}
可以将 lang=“en” 改为 lang=“zh”,适配国内用户访问。
然后按需精简和修改 BasicLayout 中复制来的布局代码,直到项目可以正常运行并符合预期。
整个过程中,需要注意下面这些事项:
1)BasicLayout 和 layout.tsx 页面开头添加 “use client” 声明,表示客户端渲染
2)移除 useState(useState 只能在 客户端使用,否则会出现水合报错)、将获取 pathname 改为使用 Next.js 的 usePathname 钩子获取
3)移除无用代码,比如 token、siderMenuType
4)定义布局的 children 属性:
interface Props {
children: React.ReactNode;
}
export default function BasicLayout({ children }: Props) {
// ...
}
5)修改菜单渲染函数:
// 菜单渲染
menuItemRender={(item, dom) => (
<Link href={item.path || "/"} target={item.target}
{dom}
</Link>
)}
6)移除 window 对象的使用,解决服务端和客户端水合不一致的问题
全局底部栏
在 src 下新建 components 目录,表示全局公用组件。
创建全局底部栏 GlobalFooter,通常用于展示版权信息,然后在 ProLayout 中展示:
footerRender={() => <GlobalFooter />
需要在 BasicLayout 的样式文件中补充底部内边距,否则有些内容可能会被底部栏遮挡住。示例样式如下:
.ant-pro-layout .ant-pro-layout-content {
padding-bottom: 96px !important;
}
全局顶部导航栏
可以直接利用 Ant Design Procomponents 的 ProLayout 组件实现,不用自己编写。
将 ProLayout 的 layout 属性设置为 top,可开启顶部导航栏:
<ProLayout
layout="top"
/>
ProLayout 将顶部导航栏从左到右分为几个区:标题区、菜单区、操作区、头像区。
1)标题区:用于展示网站图标和标题。该渲染函数有 logo 和 title 参数,可以在 ProLayout 中添加对应的属性,以展示网站图标和标题。
2)菜单区:用于展示导航栏的菜单,供用户切换页面。
3)操作区:可用于配置右侧的操作栏,比如搜索条、小按钮等。移动端可以不展示操作。
4)头像区:用于展示登录用户头像、用户昵称,鼠标悬浮上去还可以展示更多用户有关的操作按钮。
export default function BasicLayout({ children }: Props) {
const pathname = usePathname();
const loginUser = useSelector((state: RootState) => state.loginUser);
return (
<div
id="basicLayout"
style={{
height: "100vh",
overflow: "auto",
}}
>
<ProLayout
title="面面 - 专为面试而生"
layout="top"
logo={
<Image
src={"/assets/logo.png"}
height={32}
width={32}
alt={"面面-只为面试而生"}
/>
}
location={{
pathname,
}}
avatarProps={{
src: loginUser.userAvatar || "/assets/notLoginUser.png",
size: "small",
title: loginUser.userName || "访客",
render: (props, dom) => {
return (
<Dropdown
menu={{
items: [
{
key: "logout",
icon: <LogoutOutlined />,
label: "退出登录",
},
],
}}
>
{dom}
</Dropdown>
);
},
}}
// 操作渲染
actionsRender={(props) => {
if (props.isMobile) return [];
return [
<SearchInput key={"search"} />,
<a
key={"github"}
href={"https://gitee.com/hao-xiugong/mianmian-frontend"}
target={"_blank"}
>
<GithubFilled key="GithubFilled" />,
</a>,
];
}}
headerTitleRender={(logo, title, _) => {
return (
<a>
{logo}
{title}
</a>
);
}}
footerRender={() => {
return <GlobalFooter />;
}}
onMenuHeaderClick={(e) => console.log(e)}
// 定义菜单
menuDataRender={() => {
return [
{
path: "/",
name: "主页",
target: "_blank"
},
{
path: "/banks",
name: "题库",
target: "_blank"
},
];
}}
menuItemRender={(item, dom) => (
<Link href={item.path || "/"} target={item.target}>
{dom}
</Link>
)}
>
{children}
</ProLayout>
</div>
);
}
导航菜单配置
可以通过独立的配置文件更方便地修改导航菜单项,不用每次都修改布局代码。
实现步骤如下:
1)在 /config
目录下编写通用配置文件 menus.tsx
,核心是菜单项数组,可以用 ProLayout 提供的 TypeScript 类型来规范:
import { MenuDataItem } from "@ant-design/pro-layout";
import { CrownOutlined } from "@ant-design/icons";
// 菜单列表
const menus = [
{
path: "/",
name: "主页",
},
{
path: "/banks",
name: "题库",
},
{
path: "/questions",
name: "题目",
},
{
path: "/admin",
name: "管理",
icon: <CrownOutlined />,
children: [
{
path: "/admin/user",
name: "用户管理",
}
],
},
] as MenuDataItem[];
// 导出
export default menus;
2)在全局布局的 ProLayout 中引入菜单数据。
3)同步路由的更新到菜单项高亮。
同步高亮原理:可以使用 usePathname
客户端钩子函数获取到当前页面路径,然后传递给 ProLayout 的 location 属性即可自动匹配到对应 path 的菜单项。
4)扩展能力:在 ProLayout 的菜单渲染函数中可以根据菜单项的属性来自定义菜单项渲染逻辑,比如支持配置超链接是否在新页面打开。target 的值为 _blank
表示在新页面打开。