rerumaccusamus
Version:
The meta-framework suite designed from scratch for frontend-focused modern web development.
155 lines (115 loc) • 5.82 kB
Markdown
---
title: 使用自控式路由
---
上一章节中,我们学习了如何创建应用入口。
这一章节中,我们将会学习如何为入口增加【 客户端路由 】。
我们分别用两种不同的方式,为 `contacts` 和 `landing-page` 增加客户端路由逻辑。
`contacts` 和 `landing-page` 这两个入口,都是通过 CLI 自动创建出来的,在创建过程中我们没有修改入口的默认配置,因此每个入口的客户端路由都是默认开启的。
```js title="package.json"
"modernConfig": {
"runtime": {
"router": true,
"state": true
}
}
```
之前我们已经为联系人列表增加了 Archive 按钮,接下来我们添加一个客户端路由 `/archives`,访问这个路由时,只显示已存档的联系人,而原有的 `/` 继续显示所有联系人。
打开 `src/contacts/App.tsx`,在原有的 `mockData` 下方新增 `mockArchivedData`:
```js
const mockData = getAvatar([
{ name: 'Thomas', email: 'w.kccip@bllmfbgv.dm' },
{ name: 'Chow', email: 'f.lfqljnlk@ywoefljhc.af' },
{ name: 'Bradley', email: 'd.wfovsqyo@gpkcjwjgb.fr' },
{ name: 'Davis', email: '"t.kqkoj@utlkwnpwk.nu' },
]);
const mockArchivedData = getAvatar([
{ name: 'Thomas', email: 'w.kccip@bllmfbgv.dm' },
{ name: 'Chow', email: 'f.lfqljnlk@ywoefljhc.af' },
]);
```
在文件顶部引入 [React Router](https://reactrouter.com/) 的 `Route`、`Switch` 和 [React Helmet](https://github.com/nfl/react-helmet) 的 `Helmet` 组件:
```js
import { Route, Switch } from '@modern-js/runtime/router';
import { Helmet } from '@modern-js/runtime/head';
```
在 `App` 组件中使用 `Route` 写两个路由,分别用不同的 mock 数据渲染列表:
```js
function App() {
return (
<div className="container lg mx-auto">
<Switch>
<Route path="/" exact={true}>
<Helmet>
<title>All</title>
</Helmet>
<List
dataSource={mockData}
renderItem={info => <Item key={info.name} info={info} />}
/>
</Route>
<Route path="/archives" exact={true}>
<Helmet>
<title>Archives</title>
</Helmet>
<List
dataSource={mockArchivedData}
renderItem={info => <Item key={info.name} info={info} />}
/>
</Route>
</Switch>
</div>
);
}
```
:::note 注
Modern.js 默认集成了 react-helmet,无需安装依赖,可以直接使用,也可以结合 SSR 使用,满足 SEO 需求。
Modern.js 也默认集成了 react-router,无需安装依赖和自己配置 `BrowserRouter` 等样板代码,可以直接用 Route、Switch 等组件实现路由逻辑。
:::
React Router v4+ 有两种用法,一种是 `component-based` 的,一种是基于全局配置的。这两种都由开发者自己用代码来控制客户端路由逻辑,所以我们把这种模式称作【**自控式路由**】。
执行 `pnpm run dev`,访问 `http://localhost:8080/contacts`,可以看到完整的联系人,页面的标题是 All:

访问 `http://localhost:8080/contacts/archives`,只会看到已存档的联系人,页面的标题是 Archives:

查看页面 HTML 源码,可以看到两个页面的内容是一样,是在客户端针对不同 URL 渲染不同内容。
**接下来我们增加一个简单的导航栏,让用户能在两个列表之间切换**。
打开 src/contacts/App.tsx,在顶部导入 Radio 组件:
```tsx
import { List, Radio } from 'antd';
```
然后将 UI 最顶部进行修改,增加一组单选框
```tsx {3-8}
return (
<div className="container lg mx-auto">
<div className="h-16 p-2 flex items-center justify-center">
<Radio.Group onChange={handleSetList} value={currentList}>
<Radio value="/">All</Radio>
<Radio value="/archives">Archives</Radio>
</Radio.Group>
</div>
<Switch>
```
然后我们来实现 `currentList` 和 `handleSetList`。
引入两个 React Hook:`useState` 和 `useHistory`,以及 Ant Design 的事件类型定义:
```js
import { useState } from 'react';
import { List, Radio, RadioChangeEvent } from 'antd';
import { Route, Switch, useHistory } from '@modern-js/runtime/router';
```
最后在 App 组件里增加局部状态和相关逻辑:
```js {2-8}
function App() {
const history = useHistory();
const [currentList, setList] = useState(history.location.pathname || '/');
const handleSetList = (e: RadioChangeEvent) => {
const { value } = e.target;
setList(value);
history.push(value);
};
```
到这里就已经完成了页面导航栏实现,执行 `pnpm run dev` 查看效果:

点击导航栏中 Archives,可以看到单选框的选中状态和 URL 都会变化,页面没有刷新,只发生了 CSR。
如果我们将 contacts 入口的 SSR 选项开启后([配置教程](/docs/apis/config/server/ssr)),重新访问两个页面,可以看到 HTML 内容是不同的,这是因为在 SSR 阶段页面就执行了客户端路由的逻辑,HTML 里已经包含了最终的渲染结果。
访问 `http://localhost:8080/contacts/archives`,点击顶部单选框,可以看到在有 SSR 的情况下,CSR 不受影响,跟开启 SSR 之前的效果一致,实现了 UX 的最大化(首屏 SSR,后续交互 CSR)。
---
> 本小节的代码可以在[这里查看](https://github.com/modern-js-dev/modern-js-examples/tree/main/tutorials/c08/hello-modern)。