王志远,微医前端技术部
前言
next采用约定式路由,pages是路由根目录,其下.js文件都会被转为路由文件,文件夹则会被转为多级路由;如
├── pages
│ ├── index.tsx
│ ├── profile.tsx
│ └── user
│ └── index.tsx
│ └── list.tsx
│ └── detail.tsx
会生成如下可访问路由
- /index
- /user/index
- /user/user-detail
除了这种基础的使用,本文关注路由间跳转、路由参数、全局包裹等高阶使用。
目标
- 页面通用组件,如底部信息、头部导航栏,侧边栏等,实现路由跳转
- 二级路由:用户列表页,用户详情页
- 路由间传参:跳转指定用户详情
开始实现
基础路由跳转
实现思路
- 页面通用组件:我们实现个点击跳转路由时只修改内容区域的应用,这意味着头部和尾部都是页面通用组件;在 next 中这需要实现在
_app.tsx中。 - 路由跳转:这需要
Link组件,可以通过next/link导入;
具体实现
新建好三个对应的路由页,内容采用简单的文字展示即可
profile.tsx
import router from 'next/router';
import { Button, Layout } from 'antd';
const { Content } = Layout;
export default function () {
return (
<Content style={{ margin: '20px auto' }}>
<Button>Profile</Button>
</Content>
)
}
user.tsx
import router from 'next/router';
import { Button, Layout } from 'antd';
const { Content } = Layout;
export default function () {
return (
<Content style={{ margin: '20px auto' }}>
<Button>user.tsx</Button>
</Content>
)
}
这需要实现在_app.tsx中
import App, { Container } from 'next/app';
import Link from 'next/link';
import { Layout, Menu, Icon } from 'antd';
import 'antd/dist/antd.css';
import { withRouter } from 'next/router';
const { Header, Footer } = Layout;
class LayoutApp extends App<any> {
render() {
let { Component } = this.props as any;
let pathname = this.props.router.pathname;
pathname = '/' + (pathname.split('/')[1]);
return (
<Container>
<style jsx>
{`a{display:inline-block!important;}`}
</style>
<Layout>
<Header className="header">
<Menu theme="dark" mode="horizontal" style={{ lineHeight: '64px', display: 'inline-block' }}
selectedKeys={[pathname]}
defaultSelectedKeys={[pathname]}>
<Menu.Item key="/">
<Icon type="home" /><Link href="/"><a>首页</a></Link>
</Menu.Item>
<Menu.Item key="/user">
<Icon type="/user" /> <Link href="/user" ><a>用户管理</a></Link>
</Menu.Item>
<Menu.Item key="/profile">
<Icon type="profile" /><Link href="/profile"><a>个人中心</a></Link>
</Menu.Item>
<Menu.Item key="/login">
<Icon type="login" /><Link href="/login"><a>登录</a></Link>
</Menu.Item>
</Menu>
</Header>
<Component />
<Footer style={{ textAlign: 'center' }} >@copyright wzyan</Footer>
</Layout>
</Container>
)
}
}
export default withRouter(LayoutApp);
查看效果
路由间传参:我们需要实现高亮当前路由对应菜单栏项,这就需要路由参数了,我们需要withRouter函数包裹,可以通过next/router导入
二级路由
最常见的就是用户详情页了,根据最上面的目录树,我们知道存在user/detail文件,所以我们只需要实现用户 id 的传递即可;用户列表页传递id,用户详情页获取id。
注意:detail和list是二级路由,需要在二级路由中引用父组件,并包裹子组件,这样当路由匹配上时,user/index.tsx组件会存在于 props 的属性children上,可用于渲染子路由。(类似于 vue 的slot)
实现思路
- 用户首页存在侧边栏跳转【用户列表】+【添加用户】入口
- 用户列表每一项点击后跳转详情页,传递id
具体实现
【用户首页存在侧边栏跳转【用户列表】+【添加用户】入口】,我们就得修改user/index.tsx文件,引入 antd 的Menu组件,这里实现如下内容;并把子路由视图props.children放在 content 区域
User/index.tsx
import { withRouter } from 'next/router';
import Link from 'next/link';
import { Layout, Menu, Icon } from 'antd';
const { Sider, Content } = Layout;
function UserLayout(props) {
return (
<>
<style jsx>
{`a{display:inline-block!important;}`}
</style>
<Layout>
<Sider>
<Menu
theme="dark"
mode="inline"
defaultSelectedKeys={[props.router.pathname]}
>
<Menu.Item key="/user/list">
<Icon type="user" /><Link href="/user/list"><a>用户列表</a></Link>
</Menu.Item>
<Menu.Item key="/user/add">
<Icon type="plus" /><Link href="/user/add"><a>添加用户</a></Link>
</Menu.Item>
</Menu>
</Sider>
<Content style={{ padding: 8 }}>
{props.children}
</Content>
</Layout>
</>
)
}
export default withRouter(UserLayout);
user/detail
import React, { useState } from "react";
import UserLayout from ".";
function UserDetail(props) {
return (
<UserLayout>
我是详情
</UserLayout>
);
}
export default withRouter(UserDetail);
查看效果
路由间传参
- 实现用户列表页的内容,Mock两条数据
- 实现带参路由跳转
- Link组件直接跳转(本次案例用这个)
- 函数式跳转
- 接收参数:
props.router.query.id
具体实现如下
user/list.tsx
import UserLayout from "./index";
import { List } from "antd";
import Link from "next/link";
import axios from "../../utils/axios";
function UseList(props) {
let list = [
{ _id: 1, username: "zhangsan", password: "1" },
{ _id: 2, username: "lisi", password: "2" },
];
return (
<UserLayout>
<List
header={<div>用户列表</div>}
footer={<div>共计多少{list?.length}个用户</div>}
bordered
dataSource={list}
renderItem={(item: any) => (
<List.Item key={item._id}>
<Link
as={`/user/detail/${item._id}`}
href={{ pathname: `/user/detail`, query: { id: item._id } }}
>
<a>{item.username}</a>
</Link>
</List.Item>
)}
/>
</UserLayout>
);
}
export default UseList;
user/detail
import React, { useState } from "react";
import UserLayout from ".";
function UserDetail(props) {
return (
<UserLayout>
<p>ID: {props.router.query.id}</p>
</UserLayout>
);
}
export default withRouter(UserDetail);