配置与匹配
路由配置与匹配
路由配置
首先我们需要从 react-router-dom 中导入相应组件:
import React from "react";
import { BrowserRouter as Router, Route, Link } from "react-router-dom";
然后定义Home
,About
这两个组件:
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
函数不过无论路径是否匹配其都会执行。该函数会被传入某个包含match
与history
属性的对象,如果不匹配时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}
/>;