只需cv,实战中学习next | 集成路由

883 阅读3分钟

王志远,微医前端技术部

前言

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。

注意:detaillist是二级路由,需要在二级路由中引用父组件,并包裹子组件,这样当路由匹配上时,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);