添加客户端路由
上一章节中,我们学习了如何为创建 UI 组件,并添加样式。
这一章节中,我们将会学习如何添加客户端路由。
之前我们已经为联系人列表增加了 Archive 按钮,接下来我们添加一个客户端路由 /archives
,访问这个路由时,只显示已存档的联系人,而原有的 /
继续显示所有联系人。
新建 src/routes/archives/page.tsx
文件:
mkdir -p src/routes/archives
touch src/routes/archives/page.tsx
添加如下代码:
src/archives/page.tsx
import { List } from 'antd';
import { Helmet } from '@modern-js/runtime/head';
import Item from '../../components/Item';
const getAvatar = (users: Array<{ name: string; email: string }>) =>
users.map(user => ({
...user,
avatar: `https://api.dicebear.com/7.x/pixel-art/svg?seed=${user.name}`,
}));
const getMockArchivedData = () =>
getAvatar([
{ name: 'Thomas', email: 'w.kccip@bllmfbgv.dm' },
{ name: 'Chow', email: 'f.lfqljnlk@ywoefljhc.af' },
]);
function Index() {
return (
<div className="container lg mx-auto">
<Helmet>
<title>Archives</title>
</Helmet>
<List
dataSource={getMockArchivedData()}
renderItem={info => <Item key={info.name} info={info} />}
/>
</div>
);
}
export default Index;
这里使用了 React Helmet 的 Helmet
组件,在 src/routes/page.tsx
中也添加 Helmet 组件:
import { Helmet } from '@modern-js/runtime/head';
function Index() {
return (
<div className="container lg mx-auto">
<Helmet>
<title>All</title>
</Helmet>
...
</div>
);
}
INFO
Modern.js 默认集成了 react-helmet,也可以结合 SSR 使用,满足 SEO 需求。
因为现在有多个页面,都需要用到前面的 Utility Class,因此我们需要把样式文件移动到 src/routes/layout.tsx
:
import 'tailwindcss/base.css';
import 'tailwindcss/components.css';
import 'tailwindcss/utilities.css';
import '../styles/utils.css';
执行 pnpm run dev
,访问 http://localhost:8080
,可以看到完整的联系人,页面的标题是 All:
访问 http://localhost:8080/archives
,只会看到已存档的联系人,页面的标题是 Archives:
查看页面 HTML 源码,可以看到两个页面的内容是一样,是在客户端针对不同 URL 渲染不同内容。
接下来我们增加一个简单的导航栏,让用户能在两个列表之间切换。
打开 src/routes/layout.tsx
,在顶部导入 Radio 组件:
import { Radio } from 'antd';
然后将 UI 最顶部进行修改,增加一组单选框
1export default function Layout() {
2 return (
3 <div>
4 <div className="h-16 p-2 flex items-center justify-center">
5 <Radio.Group onChange={handleSetList} value={currentList}>
6 <Radio value="/">All</Radio>
7 <Radio value="/archives">Archives</Radio>
8 </Radio.Group>
9 </div>
10 <Outlet />
11 </div>
12 );
13}
然后我们来实现 currentList
和 handleSetList
。
引入三个 React Hook:useState
和 useNavigate
和 useParams
,以及 Ant Design 的事件类型定义:
import { useState } from 'react';
import { Radio, RadioChangeEvent } from 'antd';
import { Outlet, useLocation, useNavigate } from '@modern-js/runtime/router';
最后在 Layout 组件里增加局部状态和相关逻辑:
1export default function Layout() {
2 const navigate = useNavigate();
3 const location = useLocation();
4 const [currentList, setList] = useState(location.pathname || '/');
5 const handleSetList = (e: RadioChangeEvent) => {
6 const { value } = e.target;
7 setList(value);
8 navigate(value);
9 };
10 return (
11 ...
12}
到这里就已经完成了页面导航栏实现,执行 pnpm run dev
查看效果:
点击导航栏中 Archives,可以看到单选框的选中状态和 URL 都会变化,页面没有刷新,只发生了 CSR。
通过 URL 访问两个页面,可以看到 HTML 内容是不同的,这是因为在 SSR 阶段页面就执行了客户端路由的逻辑,HTML 里已经包含了最终的渲染结果。