目录
1. 知识地图
2. 前端路由
3. 路由库——React Router
3.1. 库结构
3.2. 示例:基础
3.3. 示例:传参数
3.4. 示例:嵌套路由
4. 路由实践
在现代前端开发中,路由是非常重要的一环
1. 知识地图
2. 前端路由
前端路由起源于 SPA 单页应用架构(现代前端开发中最流行的页面模型):
人话就是
浏览器地址变化=>视觉上的页面切换=>实际上的组件切换
前端路由就是用来完成这个任务的技术
3. 路由库——React Router
3.1. 库结构
3.2. 示例:基础
描述:
效果图:
关键代码:
import React from "react";
import {
BrowserRouter as Router,
Route,
Link
} from "react-router-dom";
import Login from "./pages/Login"
import Home from "./pages/Home"
import Error404 from "./pages/Error404";
export default function BasicExample() {
return (
<Router>
<div>
<ul>
<li>
<Link to="/home">Home</Link>
</li>
<li>
<Link to="/login">Login</Link>
</li>
<li>
<Link to="/error/404">Error404</Link>
</li>
</ul>
<hr/>
<div>
<Route path="/home">
<Home/>
</Route>
<Route path="/login">
<Login/>
</Route>
<Route path="/error/404">
<Error404/>
</Route>
</div>
</div>
</Router>
);
}
3.3. 示例:传参数
描述:
效果图:
关键代码:
App.tsx
import React from "react";
import {
BrowserRouter as Router,
Route,
Link,
} from "react-router-dom";
import Person from "./pages/Person"
export default function ParamsExample() {
return (
<Router>
<div>
<h2>Person</h2>
<ul>
<li>
<Link to="/person/001">Person-001</Link>
</li>
<li>
<Link to="/person/002">Person-002</Link>
</li>
<li>
<Link to="/person/003">Person-003</Link>
</li>
<li>
<Link to="/person/004">Person-004</Link>
</li>
</ul>
<Route path="/person/:empno" component={Person} />
</div>
</Router>
);
}
Person.tsx
import React from "react";
import {useParams} from "react-router-dom"
export default function Person() {
// We can use the `useParams` hook here to access
// the dynamic pieces of the URL.
let { empno } = useParams();
return (
<div>
<h3>Empno: {empno}</h3>
</div>
);
}
3.4. 示例:嵌套路由
描述:
关键代码:
App.tsx
import React from "react";
import {
BrowserRouter as Router,
Switch,
Route,
Link,
useParams,
useRouteMatch
} from "react-router-dom";
import Login from "./pages/Login"
import Error404 from "./pages/Error404";
import Layout from "./layout/Layout";
export default function NestingExample() {
return (
<Router>
<div>
<ul>
<li>
<Link to="/">Index</Link>
</li>
<li>
<Link to="/login">Login</Link>
</li>
<li>
<Link to="/error/404">Error404</Link>
</li>
</ul>
<hr />
<Switch>
<Route path="/login">
<Login />
</Route>
<Route path="/error/404">
<Error404 />
</Route>
<Route path="/">
<Layout />
</Route>
</Switch>
</div>
</Router>
);
}
Layout.tsx
import React from "react";
import {
Route,
Link,
Redirect,
Switch,
} from "react-router-dom";
import Home from "../pages/Home";
import PersonMng from "../pages/PersonMng";
import OrgnMng from "../pages/OrgnMng";
export default function Layout() {
return (
<div>
<Redirect to={`/home`} />
<ul>
<li>
<Link to={`/home`}>首页</Link>
</li>
<li>
<Link to={`/person`}>个人</Link>
</li>
<li>
<Link to={`/orgn`}>单位</Link>
</li>
</ul>
<Switch>
<Route path={`/home`} component={Home}/>
<Route path={`/person`} component={PersonMng}/>
<Route path={`/orgn`} component={OrgnMng}/>
</Switch>
</div>
);
}
4. 路由实践
效果图:
关键代码:
1. 路由表配置示例:
import {MxRoutes} from "dw-mx-static-config-router"
/**
* Static import
*/
import BasicLayout from "@/layouts/BasicLayout";
import Home from "@/pages/Home";
import PersonMng from "@/pages/PersonMng";
import OrgnMng from "@/pages/OrgnMng";
import Login from "@/pages/Login";
import Error404 from "@/pages/Error404";
import NeedUserAuthorized from "@/pages/NeedUserAuthorized";
/**
* Config
*/
const config: {
routes: MxRoutes
} = {
routes: [
{
path: '/login',
component: Login,
exact: true,
}, {
path: '/error/404',
component: Error404,
exact: true,
},{
path: '/',
component: BasicLayout,
wrappers: [NeedUserAuthorized],
routes: [{
path: '/',
redirect: '/home',
exact: true,
},{
path: '/home',
component: Home,
exact: true,
name: "首页"
}, {
path: '/person',
component: PersonMng,
exact: true,
name: "个人"
}, {
path: '/orgn',
component: OrgnMng,
exact: true,
name: "单位"
}],
}
]
};
export default config;
备注:路由表中的 name 字段,是供布局组件使用的,用于菜单展示。
2. 布局组件示例:
import React from "react";
import {mxBrowserHistory, Link} from "dw-mx-static-config-router"
import {Menu, Dropdown, Avatar} from 'antd';
import {HeartTwoTone, UserOutlined, LockOutlined, LogoutOutlined} from 'dw-mx-icons';
import ProLayout from 'dw-mx-layout-antdpro';
import CurrentUser from "@/auth/CurrentUser";
export default function BasicLayout(props) {
const {children, route} = props;
const menu = (
<Menu
className={'app-top-user-menu'}
onClick={() => {
// this.changeUserStyle(false);
}}
>
<Menu.Item key={'person'} onClick={() => {
}}>
<UserOutlined
style={{
fontSize: 10
}}
/>
<span onClick={() => {
}}>个人中心</span>
</Menu.Item>
<Menu.Item key={'password'} onClick={() => {
}}>
<LockOutlined
style={{
fontSize: 10
}}
/>
<span>修改密码</span>
</Menu.Item>
<Menu.Divider/>
<Menu.Item key={'logout'} onClick={() => {
CurrentUser.logOut();
mxBrowserHistory.replace("/login")
}}>
<LogoutOutlined
style={{
fontSize: 10
}}
/>
<span>退出登录</span>
</Menu.Item>
</Menu>
);
return (
<ProLayout route={route}
title={"账户管理中心"}
rightContentRender={() => <div style={{display: 'flex', flexDirection: 'row'}}>
<Dropdown
overlay={menu}
>
<span style={{marginRight: 16}}>
<Avatar icon={<UserOutlined/>} style={{marginRight: 8, color: "#1890ff"}}/>
<span className={'app-top-userInfo'}>
Admin
</span>
</span>
</Dropdown>
</div>}
menuItemRender={(menuItemProps, defaultDom) => {
return <Link to={menuItemProps.path}>{defaultDom}</Link>;
}}>
{children}
</ProLayout>
);
}
3. 路由鉴权示例:
import React from "react";
import {Redirect} from "dw-mx-static-config-router";
import CurrentUser from "@/auth/CurrentUser";
export default function NeedUserAuthorized(props) {
const {children} = props;
if (CurrentUser.isLoggedIn()) {
return children;
} else {
return <Redirect to={"/login"}></Redirect>;
}
}
4. 内部页面示例:
import React from "react";
export default function Home() {
return (
<div>
<h1>这是首页</h1>
</div>
);
}
5. 关键实现:静态路由表渲染组件
import React from "react";
import {Router, Switch, Redirect, Route, RouteComponentProps} from "react-router-dom"
type MxRoutes = MxRoute[];
interface MxRoute {
path: string; // 配置 path
exact?: boolean; // 表示是否严格匹配,即 location 是否和 path 完全对应上
strict?: boolean; // 是否严格匹配结尾'/'
sensitive?: boolean; // 是否大小写敏感匹配
component?: any; // 配置 location 和 path 匹配后用于渲染的 React 组件路径
redirect?: string; // 配置路由跳转
routes?: MxRoute[]; // 配置子路由,通常在需要为多个路径增加 layout 组件时使用
wrappers?: any[]; // 配置路由的高阶组件封装
key?: any;
[key: string]: any; // 用户自定义的字段
}
interface MxBrowserRouterProps {
routes: MxRoutes;
history: any;
}
function render({route, props}: { route: MxRoute, props: RouteComponentProps<any> }) {
const routes = renderRoutes(route.routes || []);
const {component: Component, wrappers} = route;
if (Component) {
const newProps = {
...props, // 路由组件参数
route,
};
// @ts-ignore
let wrappedComponent = <Component {...newProps}>{routes}</Component>;
if (wrappers) {
wrappers.forEach((wrapper)=>{
wrappedComponent = React.createElement(wrapper, newProps, wrappedComponent);
});
}
return wrappedComponent;
} else {
return routes;
}
}
function getRouteElement({route, index}: { route: MxRoute, index: number }) {
const routeProps = {
key: route.key || index,
exact: route.exact,
strict: route.strict,
sensitive: route.sensitive,
path: route.path,
};
if (route.redirect) {
return <Redirect {...routeProps} from={route.path} to={route.redirect}/>;
} else {
return (
<Route
{...routeProps}
render={(props: RouteComponentProps<any>) => {
return render({route, props});
}}
/>
);
}
}
function renderRoutes(routes: MxRoutes) {
return routes ? (
<Switch>
{routes.map((route, index) =>
getRouteElement({
route,
index
}),
)}
</Switch>
) : null;
}
const MxStaticConfigBrowserRouter:React.FunctionComponent<MxBrowserRouterProps> = (props) =>{
const {history, routes} = props;
return <Router history={history}>{renderRoutes(routes)}</Router>;
};
export default MxStaticConfigBrowserRouter;
参考:
React 路由库:React router https://reacttraining.com/react-router/ https://github.com/ReactTraining/react-router/ React router 的底层依赖库: https://github.com/ReactTraining/history UmiJS 对路由的管理: https://umijs.org/zh-CN/docs/routing Antdesign Pro 对路由的管理: https://pro.ant.design/docs/router-and-nav-cn
Vue 路由库:Vue Router https://router.vuejs.org/zh/ iview-admin 对路由的管理: https://lison16.github.io/iview-admin-doc/#/%E8%B7%AF%E7%94%B1%E9%85%8D%E7%BD%AE