UNPKG

rerumaccusamus

Version:

The meta-framework suite designed from scratch for frontend-focused modern web development.

155 lines (115 loc) 5.82 kB
--- 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: ![display](https://lf3-static.bytednsdoc.com/obj/eden-cn/aphqeh7uhohpquloj/modern-js/docs/08/display.png) 访问 `http://localhost:8080/contacts/archives`,只会看到已存档的联系人,页面的标题是 Archives: ![display1](https://lf3-static.bytednsdoc.com/obj/eden-cn/aphqeh7uhohpquloj/modern-js/docs/08/display1.png) 查看页面 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` 查看效果: ![display2](https://lf3-static.bytednsdoc.com/obj/eden-cn/aphqeh7uhohpquloj/modern-js/docs/08/display2.png) 点击导航栏中 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)。