v3 与 v4 版本对比
- 静态与动态路由
相对于v2,v3版本,v4一个最大的变化是路由从静态改为了动态,就是说用v4开发不需要专门再写一个类似route.js的文件来专门存放路由与组件的对应的关系,用v4直接写在Route组件的path上就行。此外v4分成了好几个库:
react-router React Router 核心
react-router-dom 用于 DOM 绑定的 React Router
react-router-native 用于 React Native 的 React Router
react-router-redux React Router 和 Redux 的集成
react-router-config 静态路由配置的小助手
做浏览器端应用只需引入react-router-dom即可,做RN的话需要引入react-router-native,同时如果使用了redux,还必须引入react-router-redux - 特点
v3 的特点:
1、路由配置集中在一个地方
2、布局和页面嵌套是由<Route>组件的嵌套派生而来的
v4 的特点:
1、不提倡将路由配置集中,而是分散化
2、Route直接参与布局和页面嵌套
3、Route本身就是组件
4、Route 代替 props.children 实现嵌套 - V4的新特性
1、包容路由(Inclusive Routing)
<Route path="/" component={HomePage} />
<Route path="/users" component={UsersPage} />
当访问/users,HomePage 和 UserPage 都会被渲染
2、独占路由(Exclusive Routing)
可以使用switch组件来实现v3式的独占路由,为了避免包容路由的规则,仍需使用exact
const PrimaryLayout = () => (
<div className="primary-layout">
<PrimaryHeader />
<main>
<Switch>
<Route path="/" exact component={HomePage} /> // 使用exact来精准匹配
<Route path="/users/add" component={UserAddPage} /> // /users/add 放在 /users前面就可以避免对/users使用exact
<Route path="/users" component={UsersPage} /> // /users 放在 /users/add后面就可以避免对/users使用exact
<Redirect to="/" /> // 都不匹配时,走 redirect
</Switch>
</main>
</div>
)
0、重要概念
- Route
Route就是一个组件,当路径匹配时就渲染,否则渲染为null;
Route的path参数可以设正则,例如 path='/(index|home|)' 可以匹配/index或/home或/; 再如 path='/list/:number'可匹配/list/1,注意number不能改其他名字
Route对应的组件可以从props中获取 match , location , history 三个参数:
// 取参数写法:
cons { dispatch, match:{ params:{ number } } } = this.props
// match 参数
params - (object) Key/value pairs parsed from the URL corresponding to the dynamic segments of the path
isExact - true if the entire URL was matched (no trailing characters)
path - (string) The path pattern used to match. Useful for building nested <Route>s
url - (string) The matched portion of the URL. Useful for building nested <Link>s
// location 参数(Link的to参数可以是location对象)
const location = {
pathname: '/somewhere'
state: { fromDashboard: true }
}
<Link to={location}/>
{
key: 'ac3df4', // not with HashHistory!
pathname: '/somewhere'
search: '?some=search-string',
hash: '#howdy',
state: {
[userDefined]: true
}
}
// history 参数
`length` - (number) The number of entries in the history stack
`action` - (string) The current action (`PUSH`, `REPLACE`, or `POP`)
`location` - (object) The current location. May have the following properties:(这个location和上面那个不一样)
`pathname` - (string) The path of the URL
`search` - (string) The URL query string
`hash` - (string) The URL hash fragment
`state` - (string) location-specific state that was provided to e.g. `push(path, state)` when this location was pushed onto the
stack. Only available in browser and memory history.
`push(path, [state])` - (function) Pushes a new entry onto the history stack
`replace(path, [state])` - (function) Replaces the current entry on the history stack
`go(n)` - (function) Moves the pointer in the history stack by `n` entries
`goBack()` - (function) Equivalent to `go(-1)`
`goForward()` - (function) Equivalent to `go(1)`
`block(prompt)` - (function) Prevents navigation (see [the history docs](https://github.com/ReactTraining/history#blocking-transitions))
- Link
可设置to和replace属性 - NavLink
特殊的Link,当激活时会自动加上active类名 - Prompt
页面离开时的弹出提示框 - Redirect
可设置 from 和 to 属性 ,当 Link 用即可
1、基本使用
import React, { Component } from 'react'
import {
BrowserRouter as Router,
Route,
Link
} from 'react-router-dom'
const Home = () => (
<div>
<h2>Home</h2>
</div>
)
const About = () => (
<div>
<h2>About</h2>
</div>
)
const Topic = ({ match }) => ( // 函数式组件,每个组件会被传入match、location 和 history,用的最多的是match
<div>
<h3>{match.params.topicId}</h3>
</div>
)
const Topics = ({ match }) => (
<div>
<h2>Topics</h2>
<ul>
<li>
<Link to={`${match.url}/rendering`}>
Rendering with React
</Link>
</li>
<li>
<Link to={`${match.url}/components`}>
Components
</Link>
</li>
<li>
<Link to={`${match.url}/props-v-state`}>
Props v. State
</Link>
</li>
</ul>
<Route path={`${match.url}/:topicId`} component={Topic}/>
<Route exact path={match.url} render={() => (
<h3>Please select a topic.</h3>
)}/>
</div>
)
class App extends Component {
render() {
return (
<Router>
<div>
<ul>
<li><Link to="/">Home</Link></li>
<li><Link to="/about">About</Link></li>
<li><Link to="/topics">Topics</Link></li>
</ul>
<hr/>
<Route exact path="/" component={Home}/> //exact表示精准匹配,/about不会渲染Home
<Route path="/about" component={About}/>
<Route path="/topics" component={Topics}/>
</div>
</Router>
)
}
}
export default App
2、与redux集成
// 先安依赖:npm install --save react-router-redux@next npm install --save history
import React from 'react'
import ReactDOM from 'react-dom'
import { Route } from 'react-router'
import { createStore, combineReducers, applyMiddleware } from 'redux'
import { Provider } from 'react-redux'
import { ConnectedRouter, routerReducer, routerMiddleware, push } from 'react-router-redux'
import createHistory from 'history/createBrowserHistory'
import reducers from './reducers' // Or wherever you keep your reducers
// Create a history of your choosing (we're using a browser history in this case)
const history = createHistory()
// Build the middleware for intercepting and dispatching navigation actions
const middleware = routerMiddleware(history)
// Add the reducer to your store on the `router` key
// Also apply our middleware for navigating
const store = createStore(
combineReducers({
...reducers,
router: routerReducer
}),
applyMiddleware(middleware)
)
// Now you can dispatch navigation actions from anywhere!
// store.dispatch(push('/foo'))
ReactDOM.render(
<Provider store={store}>
{ /* ConnectedRouter will use the store from Provider automatically */ }
<ConnectedRouter history={history}>
<div>
<Route exact path="/" component={Home}/>
<Route path="/about" component={About}/>
<Route path="/topics" component={Topics}/>
</div>
</ConnectedRouter>
</Provider>,
document.getElementById('root')
)
3、V4 的使用技巧
- 嵌套
子路由中用this.props.match.path来获取父路由的匹配规则
<Switch>
<Route path={props.match.path} exact component={BrowseUsersPage} />
<Route path={`${props.match.path}/:userId`} component={UserProfilePage} />
</Switch>
- 指定参数类型(/:userId(\d+))
<Route path={`/users/:userId(\\d+)`} component={UserProfilePage} /> // /users/edit 不会匹配,/users/123才会匹配
- <Link> & <NavLink>
功能一样,只是navlink组件可更加方便定制激活时的样式 - match & history 常用属性
match.params.XX
match.path // 用于嵌套 Routes
match.url // 用于嵌套 Links
history.push - URL Query Strings
v4里不能直接获取类似hash或者query string这样的参数,可以使用这个三方库 query-string
// 1、取查询字符串
const parsed = queryString.parse(location.search); // location.search='?foo=bar'
console.log(parsed); // {foo: 'bar'}
// 2、取hash
const parsedHash = queryString.parse(location.hash); // location.hash = '#token=bada55cafe'
console.log(parsedHash); // {token: 'bada55cafe'}
// 3、制作location.search
parsed.foo = 'unicorn';
parsed.ilike = 'pizza';
const stringified = queryString.stringify(parsed); // 'foo=unicorn&ilike=pizza'
location.search = stringified;
// note that `location.search` automatically prepends a question mark
console.log(location.search); // '?foo=unicorn&ilike=pizza'
- 布局上
当应用里有登录这种逻辑时,最好将登录和主应用分离,将其作为两个顶级路由
class App extends React.Component {
render() {
return (
<Provider store={store}>
<BrowserRouter>
<Switch>
<Route path="/auth" component={UnauthorizedLayout} />
<AuthorizedRoute path="/app" component={PrimaryLayout} />
</Switch>
</BrowserRouter>
</Provider>
)
}
}
class AuthorizedRoute extends React.Component {
componentWillMount() {
getLoggedUser()
}
render() {
const { component: Component, pending, logged, ...rest } = this.props
return (
<Route {...rest} render={props => {
if (pending) return <div>Loading...</div>
return logged
? <Component {...this.props} />
: <Redirect to="/auth/login" />
}} />
)
}
}
const stateToProps = ({ loggedUserState }) => ({
pending: loggedUserState.pending,
logged: loggedUserState.logged
})
export default connect(stateToProps)(AuthorizedRoute)
- 组件命名
顶级组件以 Layout结尾,页面级组件以 Page 结尾 - Route的三种渲染方式
component // 本质调用React.createElement 创建Component
render // 适用于要封装高阶组件的情形,如封装一个<FadingRoute>
// wrapping/composing
const FadingRoute = ({ component: Component, ...rest }) => (
<Route {...rest} render={props => (
<FadeIn>
<Component {...props}/>
</FadeIn>
)}/>
)
<FadingRoute path="/cool" component={Something}/>
children // 不管路径是否匹配,总是渲染。只是当路径不匹配时,match为null
<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>
)}/>
)
参考
1、v4中文文档
2、初探 React Router 4.0
3、React Router 4 简介及其背后的路由哲学
4、All About React Router 4