1. 安装及入门使用
npm install react-router-dom
1.1 HelloWorld
import React from "react";
import {
BrowserRouter as Router,
Switch,
Route,
Link
} from "react-router-dom";
export default function App() {
return (
<Router>
<div>
<nav>
<ul>
<li>
<Link to="/">Home</Link>
</li>
<li>
<Link to="/about">About</Link>
</li>
<li>
<Link to="/users">Users</Link>
</li>
</ul>
</nav>
{/* A <Switch> looks through its children <Route>s and
renders the first one that matches the current URL. */}
<Switch>
<Route path="/about">
<About />
</Route>
<Route path="/users">
<Users />
</Route>
<Route path="/">
<Home />
</Route>
</Switch>
</div>
</Router>
);
}
function Home() {
return <h2>Home</h2>;
}
function About() {
return <h2>About</h2>;
}
function Users() {
return <h2>Users</h2>;
}
1.2 嵌套路由
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>
{/* The Topics page has its own <Switch> with more routes
that build on the /topics URL path. You can think of the
2nd <Route> here as an "index" page for all topics, or
the page that is shown when no topic is selected */}
<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>;
}
1.3 主要组件
1.3.1 路由组件: BrowserRouter和HashRouter
BrowserRouter使用浏览器的History API来管理url及与浏览器进行交互, 需要服务器增加配置以让所有的url请求返回同一个页面
HashRouter将页面当前位置存储在url的hash部分(http://example.com/#/your/page.),不需要服务器增加特殊配置
1.3.2 路由匹配组件Route和Switch
Switch组件搜索其下路由Route组件,渲染第一个匹配到的路由而忽略其他
Route为视图渲染出口
import React from "react";
import ReactDOM from "react-dom";
import {
BrowserRouter as Router,
Switch,
Route
} from "react-router-dom";
function App() {
return (
<div>
<Switch>
{/*
/about会命中,并忽略下面的路由
*/}
<Route path="/about">
<About />
</Route>
{/*
这个路由更加精确
*/}
<Route path="/contact/:id">
<Contact />
</Route>
<Route path="/contact">
<AllContacts />
</Route>
{/*
之前没有路由命中,此路由作为fallback, 可以在这里渲染404页面
*/}
<Route path="/">
<Home />
</Route>
</Switch>
</div>
);
}
ReactDOM.render(
<Router>
<App />
</Router>,
document.getElementById("root")
);
1.3.3 导航组件Link,NavLink和Redirect
Link组件用来在应用中创建链接。Link会被渲染成a标签
<Link to="/">Home</Link>
// <a href="/">Home</a>
NavLink是一种特殊类型的Link,支持自动添加active class
<NavLink to="/react" activeClassName="hurray">
React
</NavLink>
// When the URL is /react, this renders:
// <a href="/react" className="hurray">React</a>
// When it's something else:
// <a href="/react">React</a>
任何时候你想强制导航,你可以渲染一个Redirect组件。当渲染时,它将使用其来支持导航
<Redirect to="/login" />
1.4 代码分割
即code-splitting, 网页的增量下载, 未使用到的包不会加载
我们使用webpack, @babel/plugin-syntax-dynamic-import, 和 loadable-components来实现代码分割
首先安装依赖包
yarn add @babel/preset-react @babel/plugin-syntax-dynamic-import loadable-components --dev
配置.babelrc文件(没有的话在项目根目录下新建一个)
{
"presets": ["@babel/preset-react"],
"plugins": ["@babel/plugin-syntax-dynamic-import"]
}
修改App.js
import React from "react";
import { BrowserRouter as Router, Route, Switch } from "react-router-dom";
import loadable from "@loadable/component";
import Loading from "../components/Loading";
import "./App.css";
const Counter = loadable(() => import("../features/counter/Counter"), {
fallback: <Loading />,
});
const Book = loadable(() => import("../features/book/Book"), {
fallback: <Loading />,
});
function App() {
return (
<Router>
<Switch>
<Route exact path="/" component={Counter} />
<Route path="/book" component={Book} />
</Switch>
</Router>
);
}
export default App;
1.5 滚动状态恢复
当路由切换时候页面自动滚动到顶部或者恢复滚动位置
import { useEffect } from "react";
import { useLocation } from "react-router-dom";
export default function ScrollToTop() {
const { pathname } = useLocation();
useEffect(() => {
window.scrollTo(0, 0);
}, [pathname]);
return null;
}
不使用Hook(v16.8以上才能够使用), 可以使用react-router-dom提供的withRouter高阶函数
import React from "react";
import { withRouter } from "react-router-dom";
class ScrollToTop extends React.Component {
componentDidUpdate(prevProps) {
if (
this.props.location.pathname !== prevProps.location.pathname
) {
window.scrollTo(0, 0);
}
}
render() {
return null;
}
}
export default withRouter(ScrollToTop);
1.6 重要API的适应
BrowserRouter
基于H5 History接口的路由
<BrowserRouter
basename={optionalString} <!-- 当应用放在服务器非根目录时候配置这个属性,否则不用配置 -->
forceRefresh={optionalBool} <!-- 路由切换时候是否整页刷新 -->
getUserConfirmation={optionalFunc}
keyLength={optionalNumber} <!-- location.key的长度,默认为6 -->
>
<App />
</BrowserRouter>
HashRouter
使用URL的哈希部分(例如window.location.hash)来保持你的UI与URL同步。
<HashRouter
basename={optionalString} <!-- url基路径, 项目部署在服务器非根目录时候使用 -->
getUserConfirmation={optionalFunc}
hashType={optionalString} <!-- 使用的hash编码类型"slash"(默认), "noslash", "hashbang" -->
>
<App />
</HashRouter>
Link
声明式路由组件
<Link to="/about">About</Link>
-
to
导向哪个路由,可以为字符串,也可以为一个对象或者函数<Link to="/courses?sort=name" />
<Link to={{ pathname: "/courses", // 路径 search: "?sort=name", // 查询参数 hash: "#the-hash", // hash值 state: { fromDashboard: true } // 持久化到location的状态数据 }} />
一个函数,当前位置作为参数传递给它,并且应该以字符串或对象的形式返回位置表示
<Link to={location => ({ ...location, pathname: "/courses" })} />
<Link to={location => `${location.pathname}?sort=name`} />
其他可用属性
- replace: 当为true时,单击该链接将替换历史堆栈中的当前条目,而不是添加一个新条目。
- innerRef
值为函数
<Link
to="/"
innerRef={node => {
// node指向挂载的dom元素, 卸载时候为null
}}
/>
值为Ref对象
let anchorRef = React.createRef()
<Link to="/" innerRef={anchorRef} />
- component
定制化自己的导航组件
const FancyLink = React.forwardRef((props, ref) => (
<a ref={ref} {...props}>💅 {props.children}</a>
))
<Link to="/" component={FancyLink} />
- title, id, className等a标签接受的属性
NavLink
的一个特殊版本,当呈现的元素与当前URL匹配时,它将向该元素添加样式属性。-
activeClassName
当元素处于active状态时,类将提供该class。默认的给定class是active。这将与className样式叠加 -
activeStyle 内嵌方式声明active状态样式
-
exact 布尔类型, 为true是路径完全匹配才会添加active class
-
strict 路径匹配是否严格, 为true的话结尾的斜杠会被考虑
-
isActive函数, 可以自定义active class添加逻辑
<NavLink
to="/events/123"
isActive={(match, location) => {
if (!match) {
return false;
}
// only consider an event active if its event id is an odd number
const eventID = parseInt(match.params.eventID);
return !isNaN(eventID) && eventID % 2 === 1;
}}
>
Event 123
</NavLink>
Redirect
重定向
<Route exact path="/">
{loggedIn ? <Redirect to="/dashboard" /> : <PublicHomePage />}
</Route>
to也可以为对象
<Redirect
to={{
pathname: "/login",
search: "?utm=your+face",
state: { referrer: currentLocation }
}}
/>
push属性: 当为真时,重定向将把一个新的条目推送到历史中,而不是取代当前的条目。
from属性: 要重定向的路径名。路径-regexp@^1.7.0能够理解的任何有效URL路径。在to中为模式提供了所有匹配的URL参数。必须包含to中使用的所有参数。不被to使用的其他参数将被忽略。
<Switch>
<Redirect from="/old-path" to="/new-path" />
<Route path="/new-path">
<Place />
</Route>
</Switch>
// Redirect with matched parameters
<Switch>
<Redirect from="/users/:id" to="/users/profile/:id" />
<Route path="/users/profile/:id">
<Profile />
</Route>
</Switch>
exact属性, 路径是否完全匹配
strict属性: 路径匹配是否严格,区分斜杠
sensitive属性: 路径匹配是否大小写敏感
Route
路由组件可能是反应路由器中最重要的组件,了解和学习使用好。它最基本的职责是在路径与当前URL匹配时呈现某个UI
route component
只有路由匹配才会挂载component指定的组件
ReactDOM.render(
<Router>
<Route path="/user/:username" component={User} />
</Router>,
node
);
render func
路由匹配函数就会调用
ReactDOM.render(
<Router>
<Route path="/home" render={() => <div>Home</div>} />
</Router>,
node
);
children func
不管路由是否匹配都会渲染对应组件
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
);
上面的例子中如果路径匹配的话会渲染带有active class的li
当添加动画过渡效果时候children属性也很有用
<Route
children={({ match, ...rest }) => (
{/* Animate will always render, so you can use lifecycles
to animate its child in and out */}
<Animate>
{match && <Something {...rest}/>}
</Animate>
)}
/>
1.7 几个重要变量
location
代表当前的位置信息
{
key: 'ac3df4', // not with HashHistory!
pathname: '/somewhere',
search: '?some=search-string',
hash: '#howdy',
state: {
[userDefined]: true
}
}
location会出现在下面几个地方
Route component(路由组件)中使用 this.props.location
Route组件的 render函数 as ({ location }) => ()
Route组件的children属性 as ({ location }) => ()
withRouter高阶函数包裹的组件中使用 as this.props.location
match
一个说明路由为何匹配的对象,包含路由跳转参数params, 是否精确匹配isExact, 路径path, url
- 路由组件(Route component)中使用this.props.match
- 路由render函数中解构出match对象Route render as ({ match }) => ()
- 路由children函数中解构出match对象Route children as ({ match }) => ()
- withRouter高阶函数包裹组件中使用 this.props.match
- matchPath
- useRouteMatch
可以基于此来实现路由嵌套
import { Link, Switch, Route } from "react-router-dom";
import { useRouteMatch } from "@/utils";
export default function NestRoute() {
const { path, url } = useRouteMatch("/demo");
return (
<>
<h3>路由嵌套</h3>
<ul>
<li>
<Link to={`${url}/demo1`}>demo1</Link>
</li>
<li>
<Link to={`${url}/demo2`}>demo2</Link>
</li>
</ul>
<Switch>
<Route path="/demo" exact>
请选择一个实例
</Route>
<Route
path={`${path}/:demoId`}
render={({ match }) => <h1>{match.params.demoId}</h1>}
></Route>
</Switch>
</>
);
}
history
路由历史对象,包含以下属性
- length - (number) 历史堆栈中的条目数
- action - (string) 当前动作类型 (PUSH, REPLACE, or POP)
- location - (object) 当前的location对象,可能包含以下属性:
- pathname - (string) URL的path部分
- search - (string) URL的query部分
- hash - (string) URL hash部分
- state - (object)位置特定的状态,当此位置被推入堆栈时提供的推入状态(路径、状态)。仅在浏览器和内存历史中可用.
- push(path, [state]) - (function) 将新条目推入历史堆栈
- replace(path, [state]) - (function)替换历史堆栈上的当前条目
- go(n) - (function) 在历史堆栈中移动n(可正可负,即向前或者向后)个条目的指针
- goBack() - (function) 等价于go(-1), 后退一页
- goForward() - (function) 等价于 go(1), 前进一页
历史对象是可变的。因此,建议从渲染道具<Route>中访问位置,而不是从history.location中访问
1.8 常用Hooks
react >= 16.8
useHistory
import { useHistory } from "react-router-dom";
function HomeButton() {
let history = useHistory();
function handleClick() {
history.push("/home");
}
return (
<button type="button" onClick={handleClick}>
Go home
</button>
);
}
useLocation
获取位置对象
let location = useLocation();
useParams
useParams返回一个包含URL参数的键/值对的对象。使用它来访问match。当前<Route>的参数。
import React from "react";
import ReactDOM from "react-dom";
import {
BrowserRouter as Router,
Switch,
Route,
useParams
} from "react-router-dom";
function BlogPost() {
let { slug } = useParams();
return <div>Now showing post {slug}</div>;
}
ReactDOM.render(
<Router>
<Switch>
<Route exact path="/">
<HomePage />
</Route>
<Route path="/blog/:slug">
<BlogPost />
</Route>
</Switch>
</Router>,
node
);
useRouteMatch
useRouteMatch钩子尝试以与相同的方式匹配当前URL。它主要用于在不实际呈现<Route>的情况下访问匹配数据
import { Route } from "react-router-dom";
function BlogPost() {
return (
<Route
path="/blog/:slug"
render={({ match }) => {
// Do whatever you want with the match...
return <div />;
}}
/>
);
}
使用useRouteMatch的话就可以不实际渲染出<Route>了
import { useRouteMatch } from "react-router-dom";
function BlogPost() {
let match = useRouteMatch("/blog/:slug");
// Do whatever you want with the match...
return <div />;
}
1.9 路由传参
1.通配符传参
Route定义方式:
<Route path='/path/:name' component={Path}/>
Link组件:
<Link to="/path/通过通配符传参">通配符</Link>
参数获取:
this.props.match.params.name
优点:简单快捷,并且,在刷新页面的时候,参数不会丢失。
缺点:只能传字符串,并且,如果传的值太多的话,url会变得长而丑陋。
如果,你想传对象的话,可以用JSON.stringify(),想将其转为字符串,然后另外的页面接收后,用JSON.parse()转回去。
2.query
Route定义方式:
<Route path='/query' component={Query}/>
Link组件:
var query = {
pathname: '/query',
query: '我是通过query传值 '
}
<Link to={query}>query</Link>
参数获取:
this.props.location.query
优点:优雅,可传对象
缺点:刷新页面,参数丢失
3.state传参
Route定义方式:
<Link to={state}>state</Link>
Link组件:
var state = {
pathname: '/state',
state: '我是通过state传值'
}
<Route path='/state' component={State}/>
参数获取:
this.props.location.state
优点:优雅,可传对象
缺点:刷新页面,参数丢失