快来加入我们吧!
"小和山的菜鸟们",为前端开发者提供技术相关资讯以及系列基础文章。为更好的用户体验,请您移至我们官网小和山的菜鸟们 ( https://xhs-rookies.com/ ) 进行学习,及时获取最新文章。
"Code tailor" ,如果您对我们文章感兴趣、或是想提一些建议,微信关注 “小和山的菜鸟们” 公众号,与我们取的联系,您也可以在微信上观看我们的文章。每一个建议或是赞同都是对我们极大的鼓励
前言
这节我们将介绍 React
中 react - router
,路由跳转的配置以及使用
本文会向你介绍以下内容:
- 认识
react-router
react-router
基本使用react-router
高级使用react-router-config
认识 react - router
如果你是第一次接触 router 这个名词,可以先去这里补充一下自己的路由知识再来往下阅读哦
安装 react-router
:
yarn add react-router-dom
npm install react-router-dom
什么是 react-router
?
react-router
是一组以声明方式与您的应用程序组合的导航组件,换句话,react-router
是 React
体系中的路由库,它通过管理 URL,实现组件的切换和状态的变化
react-router 基本使用
React Router
中的组件主要分为三类:
路由器 BrowserRouter
和 HashRouter
BrowserRouter
使用常规URL路径,创建一个像example.com/some/path
这样真实的 URL ,但是他们要求正确的配置服务器HashRouter
将当前位置存储在URL
的哈希部分中,因此URL
看起来类似于http://example.com/#/your/page
,由于哈希从不发送到服务器,因此这意味着不需要特殊的服务器配置
两者之间的主要区别是它们存储 URL
和与 Web
服务器通信的方式
路由匹配器,例如 Switch 和 Route:
当渲染 Switch
组件时,它将搜索它的 Route
子元素,以查找路径与当前 URL
匹配的元素,它将呈现找到的第一个 Route
并忽略所有的其他路由。这意味着您应该将包含更多特定路径的 Route
放在不那么特定的路由之前
Switch
我们来看下面的路由规则:
- 当我们匹配到某一个路径时,我们会发现有一些问题;
- 比如
/about
路径匹配到的同时,/:userid
也被匹配到了,并且最后的一个NoMatch
组件总是被匹配到;
<Route exact path="/" component={Home} />
<Route path="/about" component={About} />
<Route path="/profile" component={Profile} />
<Route path="/:userid" component={User}/>
<Route component={NoMatch}/>
原因是什么呢?默认情况下,react-router
中只要是路径被匹配到的 Route
对应的组件都会被渲染;
但是实际开发中,我们往往希望有一种排他的思想:
- 只要匹配到了第一个,那么后面的就不应该继续匹配了;
- 这个时候我们可以使用
Switch
来将所有的Route
进行包裹即可;
<Switch>
<Route exact path="/" component={Home} />
<Route path="/about" component={About} />
<Route path="/profile" component={Profile} />
<Route path="/:userid" component={User} />
<Route component={NoMatch} />
</Switch>
Route
Route 的渲染方法一共有三类
- <Route component>
- <Route render>
- <Route children>
Route component
仅在位置匹配时根据 route props
渲染
import React from "react";
import ReactDOM from "react-dom";
import { BrowserRouter as Router, Route } from "react-router-dom";
// All route props (match, location and history) are available to User
function User(props) {
return <h1>Hello {props.match.params.username}!</h1>;
}
ReactDOM.render(
<Router>
<Route path="/user/:username" component={User} />
</Router>,
node
);
当你在使用此方法加载时,router
通常会根据给定的 component
去调用 React.createElement
创建一个新的 React
元素。这也就意味着如果你在此是用内联函数渲染的组件,那么它将会在每次渲染时都创建一个新的函数,即卸载组件安装新有组件,而不是更新。所以当你使用内联函数渲染组件时,请选择 render
或 children
方法
Route render
你可以传入一个在位置匹配时调用的函数,而不是使用组件 prop
为您创建一个新的 React
元素
import React from "react";
import ReactDOM from "react-dom";
import { BrowserRouter as Router, Route } from "react-router-dom";
// convenient inline rendering
ReactDOM.render(
<Router>
<Route path="/home" render={() => <div>Home</div>} />
</Router>,
node
);
// wrapping/composing
// You can spread routeProps to make them available to your rendered Component
function FadingRoute({ component: Component, ...rest }) {
return (
<Route
{...rest}
render={routeProps => (
<FadeIn>
<Component {...routeProps} />
</FadeIn>
)}
/>
);
}
ReactDOM.render(
<Router>
<FadingRoute path="/cool" component={Something} />
</Router>,
node
);
需要注意的是:<Route component> 的优先级比 <Route render> 高,所以不要在同一个 Route
中同时调用两种方法
Route children
工作原理同 <Route render> ,只是无论是否匹配都会调用
import React from "react";
import ReactDOM from "react-dom";
import {
BrowserRouter as Router,
Link,
Route
} from "react-router-dom";
function ListItemLink({ to, ...rest }) {
return (
<Route
path={to}
children={({ match }) => (
<li className={match ? "active" : ""}>
<Link to={to} {...rest} />
</li>
)}
/>
);
}
ReactDOM.render(
<Router>
<ul>
<ListItemLink to="/somewhere" />
<ListItemLink to="/somewhere-else" />
</ul>
</Router>,
node
);
需要注意的是:<React children> 优先级比上面两个优先级都高
而这三种渲染方式都会传递相同的 route props
- match :包含 <Route path> 如何匹配
URL
的信息 - location :代表应用程序现在所在的位置
- history : 用于在 JavaScript 中以各种方式管理会话历史记录
路由导航,例如:Link 、NavLink 、Redirect
<Link>
组件将在您的应用程序中创建链接。无论在何处呈现<Link>
,锚点都将呈现在HTML
文档中,而其最终会被渲染成a
元素NavLink
在Link
基础之上增加了一些样式属性,当其prop
与当前位置匹配时,可以给它设置一个activeClassName
(被选中) 的样式。- 任何时候要强制导航,你都可以使用
<Redirect>
,当呈现<Redirect>
时,将根据prop
的to
值进行导航。
NavLink
路径选中时,对应的 a 元素变为红色
activeStyle
:活跃时(匹配时)的样式;activeClassName
:活跃时添加的 class;exact
:是否精准匹配;
<NavLink to="/" activeStyle={{color: "red"}}>home</NavLink>
<NavLink to="/about" activeStyle={{color: "red"}}>about</NavLink>
<NavLink to="/profile" activeStyle={{color: "red"}}>profile</NavLink>
但是,我们会发现在选中 about
或 profile
时,第一个也会变成红色:
- 原因是/路径也匹配到了
/about
或/profile
- 这个时候,我们可以在第一个
NavLink
中添加上exact
属性
<NavLink exact to="/" activeStyle={{ color: 'red' }}>
home
</NavLink>
默认的 activeClassName
:
- 事实上在默认匹配成功时,
NavLink
就会添加上一个动态的active class
; - 所以我们也可以直接编写样式
a.active {
color: red;
}
当然,如果你担心这个 class
在其他地方被使用了,出现样式的层叠,也可以自定义 class
<NavLink exact to="/" activeClassName="link-active">home</NavLink>
<NavLink to="/about" activeClassName="link-active">about</NavLink>
<NavLink to="/profile" activeClassName="link-active">profile</NavLink>
Redirect
Redirect
用于路由的重定向,当这个组件出现时,就会执行跳转到对应的 to
路径中,而 Redirect
最常见的场景即鉴权
- 用户浏览器输入
User
界面url
; - 若用户未登录,需重定向到登录页面。若已登录,则可进入
User
界面
App.js
中提前定义好 Login
页面对应的 Route
:
<Switch>
...其他Route
<Route path="/login" component={Login} />
<Route component={NoMatch} />
</Switch>
在 User.js
中写上对应的逻辑代码:
import React, { PureComponent } from 'react'
import { Redirect } from 'react-router-dom'
export default class User extends PureComponent {
constructor(props) {
super(props)
this.state = {
isLogin: false,
}
}
render() {
return this.state.isLogin ? (
<div>
<h2>微信公众号:小和山的菜鸟们</h2>
<h2>用户名: Coder tailor</h2>
</div>
) : (
<Redirect to="/login" />
)
}
}
react-router 高级使用
嵌套路由
此示例显示嵌套路由的工作原理。路由 /topics
加载 Topics
组件,该组件根据路径 :id
值有条件地呈现任何进一步的 <Route>
。
import React from "react";
import {
BrowserRouter as Router,
Switch,
Route,
Link,
useRouteMatch,
useParams
} from "react-router-dom";
export default function App() {
return (
<Router>
<div>
<ul>
<li>
<Link to="/">Home</Link>
</li>
<li>
<Link to="/about">About</Link>
</li>
<li>
<Link to="/topics">Topics</Link>
</li>
</ul>
<Switch>
<Route path="/about">
<About />
</Route>
<Route path="/topics">
<Topics />
</Route>
<Route path="/">
<Home />
</Route>
</Switch>
</div>
</Router>
);
}
function Home() {
return <h2>Home</h2>;
}
function About() {
return <h2>About</h2>;
}
function Topics() {
let match = useRouteMatch();
return (
<div>
<h2>Topics</h2>
<ul>
<li>
<Link to={`${match.url}/components`}>Components</Link>
</li>
<li>
<Link to={`${match.url}/props-v-state`}>
Props v. State
</Link>
</li>
</ul>
{/* Topics 页面有自己的 <Switch>,其中包含更多基于 /topics URL 路径
的路由。您可以将此处的第二个 <Route> 视为所有主题的“索引”页面,或者
未选择主题时显示的页面 */}
<Switch>
<Route path={`${match.path}/:topicId`}>
<Topic />
</Route>
<Route path={match.path}>
<h3>Please select a topic.</h3>
</Route>
</Switch>
</div>
);
}
function Topic() {
let { topicId } = useParams();
return <h3>Requested topic ID: {topicId}</h3>;
}
动态路由
当我们讨论动态路由时,我们是指在您的应用渲染时发生的路由,而不是在运行中的应用之外配置或约定的。
这意味着几乎所有内容都是 React Router
中的一个组件。下面是对该 API
的回顾,以了解其工作原理:
首先,为您要定位的环境获取一个 Router
组件,并将其呈现在应用程序的顶部。
// react-dom (what we'll use here)
import { BrowserRouter } from "react-router-dom";
ReactDOM.render(
<BrowserRouter>
<App />
</BrowserRouter>,
el
);
const App = () => (
<div>
<nav>
<Link to="/dashboard">Dashboard</Link>
</nav>
<div>
<Route path="/dashboard" component={Dashboard} />
</div>
</div>
);
该 Route
将呈现 <Dashboard {... props} />
,其中 props
是路由器特定的东西,比如 { match, location, history }
。如果用户不在 / dashboard
上,则 Route
将呈现 null
。
react-router-config
路由配置的方式多种多样,让我们看一下来自官网的最佳实践
// 路由配置只是数据
// React 非常擅长将数据映射到组件中,而 <Route> 是一个组件.
//我们的路由配置只是一个带有 path 和 component props 的逻辑"路由"数组
//排序方式与你在 `<Switch>` 中的顺序相同。
//路由配置
const routes = [
{
path: "/sandwiches",
component: Sandwiches
},
{
path: "/tacos",
component: Tacos,
routes: [
{
path: "/tacos/bus",
component: Bus
},
{
path: "/tacos/cart",
component: Cart
}
]
}
];
嵌入 App 中
export default function App() {
ReactDOM.render(
<Router>
<div>
<ul>
<li>
<Link to="/tacos">Tacos</Link>
</li>
<li>
<Link to="/sandwiches">Sandwiches</Link>
</li>
</ul>
<Switch>
{routes.map((route, i) => (
<RouteWithSubRoutes key={i} {...route} />
))}
</Switch>
</div>
</Router>
,document.getElementById('root')
)
}
组件在这儿
function Sandwiches() {
return <h2>Sandwiches</h2>;
}
function Tacos({ routes }) {
return (
<div>
<h2>Tacos</h2>
<ul>
<li>
<Link to="/tacos/bus">Bus</Link>
</li>
<li>
<Link to="/tacos/cart">Cart</Link>
</li>
</ul>
<Switch>
{routes.map((route, i) => (
<RouteWithSubRoutes key={i} {...route} />
))}
</Switch>
</div>
);
}
function Bus() {
return <h3>Bus</h3>;
}
function Cart() {
return <h3>Cart</h3>;
}
//<Route> 的特殊包装器,它知道如何通过将"子"路由
//传递到它呈现的组件的 `routes` 属性来处理它们。
function RouteWithSubRoutes(route) {
return (
<Route
path={route.path}
render={props => (
// pass the sub-routes down to keep nesting
<route.component {...props} routes={route.routes} />
)}
/>
);
}
下节预告
本节我们学习了 React-Router
中高阶组件以及组件补充的内容,至此,React 相关知识我们已学习完毕,下一节我们将为之前的留言板加上登录功能!