[ 译 ] React Router v4 Api文档

React Router v4版的代码库中可以分为以下几个包:

本博客将重点关注react-routerreact-router-dom的api文档(其中关于服务端渲染以及react native的内容(react native路由个人还是比较喜欢使用react-navigation)将不涉及)。

一. react-router

接下来将介绍:5个组件(Router, Route, Switch, Redirect, Prompt),3个属性(history, location, match),2个函数(withRouter, generatePath)。

1.Router组件

Router组件很简单,它可以用来包裹Route, Switch, Redirect等组件,不过该组件已经不再建议使用(low-level),建议使用以下高级(high-level)组件替代它:BrowserRouter, HashRouter, MemoryRouter, NativeRouter, StaticRouter。

history属性(object):history属性用来指定url中匹配对应路由时的展示方式。(hashHistory:url中将添加随机产生的hash值(比较丑陋),browserHistory:url中的路径将与Route组件的path属性中设置的路径匹配,但是这种方式需要服务器端的支持,一旦匹配到某个路径,但是服务器端未对该路径进行单独处理,此时刷新浏览器,可能会出现404 not found的错误)。

使用如下:

import React from 'react'
import { render } from 'react-dom'
import App from './modules/App'
import About from "./modules/About"
import Repos from "./modules/Repos"
import Repo from "./modules/Repo"
import Home from "./modules/Home"
import { Router, Route, hashHistory, IndexRoute, browserHistory  } from "react-router";

render((
    <Router history={browserHistory}>
        <Route path="/" component={App}>
            <IndexRoute component={Home} />
            <Route path="/about" component={About} />
            <Route path="/repos" component={Repos}>
                <Route path="/repos/:userName/:repoName" component={Repo} />
            </Route>
        </Route>
    </Router>
), document.getElementById("app"));

2.Route组件

Route组件可能是react-router中最重要的一个组件了。它最基本的职责是:当url路径(location)匹配到路由的路径(Route’s path)时,需要渲染对应的组件(UI Component)。

考虑以下代码:

import { BrowserRouter as Router, Route } from 'react-router-dom'

<Router>
	<div>
		<Route exact path="/" component={Home}/>
		<Route path="/news" component={NewsFeed}/>
	</div>
</Router>

当url是”/”时,页面将会渲染以下结构:

<div>
	<Home/>
</div>

当url是”/news”,页面将会渲染以下结构:

<div>
	<NewsFeed/>
</div>

在Route组件上指定路径匹配时渲染的组件有以下三种方式:

  • Route component
  • Route render
  • Route children

(1)传入要渲染的组件名:
这种方式,只有在路径匹配时,才会渲染对应的组件,例如:

<Route path="/user/:username" component={User} />;

function User({ match }) {
  return <h1>Hello {match.params.username}!</h1>;
}

当使用这种方式时,router将根据你给定的要渲染的React组件调用React.createElement方法创建一个新的React组件(元素)。这就意味着,如果你提供的是一个内联函数(该函数返回一个React组件),那么在每次渲染时,都会创建一个新的组件(产生不必要的重复渲染),即每一次路径匹配时,已渲染的组件都会卸载再重新挂载,而不是更新已渲染的组件。所以,如果非要用内联函数的形式,可以采用render prop。

(2)传入render内联函数:

采用这种方式传入内联函数,将不会出现以上分析中的重复渲染的问题。例如:

// convenient inline rendering
<Route path="/home" render={() => <div>Home</div>}/>

// wrapping/composing
const FadingRoute = ({ component: Component, ...rest }) => (
	<Route {...rest} render={props => (
		<FadeIn>
			<Component {...props}/>
		</FadeIn>
	)}/>
)

<FadingRoute path="/cool" component={Something}/>

(3)传入children内联函数:
当你想要在在路径匹配或不匹配时渲染不同的效果就可以采用这种方式:

<ul>
	<ListItemLink to="/somewhere" />
	<ListItemLink to="/somewhere-else" />
</ul>;

const ListItemLink = ({ to, ...rest }) => (
	<Route
		path={to}
		children={({ match }) => (
			<li className={match ? "active" : ""}>
				<Link to={to} {...rest} />
			</li>
		)}
	/>
);

这在动画中也同样有用:


<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>
)}/>

在以上三种方式中,match, location, history这三个参数都会传入构造React组件的函数中。

Route组件还支持以下props属性:

path(string | string[]):
任何可以被path-to-regexp解析的有效URL路径。例如:

