1、安装
ReactRouter 是一个基于 React 的路由库,它被拆分为三个独立的包,分别是:
- react-router-dom:提供 Web 应用所需的特定组件
- react-router-native:提供移动应用所需的特定组件
- react-router-core:提供核心的路由组件
我们可以使用 Create React App 工具快速搭建一个脚手架作为练习的环境
> npm install -g create-react-app > create-react-app my-project > cd my-project
然后使用 NPM 在项目中添加 React Router(这里只需安装 react-router-dom 即可)
> npm install --save react-router-dom
这样我们就可以在 src/App.js
文件中写下我们的练习代码啦(直接用练习代码替换掉原文件内容就好)
2、入门
组件化的思维无处不在,路由的使用也是一样的,我们可以通过组件定义路由
在 React Router 中有三种基础组件,分别是 路由器组件、路由匹配组件 和 导航组件
我们先来看一个简单的例子
import React from "react"; import { BrowserRouter, Route, Link } from "react-router-dom"; function Index() { return <h3>Home</h3> } function About() { return <h3>About</h3> } function Users() { return <h3>Users</h3> } function App() { return ( <BrowserRouter> {/* 路由器组件 BrowserRouter */} {/* 注意,路由器组件只能有一个子元素,所以我们一般会使用 <div> 将元素包裹起来 */} <div> {/* 导航组件 Link,用于在前端页面展示导航样式,类似 <a> 标签 */} {/* to 属性指定 URL 路径 */} <Link to="/">Home</Link><br /> <Link to="/about/">About</Link><br /> <Link to="/users/">Users</Link><br /> {/* 路由匹配组件 Route,用于定义路由匹配规则,并在路径匹配成功后渲染组件 */} {/* path 属性指定匹配的路径,component 属性指定使用的组件 */} <Route path="/" exact component={Index} /> <Route path="/about/" component={About} /> <Route path="/users/" component={Users} /> </div> </BrowserRouter> ) } export default App;
3、路由器组件
对于 Web 应用而言,react-router-dom 提供两种类型的路由器组件, 和
一般而言 适用于 管理动态请求 的网站,
适用于 呈现静态文件 的网站
每个 router 都会创建一个 history 对象用于跟踪当前 location,当 location 发生变化时就会重新渲染网页
路由器组件是 React Router 的核心组件,其它任何组件都必须包裹在路由器组件内使用
4、路由匹配组件
路由匹配组件用于定义路由匹配规则,react-router-dom 同样提供两种组件,<Route>
和 <Switch>
(1)<Route>
我们先来回顾一下上面的例子:
<Route path="/" exact component={Index} /> <Route path="/about/" component={About} /> <Route path="/users/" component={Users} />
<Route>
组件有两个常用的属性,分别是 path
和 component
,它们的语义也很简单
就是当路径名与 path 属性定义的路径匹配时,渲染 component 属性定义的组件;若不匹配则渲染 null
什么才是 匹配的路径 呢?这里举一个例子,假设 path 定义的路径为 /about/
,那么
- 当路径名为
/ab
时,path 不匹配 - 当路径名为
/about
时,path 匹配 - 当路径名为
/about/un
,path 匹配
很简单,对吧!只要开头一样,path 就能匹配,但是这样会产生一个 多匹配的问题
什么意思呢?我们再来举一个例子,假设我们现在访问路径 /about
,很显然我是希望程序只显示 About
组件的
但由于路径 /
和 /about/
都能匹配,所以程序最终会渲染出 Index 和 About 组件,这显然不符合我们的意图
这时候,exact 属性 就可以派上用场啦,它用来指明匹配的路径必须完全一致才算匹配成功
也就是说,为第一个路由匹配组件添加 exact 属性后,当我们访问路径 /about
时,程序就只会显示 About 组件
(2)<Switch>
如果我们希望一次渲染一个组件,我们可以给 <Router>
包裹一个 <Switch>
组件
这样,它将按照路由定义的顺序进行匹配,并且当成功匹配到一个路由后停止匹配
<Switch> <Route path="/" exact component={Index} /> <Route path="/about/" component={About} /> <Route path="/users/" component={Users} /> </Switch>
5、导航组件
导航组件用于在页面展示导航样式,react-router-dom 提供两种常用组件,<Link>
和 <NavLink>
(1)<Link>
我们还是回顾一下上面的例子:
<Link to="/">Home</Link><br /> <Link to="/about/">About</Link><br /> <Link to="/users/">Users</Link><br />
<Link>
组件很容易理解,它的作用和显示的样式与 <a>
标签十分相似
<Link>
组件有一个常用的属性 to
,用于定义要跳转到的路径
(2)<NavLink>
<NavLink>
是 <Link>
的一种特殊类型,除了有一个常用的 to
属性之外
它还有一个 activeClassName
属性,用于定义当路径匹配后使用的样式
6、URL 参数
我们可以使用 :参数名称
为路径定义参数,例如 <Route path="/users/:username" component={Users} />
那么,当路径名为 /user/Alice
时,path 可以匹配;当路径名为 /user/Bob
时,path 也可以匹配
并且,在路由匹配成功后可以给 Users
组件传入参数 match 对象 以获取匹配的参数
import React from "react"; import { BrowserRouter, Route, Link } from "react-router-dom"; function Users({ match }) { console.log(match) return <h3>Hello { match.params.username }</h3> } function App() { return ( <BrowserRouter> <div> <h3>User</h3> <Link to="/users/Alice">To Alice</Link><br /> <Link to="/users/Bob">To Bob</Link><br /> <Route path="/users/:username" component={Users} /> </div> </BrowserRouter> ) } export default App;
当路径为 /users/Alice
时,我们来看看 match 对象 包含了什么内容
{ isExact: true, // 是否为 exact 模式 params: { username: "Alice" }, // URL 参数 path: "/users/:username", // 定义的路径 url: "/users/Alice", // 真实的路径 __proto__: Object }
7、查询参数
我们可以使用 ?查询字符串
定义查询参数,例如
<Link to="/users?name=Alice">To Alice</Link>
我们也可以为 <Link>
组件的 to
属性传入一个对象,在其中指定查询字符串,例如
<Link to={{ pathname: "/user", search: "?name=Bob" }}>To Bob</Link>
然后,在路由成功匹配后可以给组件传入 location 对象 以获取匹配的参数
import React from "react"; import { BrowserRouter, Route, Link } from "react-router-dom"; function Users({ location }) { console.log(location) return <h3>Hello { location.search.split('?')[1].split('=')[1] }</h3> } function App() { return ( <BrowserRouter> <div> <h3>User</h3> <Link to="/users?name=Alice">To Alice</Link><br /> <Link to={{ pathname: "/users", search: "?name=Bob" }}>To Bob</Link><br /> <Route path="/users" component={Users} /> </div> </BrowserRouter> ) } export default App;
当路径为 /users?name=Alice
时,我们来看看 location 对象 包含了什么内容
{ key: "3qrwxq", pathname: "/users", // 匹配的路径名称 search: "?name=Alice", // 查询参数(?后面的内容) hash: "", // 锚点参数(#后面的内容) __proto__: Object }
8、重定向
import React from "react"; import { BrowserRouter, Route, Switch, Link, Redirect } from "react-router-dom"; function Matched() { return <h3>Matched!</h3> } function App() { return ( <BrowserRouter> <div> <Link to="/old-match">Old Match</Link> (to be redirected)<br/> <Link to="/new-match">New Match</Link><br/> <Switch> {/* 当路径匹配到 `/old-match` 时,重定向到 `/new-match` */} <Redirect from="/old-match" to="/new-match" /> <Route path="/new-match" component={ Matched } /> </Switch> </div> </BrowserRouter> ) } export default App;
9、防止转换
import React from "react"; import { BrowserRouter, Route, Switch, Link, Prompt } from "react-router-dom"; function Home() { return ( <div> <h3>Home Page Here!</h3> <Link to="/about">To About Page</Link> </div> ) } function About() { return ( <div> <h3>About Page Here!</h3> <Link to="/">Return Home Page</Link> {/* 不会实际渲染出来,但是在路径发生改变时会弹出提示信息 */} <Prompt message={ `Are you sure you want to go to Home Page?` } /> </div> ) } function App() { return ( <BrowserRouter> <div> <Switch> <Route path="/" exact component={ Home } /> <Route path="/about" component={ About } /> </Switch> </div> </BrowserRouter> ) } export default App;
10、路由配置
import React from "react"; import { BrowserRouter, Route, Link } from "react-router-dom"; // 1、定义组件 function Home() { return <h2>Home Page</h2> } function About({ routes }) { // 包含嵌套路由 return ( <div> <h2>About Page</h2> <Link to="/about/team">To Team</Link><br /> <Link to="/about/product">To Product</Link><br /> { routes.map((route, index) => ( <RouteWithSubRoutes key={index} {...route} /> )) } </div> ) } function Team() { return <h3>This is our team</h3> } function Product() { return <h3>This is our product</h3> } // 2、定义路由配置 const routes = [ { path: "/home", component: Home }, { path: "/about", component: About, routes: [ { path: "/about/team", component: Team }, { path: "/about/product", component: Product } ] } ] // 3、处理嵌套路由 function RouteWithSubRoutes(route) { return <Route path={route.path} render={ (props) => ( <route.component {...props} routes={route.routes} /> )} /> } // 4、传入路由配置 function App() { return ( <BrowserRouter> <div> <Link to="/home">Home</Link><br /> <Link to="/about">About</Link><br /> { routes.map((route, index) => ( <RouteWithSubRoutes key={index} {...route} /> )) } </div> </BrowserRouter> ) } export default App;
【 阅读更多关于 React Router 的内容,请参考 官方文档】