react-router 从v2/v3迁移到v4(译文)
React Router v4 是完全重写的,所以并没有简单的建议迁移方式。这篇指南提供了一些步骤,从而帮助您了解如何升级您的系统。
注意: 这篇指南同时适用于React Router v2和v3,但是简单起见,示例中仅引用v3
react-router-redux的用户请注意: 并不是所有的包都兼容React Router v4,或者说不能完美兼容。具体的,Redux DevTools的time travel暂时还不可用.
The Router
在React Router v3中, 只有一个<Router>
组件。需要为它提供一个history
对象作为它的属性.
此外,你有两种方式配置路由表——使用属性routes
或者是直接写成<Router>
的children
。
// v3
import routes from './routes'
<Router history={browserHistory} routes={routes} />
// or
<Router history={browserHistory}>
<Route path='/' component={App}>
// ...
</Route>
</Router>
而在React Router v4中,一个巨大的变化就是,现在有多个不同作用的路由组件。它们中的任何一个都会为你创建一个history
对象。<BrowserRouter>
创建browser history,<HashRouter>
创建hash history,<MemoryRouter>
创建memory history.
在v4中,不再有路由中心配置。当页面需要根据不同路由渲染不同内容的时候,只需要渲染一个<Route>
组件。
//v4
<BrowserRouter>
<div>
<Route path='/about' component={About} />
<Route path='/contact' component={Contact} />
</div>
</BrowserRouter>
有一点需要注意的是,路由组件只能有一个子元素。
// yes
<BrowserRouter>
<div>
<Route path='/about' component={About} />
<Route path='/contact' component={Contact} />
</div>
</BrowserRouter>
// no
<BrowserRouter>
<Route path='/about' component={About} />
<Route path='/contact' component={Contact} />
</BrowserRouter>
Routes
在v3中,<Route>
并不是组件。系统中的所有<Route>
元素只是用于创建路由配置对象。
/// in v3 the element
<Route path='contact' component={Contact} />
// was equivalent to
{
path: 'contact',
component: Contact
}
在v4中,你可以像布局常规React应用一样,去布局应用中的组件。在任何地方,当需要根据不同地址(pathname
)来呈现不同内容的时候,你只需要渲染一个<Route>
。
v4的<Route>
是真正的组件,所以无论你在哪里渲染了<Route>
,它都会被渲染——当<Route>
的path
与当前地址相匹配时,就会根据赋予给它的属性(component
、render
和children
)渲染内容;而当<Route>
的path
与当前地址不匹配时,则会渲染为null
。
Nesting Routes
在v3中,嵌套的路由需要被设置成上层<Route>
的children
。
<Route path='parent' component={Parent}>
<Route path='child' component={Child} />
<Route path='other' component={Other} />
</Route>
当嵌套的路由匹配时,React元素的创建需要同时取决于父子两个<Route>
的component
属性。子元素会作为children
属性传递给父元素。
<Parent {...routeProps}>
<Child {...routeProps} />
</Parent>
在使用v4后,子路由的渲染应该仅由它的上层<Route>
组件决定。
<Route path='parent' component={Parent} />
const Parent = () => (
<div>
<Route path='child' component={Child} />
<Route path='other' component={Other} />
</div>
)
on*
properties
React Router v3提供了onEnter
,onUpdate
和onLeave
方法。这些方法本质上是重写(recreating)了React的生命周期方法。
在v4中,你应该使用<Route>
渲染的组件的生命周期方法.
- onEnter:componentDidMount、componentWillMount
- onUpdate:componentDidUpdate、componentWillUpdate(componentWillReceiveProps).
- onLeave:componentWillUnmount
<Switch>
在v3中,你可以指定若干个子路由,而只渲染第一个匹配到的。
// v3
<Route path='/' component={App}>
<IndexRoute component={Home} />
<Route path='about' component={About} />
<Route path='contact' component={Contact} />
</Route>
在v4中的<Switch>
组件提供了类似的功能,当<Switch>
被渲染时,只有第一个匹配了当前地址的子<Route>
才会被渲染。
// v4
const App = () => (
<Switch>
<Route exact path='/' component={Home} />
<Route path='/about' component={About} />
<Route path='/contact' component={Contact} />
</Switch>
)
<Redirect>
在v3中,加入你需要从一个路径重定向到另一个路径,例如从“/”到“/welcome”,你需要使用<IndexRedirect >
// v3
<Route path="/" component={App}>
<IndexRedirect to="/welcome" />
</Route>
在v4中,你可以使用<Redirect>
来达到同样的目的。
// v4
<Route exact path="/" render={() => <Redirect to="/welcome" component={App} />} />
<Switch>
<Route exact path="/" component={App} />
<Route path="/login" component={Login} />
<Redirect path="*" to="/" />
</Switch>
v3中,<Redirect>
会保留query string:
// v3
<Redirect from="/" to="/welcome" />
// /?source=google → /welcome?source=google
而在v4中,你必须将这些属性显示地重新设置一遍给to
:
// v4
<Redirect from="/" to="/welcome" />
// /?source=google → /welcome
<Redirect from="/" to={{ ...location, pathname: "/welcome" }} />
// /?source=google → /welcome?source=google
PatternUtils
matchPattern(pattern, pathname)
在v3中,你可以使用内部的用于判断是否相同的代码来检查路径是否匹配。在v4中,可以使用matchPath来完成检查工作,matchPath
基于path-to-regexp这个库。
formatPattern(pattern, params)
在v3中,你需要使用PatternUtils.formatPattern通过一个路径模式(一个常量或者是路由中心中的一个配置)和一个包含了若干参数的对象来生成一个有效路径。
// v3
const THING_PATH = '/thing/:id';
<Link to={PatternUtils.formatPattern(THING_PATH, {id: 1})}>A thing</Link>
在v4中,你可以使用path-to-regexp中的compile函数来获得相同的功能。
// v4
const THING_PATH = '/thing/:id';
const thingPath = pathToRegexp.compile(THING_PATH);
<Link to={thingPath({id: 1})}>A thing</Link>
Link
必须设置to
属性
在v3中,你可以忽略掉to
属性,或者是将它设置为null,用以创建一个没有href
属性的锚点
// v3
<Link to={disabled ? null : `/item/${id}`} className="item">
// item content
</Link>
在v4中,则必须为to
赋值,如果确实有需要置空to
,可以像下边这样做个简单的封装。
// v4
import { Link } from 'react-router-dom'
const LinkWrapper = (props) => {
const Component = props.to ? Link : 'a'
return (
<Component {...props}>
{ props.children }
</Component>
)
}
<LinkWrapper to={disabled ? null : `/item/${id}`} className="item">
// item content
</LinkWrapper>