<Route path="/users/:id" component={User} />或者是<Route path={["/users/:id", "/profile/:id"]} component={User} />

注意:没有设置path属性的Route组件将总是被匹配。

exact(boolean):
设置为true时,要求路径必须完全匹配。例如:

<Route exact path="/one" component={About} /> 将只会在路径为/one或/one/匹配,/one/two是无法匹配的(如果设置为false就可以)。

strict(boolean):
对路径末尾斜杠的匹配。当设置为true时,如果path设置的路径字符串中有末尾斜杠,那么匹配的路径也必须有末尾斜杠(更加严格),例如:

<Route strict path="/one/" component={About} /> 将无法匹配/one,但是可以匹配/one/two。

如果要确保没有末尾斜杠的精确匹配,可以通过设置exact和strict属性均为true来完成。例如:

<Route exact strict path="/one" component={About} />将只会匹配路径/one。

sensitive(boolean):
若设置为true,路径匹配要求大小写一致(默认为false)。例如:

<Route sensitive path="/one" component={About} />将会匹配/one,但是无法匹配/One, ……。

3.Switch组件:

Switch组件只能用于包裹Route组件和Redirect组件,而且只会匹配第一个匹配路径的子组件。

Route组件用它们的path属性匹配,Redirect组件用它们的from属性进行匹配。

import { Switch, Route } from 'react-router'

<Switch>
	<Route exact path="/" component={Home}/>
	<Route path="/about" component={About}/>
	<Route path="/:user" component={User}/>
	<Route component={NoMatch}/>
	<Redirect from="/accounts" to="/users" />
</Switch>

<Route path="/about" component={About}/>
<Route path="/:user" component={User}/>
<Route component={NoMatch}/>

的区别在于:第二种情况中,如果是url路径是/about,那么About,User,NoMatch组件都将会被渲染(因为均匹配规则)。而在第一种使用Switch组件的情况中,只会渲染About组件。

也就是说,Switch组件会唯一的渲染其子组件中第一个匹配路径的组件。

至于为什么不将从上到下排列的Route组件设计为只匹配第一个匹配的,是为了在侧边栏或者面包屑导航中使用这种多匹配多渲染的模式。

这在动画过渡中同样有用:

<Fade>
	<Switch>
		{/* there will only ever be one child here */}
		<Route/>
		<Route/>
	</Switch>
</Fade>

<Fade>
	<Route/>
	<Route/>
	{/* there will always be two children here,
	  one might render null though, making transitions
	  a bit more cumbersome to work out */}
</Fade>

4.Redirect组件

Redirect组件渲染时将导航到一个新地址,而这个新地址会覆盖history栈中本来要访问的那个地址(说白了就是重定向)。

import { Route, Redirect } from 'react-router'

<Route exact path="/" render={() => (
	loggedIn ? (
		<Redirect to="/dashboard"/>
		) : (
		<PublicHomePage/>
	)
)}/>
	

Redirect组件还有如下属性:

to(string | object):
to字段可以是字符串,也可以是object。如果是字符串,则表示重定向的URL字符串;如果是对象,则表示重定向的location对象。例如:

<Redirect to="/somewhere/else" />

以及

<Redirect
	to={{
		pathname: "/login",
		search: "?utm=your+face",
		state: { referrer: currentLocation }
	}}
/>

其中的referrer键的值,可以在即将重定向到的组件(比如”/login”路径对应的Login组件)中,通过this.props.location.state.referrer获取到。

push(boolean):
重定向本来的行为是替换当前history栈中的路径,但是如果设置push为true,那么它将不会覆盖原路径,而是生成一个新路径,然后推入history栈中。

<Redirect push to="/somewhere/else" />

from(string):
需要被匹配的将要被重定向路径。

exact(boolean)

strict(boolean)

5.Prompt组件

用于在路由跳转前提示用户(可以用于阻止跳转)。

import { Prompt } from 'react-router'

<Prompt
	when={formIsHalfFilledOut}
	message="Are you sure you want to leave?"
/>

当when属性设置的值为true时,会在跳出当前路径时,弹出Prompt提示框。

message属性既可以是字符串,也可以是函数,例如:

<Prompt
	message={location =>
	location.pathname.startsWith("/app")
		? true
		: `Are you sure you want to go to ${location.pathname}?`
	}
/>

6.history属性

history对象是react-router库的两大主要依赖之一(另一个是React)。在js环境中,通过不同的实现来管理session history。

