React-Router是React生态的基础路由库,它通过管理URL,实现组件的切换和状态的变化。v4之前属于静态路由,而v4之后属于动态路由。
静态路由
所谓“静态路由”,就是说路由规则是固定的。只要应用程序一启动,映射关系就固定不变。无论 express、Angular 还是 Rails 等业界响当当的框架,都用的是静态路由。以 express 为例,路由规则差不多是这么写的:
app.get('/', Home);
app.get('/product/:id', Product);
app.get('/about', About);
特点:
- 路由集中在一个地方。
- 布局和页面嵌套是通过<Route>组件的嵌套而来的。
我们可以:
- 初始化路由的时候,加点业务逻辑去动态生成路由配置
- 通过路由参数(query)可以动态控制页面的输出
react-router v3 静态路由
import { Router, Route, IndexRoute } from 'react-router'
const PrimaryLayout = props => (
<div className="primary-layout">
<header>
Our React Router 3 App
</header>
<main>
{props.children}
</main>
</div>
)
const HomePage =() => <div>Home Page</div>
const UsersPage = () => <div>Users Page</div>
const App = () => (
<Router history={browserHistory}>
<Route path="/" component={PrimaryLayout}>
<IndexRoute component={HomePage} />
<Route path="/users" component={UsersPage} />
</Route>
</Router>
)
render(<App />, document.getElementById('root'))
路由配置集中在 App 函数中,<Route> 直接嵌套于 <Router>,页面组件融于其中作为路由的一部分。这便是整个路由的配置。
动态路由
所谓动态路由,指的是路由规则不是预先确定的,而是在渲染过程中确定的。
既然 React 组件渲染是动态发生的,那么就让 Route 变成一个 React 组件,和其他组件一样被渲染,在运行时完全可以决定某个Route渲染还是不渲染,也可以决定渲染这个 Route 的参数 props 是怎样,如此一来,路由规则也就完全动态了。
react-router v4 例子
import { BrowserRouter, Route } from 'react-router-dom'
const PrimaryLayout = () => (
<div className="primary-layout">
<header>
Our React Router 4 App
</header>
<main>
<Route path="/" exact component={HomePage} />
<Route path="/users" component={UsersPage} />
</main>
</div>
)
const HomePage =() => <div>Home Page</div>
const UsersPage = () => <div>Users Page</div>
const App = () => (
<BrowserRouter>
<PrimaryLayout />
</BrowserRouter>
)
render(<App />, document.getElementById('root'))
路由穿插在了各组件中,在嵌套路由(Nested Routes)的场景尤为明显。
Router 组件
react-router 的工作方式,是在组件树顶层放一个 Router 组件,然后在组件树中散落着很多 Route 组件(注意比 Router 少一个“r”),顶层的 Router 组件负责分析监听 URL 的变化,在它之下的 Route 组件可以直接读取这些信息。Router 是“提供者”,Route是“消费者”。
- BrowserRouter:使用于现代浏览器,支持H5 history API
- HashRouter:常用于旧款浏览器。根据不同的hashType
- MemoryHistory:常用于非DOM环境,例如React Native或者测试,History存于内存。
引入方式:
import {Route, BrowserRouter as Router} from "react-router-dom";
Route 组件
Route
只是一个具有渲染方法的普通 React 组件,路由匹配成功则渲染该组件。
三个常用属性:
- path:路由的匹配规则(可以省略)
- exact:用于精确匹配路由(可以省略)
- component:需要渲染的组件
所以一个完整的路由:
import {Route, BrowserRouter as Router} from "react-router-dom";
import A from 'A'
import B from 'B'
const routing = (
<Router>
<Route component={Home} >
<Route exact path="/a" component={A} />
<Route exact path="/b" component={B} />
</Router>
)
ReactDOM.render(routing, document.getElementById("root"));
Link 与 NavLink 组件
对于单页应用,需要在不同页面之间切换,往往需要一个导航栏。
<Link to="/">Home</Link>
NavLink 组件,是基于 Link 组件,它有一个 activeClassName 属性,目的在于如果路由匹配成功,则为当前导航添加选中样式。
import React from 'react';
class Navigation extends Component {
render() {
<div>
<NavLink activeClassName="active" to="/a">A</NavLink>
<NavLink activeClassName="active" to="/b">B</NavLink>
</div>
}
}
history对象
每个router都会创建一个history对象,用来保持对当前位置的追踪还有在页面发生变化的时候重新渲染页面。
React Router提供的其他组件依赖在context上储存的history对象,所以他们必须在router对象的内部渲染。
一个没有router祖先元素的React Router对象将无法正常工作。history对象还可以实现程序化导航(我们需要在当前路由状态下发生路由改变时,重定向用户到新的路由)。
例如,当用户成功登录时,用户被重定向到个人主页。
import React from "react";
class Contact extends React.Component {
onSubmit = () => {
this.props.history.push("/");
};
render() {
return (
<form>
<input placeholder="name" type="name" />
<input placeholder="email" type="email" />
<button onClick={this.onSubmit}>Submit</button>
</form>
);
}
}
export default Contact;
Switch组件:实现404页面
404 代表页面不存在(not found),也就是导航路径不存在的时候,我们需要让用户感知到提示信息,此时就需要添加404页面。
react-router 提供了一个Switch 组件,用于支持当路由匹配成功,渲染对应组件。若未能匹配路由,则渲染404组件。
React-Router v4
在v4中React-Router仓库被拆分成了多个包进行发布:react-router、react-router-dom、react-router-native、react-router-config。
- react-router:路由基础库
- react-router-dom:适用于浏览器环境的再次封装
- react-router-native:适用于react-native环境的再次封装
- react-router-config:静态路由配置助手
React-Routerv5 vs v4
原本只是计划发布React Router 4.4版本,但由于错误地使用了托字符 (^) —— 将依赖错误地写成 "react-router": "^4.3.1",导致报错。最后团队决定撤销 4.4 版本,直接改为发布 React Router v5。
- 完全兼容老版本v4
- 支持 React 16 , 兼容 React >= 15
- 消除在 React.StrictMode 严格模式中的警告
- 使用 create-react-context 实现 context API
React Router v5.1.x中的新功能
useParams
useParams可以帮助我们。在各层组件中,轻松访问router的params参数。在V5.1版本之前,我们需要通过props.match获取路由参数。对于更深层的组件还需要使用高阶组件withRouter。
useLocation
useLocation可以帮助我们。在各层组件中,轻松获取location对象。在V5.1版本之前,我们需要使用props.location。而对于更深层的组件,还需要使用withRouter。
useHistory
useHistory可以帮助我们访问history对象,进行编程式的导航。
useRouteMatch
useRouteMatch,接受一个path字符串作为参数。当参数的path与当前的路径相匹配时,useRouteMatch会返回match对象,否则返回null。
useRouteMatch在对于一些,不是路由级别的组件。但是组件自身的显隐却和当前路径相关的组件时,非常有用。
比如,你在做一个后台管理系统时,网页的Header只会在登录页显示,登录完成后不需要显示,这种场景下就可以用到useRouteMatch。
const Home = () => {
return (
<div>Home</div>
)
}
// Header组件只会在匹配`/detail/:id`时出现
const Header = () => {
// 只有当前路径匹配`/detail/:id`时,match不为null
const match = useRouteMatch('/detail/:id')
return (
match && <div>Header</div>
)
}
const Detail = () => {
return (
<div>Detail</div>
)
}
function App() {
return (
<div className="App">
<Router>
<Header/>
<Switch>
<Route exact path="/" component={Home}/>
<Route exact path="/detail/:id" component={Detail}/>
</Switch>
</Router>
</div>
);
}
Link和NavLink组件的to属性支持函数
function App() {
return (
<div className="App">
<Router>
{/* 函数的返回值等于Link的to跳转的位置 */}
<Link to={
(location) => {
return `${location.pathname}?sort=age`
}
}>go</Link>
</Router>
</div>
);
}
原文地址:
https://zhuanlan.zhihu.com/p/78176856
https://juejin.im/post/6844904037876236296