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 (
<Router basename='/route'>
<div>
<Route path="/" exact component={Home}></Route>
<Route path="/mime" component={Mime}></Route>
<Route path="/list" component={List}></Route>
<ul>
<li>
<Link to="/">home</Link>
</li>
<li>
<Link to="/mime">mime</Link>
</li>
<li>
<Link to="/list">list</Link>
</li>
</ul>
</div>
</Router>
);
}
}
二级路由
在一级路由的基础上再做路由,通过props中的match属性可以获取当前的url(),然后再去拼接二级路由的path。
export default class List extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<div>
<h1>列表页</h1>
<ul>
<li>
<Link to={`${this.props.match.url}/details`}>详情</Link>
</li>
<li>
<Link to={`${this.props.match.url}/edit`}>编辑</Link>
</li>
</ul>
<Route path={`${this.props.match.url}/details`} component={Details} />
<Route path={`${this.props.match.url}/edit`} component={Edit} />
</div>
);
}
}
带参数
如果路由后面要携带参数,可以这样写(最后括号中的值表示只能是这两个值,如果没有此限制则可以是任何值)
<Route
path={`${this.props.match.url}/:id(20|100)`}
component={Details}
/>
在子组件中通过this.props.match.params.xx
获取对应的参数
<h1>{this.props.match.params.id}</h1>
<BrowserRouter>和<HashRouter>
他们就是路由的根基,包裹在最外层。之前使用路由的肯定知道(不管是哪个版本,甚至vue的路由),路由的实现有两种方法,HashRouter
是利用URL的hash部分实现,在url中会经常有个#号,主要是针对旧版浏览器。我们一般都使用BrowserRouter
,它是利用H5的history API实现。
还有一种实现是针对非DOM环境的(react-native),利用内存实现。
- basename:如果你的文件在服务器的二级目录下,就会用到它。他会在每一个路由路径前面自动添加一级目录。如下图,当我们点击list时,url中会自动添加route目录。
该组件下只能有一个子组件,所有组件都要包裹在一层里。
<Route>
Route就是控制不同路径去对应显示内容的组件。配合Link使用。
-
exact:控制严格匹配。如果没有这个属性,当访问
list
时,home组件和list组件会同时被渲染。 -
path:访问的路径,如果没有该属性,那么将会匹配一切路径。
-
component:path路径对应要显示的组件。使用最多。如果要获取match等对象。可以使用
this.props.match
... -
render:与component一样,可以直接在后面写要渲染的内容。可以获取到mach对象。
<Route path="/home" render={({match}) => <div>Home</div>}/>
- children:和render是一样的功能,不过它不论是否匹配到都会调用。
<Route path="/mime" component={Mime}></Route>
<Route path="/list" children={({match})=> (
<div>
<h3>哈哈</h3>
<List></List>
</div>
)}></Route>
以上代码,不管当前匹配的是mime还是list或是其他,都会渲染list。
<Link>和<NavLink>
Link
的api相对较少,上面我们使用的是最简单的to,直接跳转到一个地址。就不多说了。
- to:还可以携带参数到指定页面,后面跟一个对象就可以。如下(可以实现页面间传参)
<Link to={{
pathname: '/list',
search: '?sort=name',
state: { id: 2 }
}} />
state可以传递任何类型的参数,而且类似于post方式,可以不以明文传输。在跳转的页面中实用this.props.location.state获取数据。
- replace:基本不用,为true时会替换掉上次访问的地址,也就是使用浏览器的回退按钮无法返回上一个页面。
NavLink
很明显就是为页面导航准备的,因为导航是要有激活状态的。
- activeClassName:激活时要渲染的样式名
<NavLink to="/mime" activeClassName='actvived'>mime</NavLink>
- activeStyle:直接把激活的样式写在后面
<NavLink to="/mime" activeStyle={{color: red,fontSize: 20px}}>mime</NavLink>
-
exact:如果为true,只有当访问地址严格匹配时才会使用激活样式。
-
strict:如果为true,只有挡访问地址后的斜杠严格匹配(有或没有)才会使用激活样式。
-
isActive:跟一个函数,当导航激活时要做的事情。
<StaticRouter>
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(
<StaticRouter location={req.url} context={context}>
<App/>
</StaticRouter>
)
// 如果使用<Redirect>,context.url将包含要重定向到的URL
if (context.url) {
res.writeHead(302, {
Location: context.url
})
res.end()
} else {
res.write(html)
res.end()
}
}).listen(3000)
- basename:所有位置的基本url,正确的格式应该是前面有
/
后面没有/
。
<StaticRouter basename="/main">
<Link to="/list"/> <!--渲染出来应该是<a href="/main/list">-->
</StaticRouter>
- location:如果是个字符串就应该是服务器上收到的url(req.url)。如果是个对象应该是一个类似
{pathname, search, hash, state}
的位置对象。
<StaticRouter location={{ pathname: '/home' }}>
<App/>
</StaticRouter>
- context:一个普通js对象,在渲染过程中组件可以向对象里添属性以存储有关渲染的信息。
我们使用它来找出渲染结果,context上下文对象是自己的,我们可以改变它。
//app上有个404组件,把status改成404
const NotFound = () => (
<Route
render={({ staticContext }) => {
if (staticContext) {
staticContext.status = 404;
}
return (
<div>
<h1>你输入的地址不正确哟!</h1>
</div>
);
}}
/>
);
// 在服务端我们就可以通过判断,对这个404界面发送404响应
const context = {};
const content = renderToString(
<Provider store={store}>
<StaticRouter location={req.url} context={context}>
<App />
</StaticRouter>
</Provider>
);
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)
}
<Switch>
用来包裹Route或者Redirect组件,而且不能放其他元素,用来渲染第一个匹配到的路由,不会向下继续匹配。如果不用Switch包裹的话,访问/about时下面三个组件都会被渲染。
<Route path="/about" component={About}/>
<Route path="/:user" component={User}/>
<Route component={NoMatch}/>
<Redirect>
将匹配到的路径重定向到一个新的地址。新的地址应该覆盖掉访问的地址。
<Route path="/game" component={Game}></Route>
<Redirect from="/home" to="game" push={true}></Redirect>
-
from:匹配到的访问的地址
-
to:重定向到的地址
-
push:为true时,重定向到的地址会添加到历史访问记录,并且无法回到之前访问的地址。
<withRouter>
withRouter可以用来包装任何自定义组件,并将history
、location
、match
三个对象传入。这样组件就可以拿到路由信息。
import {withRouter} from 'react-router-dom';
const Home = withRouter(({history,location,match})=>{
return <h1>{location.pathname}</h1>
})
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
<Route path>
如何与url匹配的信息,包含以下内容:
-
params:URL动态段解析对应的键值对
-
isExact:如果匹配整个URL则为true
-
path:<Route>匹配的路径
-
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)
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 ? (
<Switch {...switchProps}>
{routes.map((route, i) => (
<Route
exact={route.exact}
key={route.key || i}
path={route.path}
render={props =>
(route.render ? (
route.render(props)
) : (
<route.component {...props} {...extraProps} route={route} />
))
}
strict={route.strict}
/>
))}
</Switch>
) : null);
实际上就是在一个<Switch>中创建了多个<Route>
看一下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((
<BrowserRouter>
{renderRoutes(routes)}
</BrowserRouter>
), document.getElementById('root'))
// 在组件中调用方法
const Root = ({ route }) => (
<div>
<h1>Root</h1>
{renderRoutes(route.routes)}
</div>
)
const Home = ({ route }) => (
<div>
<h2>Home</h2>
</div>
)
const Child = ({ route }) => (
<div>
<h2>Child</h2>
{renderRoutes(route.routes, { someProp: 'these extra props are optional' })}
</div>
)
const GrandChild = ({ someProp }) => (
<div>
<h3>Grand Child</h3>
<div>{someProp}</div>
</div>
)
网友评论