有以下三个经常被使用的术语:

  1. browser history:history在DOM上的实现,用于支持HTML5 history API的浏览器
  2. hash history:history在DOM上的实现,用于支持旧的浏览器
  3. memory history:history在内存中的实现,用于测试或者是非DOM环境,比如RN

history对象有以下属性和方法:

  1. length(number):浏览器history栈中历史记录的数目
  2. action(string):路由跳转到当前页面执行的动作(PUSH, REPLACE, POP)
  3. location(object):当前所在路径的location对象

    • pathname(string):当前页面的路径
    • search(string):当前URL的查询字符串
    • hash(string):当前URL的hash字符串
    • state(object):状态对象中保存路由参数,比如在其他页面通过this.props.history.push(path, state)路由到当前页面,state对象可以被获取到
  4. push(path, [state])(function):向history栈中推入一个新的路径的方式,实现跳转,同时还可以传递路由参数。
  5. replace(path, [state])(function):、用一个新的路径替换history栈中的当前路径的方式,实现跳转,同时还可以传递路由参数。
  6. go(n)(function):向前移动history栈中指向当前路径实体的指针n步
  7. goBack()(function):等同于go(-1)
  8. goForward()(function):等同于go(1)
  9. block(prompt)(function):阻止跳转

history对象是可变的,所以总是建议在Route的props中获取location对象,而不是通过history对象获取,例如:

class Comp extends React.Component {
	componentDidUpdate(prevProps) {
		// will be true
		const locationChanged = this.props.location !== prevProps.location;

		// INCORRECT, will *always* be false because history is mutable.
		const locationChanged = this.props.history.location !== prevProps.history.location;
	}
}

<Route component={Comp} />;

7.location属性

location对象表示页面当前所处的位置。

以下四种情况中,获取location对象的方式:

  • Route Component中:通过this.props.location获取
  • Route render中:通过({ location }) => ()获取
  • Route children中:通过({ location }) => ()获取
  • withRouter函数包装的组件中,通过this.props.location获取

你也可以在history对象中通过history.location的方式获取,但是并不建议,因为history对象是变化的。

在以下场景中可以用location对象替换跳转的字符串形式的地址:

  • Link组件中的to属性
  • Redirect组件中的to属性
  • history.push(to, state)
  • history.replace(to, state)

例如:

// usually all you need
<Link to="/somewhere"/>

// but you can use a location instead
const location = {
	pathname: '/somewhere',
	state: { fromDashboard: true }
}

<Link to={location}/>
<Redirect to={location}/>
history.push(location)
history.replace(location)

最终,你还可以传递location对象作为Route组件和Switch组件的属性。这会阻止它们使用实际的location对象在router的状态中(没遇到过这种使用场景,所以不是很理解这种用法)。

8.match属性

当路由路径和当前路径成功匹配,会生成一个对象match。match对象有更多关于URL和path的信息。这些信息可以通过它的属性获取,如下所示:

  • params(object):返回一个对象包含Path-to-RegExp包从URL解析的键值对。
  • isExact (boolean):如果整个URL匹配(不包含尾随字符),则为 true
  • path(string):用于匹配路径模式。用来创建嵌套的Route组件
  • url(string):返回URL中匹配部分的字符串。用于创建嵌套的Link组件很有用

其中path属性和url属性很容易混淆,通过以下例子区分:
假设有路径”users/5″匹配”users/:id”,那么匹配成功后得到的match对象中,path值就是”users/:id”,url就是”users/5″。

以下四种情况中,获取match对象的方式:

  • Route Component中:通过this.props.match获取
  • Route render中:通过({ match }) => ()获取
  • Route children中:通过({ match }) => ()获取
  • withRouter函数包装的组件中,通过this.props.match获取

9.withRouter方法

对于不是在Route组件或者是Redirect组件中注册的路由视图组件,是无法获取到这三个路由参数的:history,location以及match。

如果想在其他组件中获取到这三个参数,可以把对应的React组件传入withRouter方法,例如:

import React from "react";
import PropTypes from "prop-types";
import { withRouter } from "react-router";

// A simple component that shows the pathname of the current location
class ShowTheLocation extends React.Component {
	static propTypes = {
		match: PropTypes.object.isRequired,
		location: PropTypes.object.isRequired,
		history: PropTypes.object.isRequired
	};

	render() {
		const { match, location, history } = this.props;

		return <div>You are now at {location.pathname}</div>;
	}
}

