React路由库。基本react项目开发过程中都会用到。
API
Router
<BrowserRouter>
使用 HTML5 提供的 history API( pushState , replaceState 和 popstate 事件) 来保持 UI和 URL 的同步。(推荐使用)
<HashRouter>
使用 URL 的 hash 部分(即window.location.hash )来保持 UI 和 URL 的同步。(不推荐使用)
<MemoryRourter>
把 URL 的历史记录保存在内存中的 <Router>
(不读取、不写入地址栏)。在测试和非浏览器环境中很有用,如ReactNative。
注意:
<HashRouter>
不支持 location.key 和location.state。在以前的版本中,我们曾尝试 shim 这种行为,但是仍有一些边缘问题无法解决。因此任何依 赖此行为的代码或插件都将无法正常使用。由于该技术仅 用于支持旧浏览器,因此我们鼓励大家使用<BrowserHistory>
。
BrowserRouter与HashRouter对比:
- HashRouter最简单,不需要服务器端渲染,靠浏览器的# 的来区分path就可以,BrowserRouter需要服务器端对不 同的URL返回不同的HTML,后端需要配置。
- BrowserRouter使⽤用HTML5 history API( pushState, replaceState和popstate事件),让页面的UI于URL同 步。
- HashRouter不⽀支持location.key和location.state,动态路 由跳转需要通过?传递参数。
- Hash history 不需要服务器任何配置就可以运行,如果你刚入门,那就使用它吧。但是我们不推荐在实际线上环境中用到它,因为每一个 web应用都应该渴望使用browserHistory 。
Link
to: String
一个字符串形式的链接地址,通过 pathname 、 search 和hash 属性创建。
<Link to='/courses?sort=name' />
to: Object
个对象形式的链接地址,可以具有以下任何属性:
- pathname 要链接到的路径
- search 查询参数
- hash URL中的hash,例如 #adc
- state 存储到location中的状态数据
<Link to={{
pathname: '/courses',
search: '?sort=name',
hash: '#the-hash',
state: {
redirect: '/login'
}
}} />
replace: Boolean
当设置为 true 时,点击链接后将替换历史堆栈中的当前条目,而不是添加新条目。默认为 false 。
Route
当Route的path和location匹配时,展示相应的UI组件。
Route的三种渲染方式:
- children: function
- component
- render: function
在不同的情况下使用不同的方式。在指定的
<Route>
中,你应该只使用其中的一种。 Route渲染优先级:children > component > render
path: String
可以是 path-to-regexp 能够理解的任何有效的 URL 路径。
<Route path="/users/:id" component={User} />
没有定义
path
的<Route>
总是会被匹配
location: Object
⼀般情况下, <Route>
尝试将其 path
与当前history location
(通常是当前的浏览器 URL)进⾏行行匹配。但是,带
有不同pathname
的location
也可以与之匹配。
当你需要将<Route>
与一个不是当前location
的location
进行匹配时,会发现这个api非常有用。如过渡动画。
如果⼀个<Route>
被包裹在一个<Switch>
中,并且与
<Switch>
的location
相匹配(或者是当前的location
),那
么<Route>
的location
参数将被<Switch>
所使用的
location
覆盖。 (ps:我也不太明白,等会去实践一下,这就是官网的解释)
Redirect
to:String
要重定向到的 URL,可以是 path-to-regexp 能够理解的任何 有效的 URL 路径。所有要使用的 URL 参数必须由 from 提供。
<Redirect to="/somewhere/else" />
to: Object
要重定向到的位置,其中 pathname 可以是 path-to-regexp 能够理解的任何有效的 URL 路径。
<Redirect to={{
pathname: '/login',
search: '?utm=your+face',
state: {
referrer: currentLocation
}
}} />
state 对象可以在重定向到的组件中通过this.props.location.state 进行访问
Switch
用于渲染与路径匹配的第一个子<Route>
或 <Redirect>
。
<Route path="/login" component={Login} />
<Route path="/hoc" component={HocComponent} />
<Route path="/render" render={() => <h1>render div</h1>} />
<Route children={() => <h2>children div</h2>} />
这段代码,最后一个Route不管path匹不匹配都会一直渲染
<Switch>
<Route path="/login" component={Login} />
<Route path="/hoc" component={HocComponent} />
<Route path="/render" render={() => <h1>render div</h1>} />
<Route children={() => <h2>children div</h2>} />
</Switch>
加上Switch,当location匹配到/login
、 /hoc
、/render
的时候,就会显示对应的UI,当location和这些都不匹配,则展示最后一个没有Route
。
所有
<Switch>
的子元素都应该是<Route>
或<Redirect>
。只有第⼀个匹配当前路径的子元素将被呈现。
源码实现
react router源码实现基于React 的高阶API
React.Context
实现。不了解的可以看下初学Context
BrowserRouter
简单分析功能:BrowserRouter
作用是把history和location传递下去。当location改变时,监听改变location的数据。
简单实现:
import React, { Component } from 'react';
import {createBrowserHistory} from "history";
import { RouterProvider } from "./RouterContext";
export default class BrowserRouter extends Component {
constructor(props) {
super(props);
this.history = createBrowserHistory();
this.state={
location: this.history.location
}
this.unlisten = this.history.listen((location) => {
this.setState({location})
})
}
componentWillUnmount(){
this.unlisten = null;
}
render() {
return (
<RouterProvider value={{history: this.history, location: this.state.location}}>
{this.props.children}
</RouterProvider>
)
}
}
Link
简单分析功能:就是个<a>
的功能,当用户点击的时候,改变location
。
简单实现:
import React, { Component } from 'react';
import { RouterContext } from './RouterContext';
export default class Link extends Component {
static contextType = RouterContext;
handleClick = (e) => {
e.preventDefault();
this.context.history.push(this.props.to)
}
render() {
return (
<a onClick={this.handleClick}>
{this.props.children}
</a>
)
}
}
Route
简单分析功能:Route需要根据location和path进行比较,如果匹配,则返回相应的组件。但是Route的渲染方式有三种:children component render
渲染顺序依次是children > component > render
简单实现:
import React, { Component } from "react";
import { RouterConsumer, RouterProvider } from "./RouterContext";
export default class Route extends Component {
render() {
return (
<RouterConsumer>
{context => {
const { path, component, children, render } = this.props;
const location = this.props.location || context.location;
const match = path === context.location.pathname;
const props = {
...context,
location,
match
};
// 第一层
// match 渲染顺序 children > component > render
// no match 渲染children或者null
// 第二层
// children 存在 判断children是不是函数,是 则调用,否 则返回children
// children 不存在 判断 component是否存在
// 第三层
// component 存在 返回 component
// component 不存在 判断render是否存在
// 第四层
// render 存在 调用render
// render 不存在 返回null
return (
<RouterProvider value={props}>
{
match
? ( children
? typeof children === "function"
? children(this.props)
: children
: component
? React.createElement(component, this.props)
: render
? render(this.props)
: null
) : (
typeof children === "function" ? children(this.props) : null
)
}
</RouterProvider>
)
}}
</RouterConsumer>
);
}
}
Switch
暂时还没实现... 等实现再加上
That's all. Thanks
脚踏实地,仰望星空。