前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >【React】:路由(Routing)

【React】:路由(Routing)

作者头像
WEBJ2EE
发布2020-05-09 15:29:19
1.3K0
发布2020-05-09 15:29:19
举报
文章被收录于专栏:WebJ2EE
代码语言:javascript
复制
目录
1. 知识地图
2. 前端路由
3. 路由库——React Router
  3.1. 库结构
  3.2. 示例:基础
  3.3. 示例:传参数
  3.4. 示例:嵌套路由
4. 路由实践   

在现代前端开发中,路由是非常重要的一环

1. 知识地图

2. 前端路由

前端路由起源于 SPA 单页应用架构(现代前端开发中最流行的页面模型):

  • 单页面应用指的是应用实际只有一个主页面,页面间的切换实际是 DOM 结构的动态替换。(优点:无刷新,用户体验好)
  • 对基于 React 的 SPA 应用,所有页面由不同的组件构成,页面的切换其实就是不同组件的切换。
  • 然后,我们把前端页面间(即组件间)的切换与浏览器地址栏中 URL 的变换关联起来(例如:根据浏览器地址栏的变化切换页面),这就是前端路由。

人话就是

浏览器地址变化=>视觉上的页面切换=>实际上的组件切换

前端路由就是用来完成这个任务的技术

3. 路由库——React Router

3.1. 库结构

3.2. 示例:基础

描述:

  • 将应用的路由拆分为:/home、/login、/error/404

效果图:

关键代码:

代码语言:javascript
复制
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. 示例:传参数

描述:

  • 通过 /person/:empno 将 /person/001、/person/002 等 URL 中的 001、002 接收为 empno 参数

效果图:

关键代码:

App.tsx

代码语言:javascript
复制
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

代码语言:javascript
复制
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. 示例:嵌套路由

描述:

  • 一级路由:/、/login、/error/404
    • 注1:/ 路由负责布局,/home、/person、/orgn 是它的子路由
    • 注2:/ 路由必须放在最后,要留意

关键代码:

App.tsx

代码语言:javascript
复制
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

代码语言:javascript
复制
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. 路由实践

  • 采用静态路表由形式描述路由。
  • 静态路由表结构采用的是 react-router-config 的官方建议结构。
  • 支持嵌套路由。
    • 抽离布局组件。
  • 支持路由重定向。
  • 支持路由级别鉴权。
  • Typescript 开发。
  • HTML5 History API 做底层实现。

效果图:

关键代码:

1. 路由表配置示例:

  • 注意/login、/error/404 与 / 路由的顺序,不能反过来。
  • / 路由控制总体布局,/home、/orgn、/person 则是 / 的子路由。
代码语言:javascript
复制
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. 布局组件示例:

代码语言:javascript
复制
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. 路由鉴权示例:

代码语言:javascript
复制
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. 内部页面示例:

代码语言:javascript
复制
import React from "react";

export default function Home() {
    return (
        <div>
            <h1>这是首页</h1>
        </div>
    );
}

5. 关键实现:静态路由表渲染组件

代码语言:javascript
复制
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

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2020-05-04,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 WebJ2EE 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档