React进阶篇(九)React Router

2020-05-06  本文已影响0人  娜姐聊前端

单页面应用(SPA)可以让Web应用看起来像多页面应用,URL变化时,不会向服务端发起请求,而是利用自身监听路由变化而更新UI。
通过使用React Router可以让Web应用根据不同URL渲染不同组件。

下面所以内容基于React Router 4。

1. 路由器

React Router通过 RouterRoute两个组件完成路由功能。

路由方式有两种(都是Router的子组件)

http://example.com/some/path

使用BrowserRouter,需要对服务器进行配置,让服务器能处理所有可能正确的URL(需要服务器返回SPA应用中唯一的HTML页面)

http://example.com/#/some/path

使用HashRouter,无需服务器配置

2. 路由配置

match {
    params, // 参数,如<Route path='/foo/:id'>,那么,当URL为'http://example.com/foo/1'时,params = {id: 1}
    isExact, // boolean值。是否完全匹配
    path, // Route的path属性
    url // URL的匹配部分
}

3. Route渲染组件

<Route path='/foo' component={Foo}>
<Route path='/foo' render={props=><Foo {...props} data={extraData}/>}>
<Route path='/foo' childern={props=>
    <div className={props.match ? 'active': ''}>
        <Foo />
    </div>}>

4. switch和exact (React Router 4)

当URL和多个Route匹配时,如果只想让第一个匹配的Route渲染,那么可以将这个Route放在Switch组件中。

如果想让Route和URL完全匹配时,才渲染Route,那么使用exact属性。

<Router>
    <Switch>
        <Route exact path='/' component={Home} />
        <Route path='/posts' component={Post} /> 
        <Route path='/:user' component={User}/ >
    </Switch>    
</Router>


5. 嵌套路由

在Route渲染的组件内部定义新的Route。比如,改造上面的Post路由组件:

const Post = (match=>{
    return (
        <div>
            <Route path={`${match.url}/:id`} component={PostDetail} />
            <Route exact path={match.url} component={PostList} >
        </div>
    )
})

6. 链接

<Link to={{
    pathname: '/post',
    search: '?sort=name',
    hash: '#hash',
    state: {name: 'React'} // 传递数据
}}>
this.props.history.push('/post');
this.props.history.replace('/post');

Demo

基于Antd和React Route 4。

现在有两个页面:

用户先通过登录页面登录,然后自动跳转到主页。

1. 定义路由配置routes.js
import React from 'react';

import BasicLayout from '@/layouts/BasicLayout';
import UserLayout from '@/layouts/UserLayout';

const Home = React.lazy(() => import('@/pages/Home));
const Child = React.lazy(() => import('@/pages/Child'));
const NotFound = React.lazy(() => import('@/pages/NotFound'));

const routerConfig = [
    {
        path: '/user',
        component: UserLayout,
        children: [
            {
                path: '/login',
                component: UserLogin
            },
            {
                path: '/',
                redirect: '/user/login', // 如果路径为 /user,会重定向到/user/login
            },
            {
                component: NotFound,
            },
        ]
    },
    {
        path: '/',
        component: BasicLayout,
        children: [
            {
                path: '/home',
                component: Home
            },
            {
                path: '/page/:id',
                component: Child
            },
            {
                path: '/',
                redirect: '/home',
            },
        ]
    }
];

export default routerConfig;
2. 定义BasicLayout上使用的菜单menu.js
export const asideMenuConfig = [
    {
        name: 'Home',
        path: '/home',
        icon: 'home',
        key: 1
    },
    {
        name: 'Child',
        path: '/page',
        icon: 'book',
        key: 2
    }
]
3. 定义导航BasicLayout.js
import React, { useState } from 'react';
import { Layout, Menu, Icon } from 'antd';
import { Link } from 'react-router-dom';

import logo from '@/components/images/logo.png';
import { asideMenuConfig } from '@/config/menu';

const { Content, Sider } = Layout;

export default (props) => {

    const [collapsed, setCollapsed] = useState(0);

    function getActiveItemKey() {
        let hash = window.location.hash;
        const path = hash.substring(1);
        const route = asideMenuConfig.filter(item => path === item.path);
        if (route.length > 0) {
            return [route[0].key + ''];
        }
        return ['1'];
    }

    return (
        <Layout style={{ minHeight: '100vh' }}>
            <Sider
                collapsible
                collapsed={collapsed}
                onCollapse={() => setCollapsed(!collapsed)}
            >
                {collapsed && <div style={{ textAlign: 'center', padding: '20px 0 20px' }}>
                    <img src={logo} alt='积木盒子' width={65} />
                </div>}
                {!collapsed && <div className="logo"><img src={logo} alt='积木盒子' /></div>}
                <Menu theme="dark" defaultSelectedKeys={getActiveItemKey()} mode="inline">
                    {asideMenuConfig.map(item =>
                        <Menu.Item key={item.key}>
                            <Link to={item.path}><span><Icon type={item.icon} /><span>{item.name}</span></span></Link>
                        </Menu.Item>
                    )}
                </Menu>
            </Sider>
            <Layout>
                {/*<Header>it is nothting</Header>*/}
                <Content style={{ position: 'relative' }}>
                    {props.children}
                </Content>
            </Layout>
        </Layout>
    )
}
4. 定义路由MyRouter.js
/**
 * Created by yelan on 2018/10/1.
 */

import React, { Suspense } from 'react';
import path from 'path';
import { HashRouter as Router, Route, Switch, Redirect } from 'react-router-dom';
import PageLoading from '@/components/PageLoading';
import routes from './config/routes';

const RouteItem = (props) => {
    const { redirect, path: routePath, component, key } = props;
    if (redirect) {
        return (
            <Redirect
                exact
                key={key}
                from={routePath}
                to={redirect}
            />
        );
    }
    return (
        <Route
            key={key}
            component={component}
            path={routePath}
        />
    );
};

export default () => {

    return (
        <Router>
            <Switch>
                {
                    routes.map((route, id) => {
                        const { component: RouteComponent, children, ...others } = route;

                        return (
                            <Route
                                key={id}
                                {...others}
                                component={props => {
                                    return (
                                        children ? (
                                            <RouteComponent key={id} {...props}>
                                                <Suspense fallback={<PageLoading />}>
                                                        <Switch>
                                                            {children.map((routeChild, idx) => {
                                                                const { redirect, path: childPath, component } = routeChild;
                                                                return RouteItem({
                                                                    key: `${id}-${idx}`,
                                                                    redirect,
                                                                    path: childPath && path.join(route.path, childPath),
                                                                    component
                                                                })
                                                            })}
                                                        </Switch>
                                                 </Suspense>
                                            </RouteComponent>
                                        ) : (
                                                <Suspense fallback={<PageLoading />}>
                                                        {
                                                            RouteItem({
                                                                key: id,
                                                                ...route
                                                            })
                                                        }
                                               </Suspense>
                                            )
                                    )
                                }}
                            ></Route>
                        )
                    })
                }
            </Switch>
        </Router>
    )
}
上一篇下一篇

猜你喜欢

热点阅读