react-router-dom
最近开始学习reactr-router
,但是看了好多文章都有问题,后来才发现是react-router 4
做了很大的改动,遵循 Just Component 的 API 设计理念。reactr-router
被拆分为reactr-router
、reactr-router-dom
、reactr-router-native
三部分。reactr-router
提供核心的API,其余两个则提供两个特定的运行环境(浏览器和react-native)所需的组件。因此所有的路由组件导入都改为了从react-router-dom
导入。
安装
注意如果是用create-react-app脚手架的话,注意安装方式(yarn或者npm),详情参考另外一篇react脚手架搭建
npm install --save react-router-dom;
npm install --save react-router-redux;
npm install --save react-router-config;
使用
先看一下基本写法:
一级路由,做正常的路由切换
import { BrowserRouter as Router, Route, Link, Switch } from 'react-router-dom'
class App extends Component {
render() {
return (
-
home
-
mime
-
list
);
}
}
二级路由
在一级路由的基础上再做路由,通过props中的match属性可以获取当前的url(),然后再去拼接二级路由的path。
export default class List extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
列表页
-
详情
-
编辑
);
}
}
带参数
如果路由后面要携带参数,可以这样写(最后括号中的值表示只能是这两个值,如果没有此限制则可以是任何值)
在子组件中通过this.props.match.params.xx
获取对应的参数
{this.props.match.params.id}
和
他们就是路由的根基,包裹在最外层。之前使用路由的肯定知道(不管是哪个版本,甚至vue的路由),路由的实现有两种方法,HashRouter
是利用URL的hash部分实现,在url中会经常有个#号,主要是针对旧版浏览器。我们一般都使用BrowserRouter
,它是利用H5的history API实现。
还有一种实现是针对非DOM环境的(react-native),利用内存实现。
- basename:如果你的文件在服务器的二级目录下,就会用到它。他会在每一个路由路径前面自动添加一级目录。如下图,当我们点击list时,url中会自动添加route目录。
该组件下只能有一个子组件,所有组件都要包裹在一层里。
Route就是控制不同路径去对应显示内容的组件。配合Link使用。
exact:控制严格匹配。如果没有这个属性,当访问
list
时,home组件和list组件会同时被渲染。path:访问的路径,如果没有该属性,那么将会匹配一切路径。
component:path路径对应要显示的组件。使用最多。如果要获取match等对象。可以使用
this.props.match
...render:与component一样,可以直接在后面写要渲染的内容。可以获取到mach对象。
Home}/>
- children:和render是一样的功能,不过它不论是否匹配到都会调用。
(
哈哈
)}>
以上代码,不管当前匹配的是mime还是list或是其他,都会渲染list。
和
Link
的api相对较少,上面我们使用的是最简单的to,直接跳转到一个地址。就不多说了。
- to:还可以携带参数到指定页面,后面跟一个对象就可以。如下(可以实现页面间传参)
state可以传递任何类型的参数,而且类似于post方式,可以不以明文传输。在跳转的页面中实用this.props.location.state获取数据。
- replace:基本不用,为true时会替换掉上次访问的地址,也就是使用浏览器的回退按钮无法返回上一个页面。
NavLink
很明显就是为页面导航准备的,因为导航是要有激活状态的。
- activeClassName:激活时要渲染的样式名
mime
- activeStyle:直接把激活的样式写在后面
mime
exact:如果为true,只有当访问地址严格匹配时才会使用激活样式。
strict:如果为true,只有挡访问地址后的斜杠严格匹配(有或没有)才会使用激活样式。
isActive:跟一个函数,当导航激活时要做的事情。
api中介绍说这是一个从来不会改变位置的Router。当用户没有实际点击时,因此位置也从未发生变化。这在服务端渲染很有用,因为在后台上, 我们只会渲染一次,而且不会直接地对用户的交互操作做出反应。
看下API中的案例:
import { createServer } from 'http'
import React from 'react'
import ReactDOMServer from 'react-dom/server'
import { StaticRouter } from 'react-router'
createServer((req, res) => {
// 这个context包含渲染的结果
const context = {}
const html = ReactDOMServer.renderToString(
)
// 如果使用,context.url将包含要重定向到的URL
if (context.url) {
res.writeHead(302, {
Location: context.url
})
res.end()
} else {
res.write(html)
res.end()
}
}).listen(3000)
- basename:所有位置的基本url,正确的格式应该是前面有
/
后面没有/
。
- location:如果是个字符串就应该是服务器上收到的url(req.url)。如果是个对象应该是一个类似
{pathname, search, hash, state}
的位置对象。
- context:一个普通js对象,在渲染过程中组件可以向对象里添属性以存储有关渲染的信息。
我们使用它来找出渲染结果,context上下文对象是自己的,我们可以改变它。
//app上有个404组件,把status改成404
const NotFound = () => (
{
if (staticContext) {
staticContext.status = 404;
}
return (
你输入的地址不正确哟!
);
}}
/>
);
// 在服务端我们就可以通过判断,对这个404界面发送404响应
const context = {};
const content = renderToString(
);
if (context.status === 404) {
res.status(404);
}
或者判断重定向,如果有context.url就说明应用被重定向,这允许我们从服务器发送适当的重定向。
if (context.url) {
// can use the `context.status` that
// we added in RedirectWithStatus
redirect(context.status, context.url)
}
用来包裹Route或者Redirect组件,而且不能放其他元素,用来渲染第一个匹配到的路由,不会向下继续匹配。如果不用Switch包裹的话,访问/about时下面三个组件都会被渲染。
将匹配到的路径重定向到一个新的地址。新的地址应该覆盖掉访问的地址。
from:匹配到的访问的地址
to:重定向到的地址
push:为true时,重定向到的地址会添加到历史访问记录,并且无法回到之前访问的地址。
withRouter可以用来包装任何自定义组件,并将history
、location
、match
三个对象传入。这样组件就可以拿到路由信息。
import {withRouter} from 'react-router-dom';
const Home = withRouter(({history,location,match})=>{
return {location.pathname}
})
history
表示浏览器历史记录的对象,可以对它进行一系列操作,有以下属性和方法:
length:获取历史堆栈中的条目数
action:获取当前操作(push/pop/replace)
location:获取当前位置,包括(pathname、search、hash、state、)
push(''):添加一个新的条目到历史堆栈(一般会用来跳转到一个新页面)
replace(''):替换掉当前堆栈上的条目
go(n):通过n个条目移动历史堆栈中的指针,向前或向后n个
goBack():后退一个历史条目,相当于go(-1)。
goForward():前进一个历史条目,相当于go(1)
block:防止导航
match
如何与url匹配的信息,包含以下内容:
params:URL动态段解析对应的键值对
isExact:如果匹配整个URL则为true
path:
匹配的路径 url:当前真正访问的路径
location
程序当前所在的位置。。。
react-router-redux
如果使用的react-router-redux
(把react-router url的变化、参数等信息作为状态,交给redux的store管理),则可以直接从state中获取路由信息。
import {routeMiddleware} from 'react-router-redux';
const store = createStore(combineReducers, applyMiddleware(routeMiddleware));
const mapStateToProps = (router) => ({
pathname: rourter.location.pathname
})
export default connect(mapStateToProps)(MyControl)
具体请看API
react-router-config
react-router-config
是帮助我们配置静态路由的。算是比较好理解的,API只有两个方法:
matchRoutes(routes, pathname)
该方法接收两个参数(routes配置,请求路径),返回一个数组。
import { matchRoutes } from 'react-router-config'
const branch = matchRoutes(routes, '/child/23')
这个返回的数组的每一个元素包含两个属性routes和match。结构如下:
[
{
route: {
path: '/app',
component: [(Function: component)],
routes: [Array]
},
match: { path: '/app', url: '/app', isExact: false, params: {} }
},
{
route: { path: '/app/resources', component: [Object] },
match: {
path: '/app/resources',
url: '/app/resources',
isExact: true,
params: {}
}
}
];
这个方法在服务端貌似用的多一些,自己没有用过不说太多。。。
renderRoutes(routes, extraProps = {})
该方法接受两个参数:配置的路由和要传的值。
写这篇文章的时候,react-router-config的renderRoutes方法并没有更新的npm上,其实已经实现了,不知道为什么没有上npm。看下源码:
import React from 'react';
import Switch from 'react-router/Switch';
import Route from 'react-router/Route';
const renderRoutes = (routes, extraProps = {}, switchProps = {}) =>
(routes ? (
{routes.map((route, i) => (
(route.render ? (
route.render(props)
) : (
))
}
strict={route.strict}
/>
))}
) : null);
实际上就是在一个
中创建了多个
看一下API中给出的代码示例,路由可以进行统一的配置了,不过有个缺陷就是:在组件的render方法中还需要调用renderRoutes方法。
import { renderRoutes } from 'react-router-config'
// 配置路由
const routes = [
{ component: Root,
routes: [
{ path: '/',
exact: true,
component: Home
},
{ path: '/child/:id',
component: Child,
routes: [
{ path: '/child/:id/grand-child',
component: GrandChild
}
]
}
]
}
]
// 设置路由
ReactDOM.render((
{renderRoutes(routes)}
), document.getElementById('root'))
// 在组件中调用方法
const Root = ({ route }) => (
Root
{renderRoutes(route.routes)}
)
const Home = ({ route }) => (
Home
)
const Child = ({ route }) => (
Child
{renderRoutes(route.routes, { someProp: 'these extra props are optional' })}
)
const GrandChild = ({ someProp }) => (
Grand Child
{someProp}
)