// Create a new component that is "connected" (to borrow redux
// terminology) to the router.
const ShowTheLocationWithRouter = withRouter(ShowTheLocation);

也可以采用ES7中装饰器的形式。在对应的类组件上,@withRouter。

10.generatePath方法

generatePath方法可以接受两个参数,第一个是路径匹配的模式,第二是路径匹配的具体参数,之后会以字符串的形式生成完整的路径。例如:

import { generatePath } from "react-router";

generatePath("/user/:id/:entity(posts|comments)", { id: 1, entity: "posts" });
// Will return /user/1/posts

一旦提供的参数不匹配,则会抛出错误,例如:

generatePath("/user/:id/:entity(posts|comments)", { id: 1 });
// TypeError: Expected "entity" to be defined

二. react-router-dom

react-router-dom库中实际上有部分内容是与react-router库重复的,不过它还单独定义一些其他内容,接下来将介绍:3个组件(BrowserRouter, Link, NavLink)。

1.BrowserRouter组件

BrowserRouter组件是使用HTML history api的Router组件。例如:

import { BrowserRouter } from 'react-router-dom'

<BrowserRouter
	basename={optionalString}
	forceRefresh={optionalBool}
	getUserConfirmation={optionalFunc}
	keyLength={optionalNumber}>
	<App />
</BrowserRouter>

可以看到它拥有四个属性:

basename(string):
为所有的location添加一个基准URL。例如:

<BrowserRouter basename="/calendar"/>
<Link to="/today"/> // renders <a href="/calendar/today">
	

getUserConfirmation(func):
一个用来确认路由跳转的函数,默认采用window.confirm。

// this is the default behavior
const getConfirmation = (message, callback) => {
	const allowTransition = window.confirm(message)
	callback(allowTransition)
}

<BrowserRouter getUserConfirmation={getConfirmation}/>

forceRefresh(boolean):
若设置为true,在路由时将会刷新整个页面。你应该最好在浏览器不支持HTML5的history api时使用:

const supportsHistory = 'pushState' in window.history
<BrowserRouter forceRefresh={!supportsHistory}/>

keyLength(number):
设置location.key的长度,默认为6。

location.key唯一标识了路径的location对象,不知道是什么的,可以点击这里

2.Link组件

为你的应用提供声明式,无障碍的导航。可以理解为一个表示链接的a标签,只不过点击这个链接会发生页面路由,而不是跳转到一个外部链接。

import { Link } from 'react-router-dom'

<Link to="/about">About</Link>

to属性可以是string类型的url路径,也可以是location对象,这里不再赘述。

还有以下属性:

replace(boolean):
若设置为true,那么点击该链接跳转路由时,会替代当前history栈的地址实体,而不是push。

innerRef(function或RefObj):
获取要跳转组件的引用。

const refCallback = node => {
  // `node` refers to the mounted DOM element or null when unmounted
}

<Link to="/" innerRef={refCallback} />

const anchorRef = React.createRef()

<Link to="/" innerRef={anchorRef} />

Link组件还可以传递任何能作为a标签属性的props,例如className,title等。

3.NavLink组件

Link组件的进一步封装,可以设置链接的激活颜色等样式。

activeClassName(string):
导航选中激活时候应用的样式名,默认样式名为active。例如:

<NavLink to="/faq" activeClassName="selected">
  FAQs
</NavLink>

activeStyle(object):

<NavLink
	to="/faq"
	activeStyle={{
		fontWeight: "bold",
		color: "red"
	}}>
	FAQs
</NavLink>

exact(boolean):
若为 true,只有当跳转地址严格匹配时,才会应用链接激活样式。

<NavLink exact to="/profile">
	Profile
</NavLink>

static(boolean):
若为 true,只有当访问地址后缀斜杠严格匹配(有或无)时激活样式才会应用。

<NavLink strict to="/events/">
	Events
</NavLink>

isActive(function):
决定导航是否激活(显示激活样式),或者在导航激活时候做点别的事情。不管怎样,它不能决定对应页面是否可以渲染。

// only consider an event active if its event id is an odd number
const oddEvent = (match, location) => {
	if (!match) {
		return false
	}
	const eventID = parseInt(match.params.eventID)
	return !isNaN(eventID) && eventID % 2 === 1
}

<NavLink
	to="/events/123"
	isActive={oddEvent}>Event 123</NavLink>

发表评论

电子邮件地址不会被公开。 必填项已用*标注