配置与匹配

路由配置与匹配

路由配置

首先我们需要从 react-router-dom 中导入相应组件:

import React from "react";
import { BrowserRouter as Router, Route, Link } from "react-router-dom";

然后定义HomeAbout这两个组件:

const Home = () => (
  <div>
    <h2>Home</h2> 
  </div>
);

const About = () => (
  <div>
    <h2>About</h2> 
  </div>
);

Route 组件会在 URL 满足匹配规则时渲染传入的组件,其支持多种匹配模式与多种组件渲染模式,基本使用如下:

import { BrowserRouter as Router, Route } from "react-router-dom";

<Router>
  <div>
      <Route exact path="/" component={Home} />
      <Route path="/news" component={NewsFeed} />
  </div>
</Router>;

React Router 定义了 3 种不同的 Route 渲染语法:

  • <Route component>: 传入某个组件类
  • <Route render>: 传入某个渲染函数
  • <Route children>: 传入子元素
  • 组件类 传入某个当路径匹配时进行渲染的 React 组件,该组件会被传入context.router 中的所有属性:
<Route path="/user/:username" component={User} />;

const User = ({ match }) => {
  return <h1>Hello {match.username}!</h1>;
};
  • 渲染函数

我们也可以传入某个路径匹配时才会调用的回掉函数,React Router 也会自动将 context.router 中的属性以参数传入到该渲染函数中,这个会非常方便于行内渲染与包裹,譬如我们可以编写如下行内渲染类:

<Route path="/home" render={() => <div>Home</div>}/>

同时我们也可以在该回调函数中对组件进行适当的封装,譬如添加渐入渐出的动画效果:

const FadingRoute = ({ component: Component, ...rest }) => (
  <Route {...rest} render={matchProps => (
  <FadeIn>
  <Component {...matchProps}/>
  </FadeIn>
  )}/>
)

<FadingRoute path="/cool" component={Something}/>

如果我们希望在设置 Route 渲染对象的同时传入额外的 Props 属性,那么也可以利用 render 渲染函数:

const App = () => {
  const color = 'red'
  return (
  <Route path='/somewhere' render={(props) => (
  <MyComponent {...props} color={color} />
  )} />
  )
}

注意,<Route component> 的优先级高于<Route render>,因此要避免在<Route>中同时使用这两种方式。

  • 子元素

有时我们希望无论路径是否匹配都能执行某些渲染或者逻辑操作,此时我们可以使用children属性,其工作机制类似render函数不过无论路径是否匹配其都会执行。该函数会被传入某个包含matchhistory属性的对象,如果不匹配时match对象会被设置为null;我们可以通过该对象来了解路由是否匹配,从而动态地调整界面展示。譬如我们需要在路由匹配成功时添加active类名:

<ul>
  <ListItemLink to="/somewhere" />
  <ListItemLink to="/somewhere-else" />
</ul>;

const ListItemLink = ({ to, ...rest }) => (
  <Route
    path={to}
    children={({ match }) => (
      <li className={match ? 'active' : ''}>
        <Link to={to} {...rest/>   {' '}
      </li>
    )}
  />
);

我们也可以添加统一的动画控制,下述代码中的Animate无论路由匹配是否成功都会被执行,因此我们可以用 React 生命周期中的回调函数来控制子元素的淡入淡出动画:

<Route children={({ match, ...rest}) => (
  <Animate>
  {match && <Something {...rest}/>}
  </Animate>
)}/>

注意,<Route children>在三者中的优先级最低。

模糊匹配

动态路由

路由权限控制

const PrivateRoute = ({ component, isAuthenticated, ...rest }: any) => {
  const routeComponent = (props: any) =>
    isAuthenticated ? (
      React.createElement(component, props)
    ) : (
      <Redirect to={{ pathname: "/login" }} />
    );
  return <Route {...rest} render={routeComponent} />;
};

该组件的用法如下:

<PrivateRoute
  path="/private"
  isAuthenticated={this.props.state.session.isAuthenticated}
  component={PrivateContainer}
/>

上述函数式组件的用法缺乏了类型约束,我们可以扩展上述简单的组件用法:

import * as React from "react";
import { Redirect, Route, RouteProps } from "react-router";

export interface ProtectedRouteProps extends RouteProps {
  isAuthenticated: boolean;
  authenticationPath: string;
}

export class ProtectedRoute extends Route<ProtectedRouteProps> {
  public render() {
    let redirectPath: string = "";
    if (!this.props.isAuthenticated) {
      redirectPath = this.props.authenticationPath;
    }

    if (redirectPath) {
      const renderComponent = () => (
        <Redirect to={{ pathname: redirectPath }} />
      );
      return (
        <Route {...this.props} component={renderComponent} render={undefined} />
      );
    } else {
      return <Route {...this.props} />;
    }
  }
}

然后其用法如下:

const defaultProtectedRouteProps: ProtectedRouteProps = {
  isAuthenticated: this.props.state.session.isAuthenticated,
  authenticationPath: "/login"
};

<ProtectedRoute
  {...defaultProtectedRouteProps}
  exact={true}
  path="/"
  component={ProtectedContainer}
/>;
上一页