单页面应用页面跳转,前进后退单靠页面切换实现起来很麻烦。于是就有了 React-Router :前段静态页面中的路由。它通过管理 URL,实现组件的切换和状态的变化。
React-Router 4 之后没有了集中路由的概念,所有的路由都是 react 的组件。后面在详细哦 React-Router 升级。
React-Router 4 之后将 react-router 拆分成两个库:react-router 和 react-router-dom
react-routerAPI
export MemoryRouter from './MemoryRouter'
export Prompt from './Prompt'
export Redirect from './Redirect'
export Route from './Route'
export Router from './Router'
export StaticRouter from './StaticRouter'
export Switch from './Switch'
export generatePath from './generatePath'
export matchPath from './matchPath'
export withRouter from './withRouter'react-router-domAPI
export BrowserRouter from './BrowserRouter'
export HashRouter from './HashRouter'
export Link from './Link'
export MemoryRouter from './MemoryRouter'
export NavLink from './NavLink'
export Prompt from './Prompt'
export Redirect from './Redirect'
export Route from './Route'
export Router from './Router'
export StaticRouter from './StaticRouter'
export Switch from './Switch'
export generatePath from './generatePath'
export matchPath from './matchPath'
export withRouter from './withRouter'这里看到有些导出的API是相同的,比如 MemoryRouter 。其实他们是一样的。 react-router-dom 是将 react-router 中的API重新在导出了一遍。
// Written in this round about way for babel-transform-imports
import { MemoryRouter } from 'react-router'
export default MemoryRouter另外 react-router-dom 新增了以下几个增强的API。所以我们在开发中调用的时候,所有的接口调用可以直接通过 react-router-dom 导入。
export BrowserRouter from './BrowserRouter'
export HashRouter from './HashRouter'
export Link from './Link'
export NavLink from './NavLink'我们先来讲讲根路由。一切发生都是从根开始。
class Router extends React.Component {}是不是很熟悉,对没错就是一个 react 组件。所以再次提醒自己 react-router 4 一切都是组件。 Router 是一个顶层组件,在组件的 context 中注入一个 router 属性,并订阅 history 的变化,然后精确匹配 / 路径。特别需要注意的是 Router 的子组件只能放置一个,如果想放置多个子组件的话,需要外层用一个容器包裹起来。下面的核心代码。
context注入router属性
// 提供子组件从`context`获取`router`
getChildContext() {
return {
router: {
...this.context.router,
history: this.props.history,
route: {
location: this.props.history.location, // 因为是根节点,location 直接从 history 获取
match: this.state.match
}
}
}
}
// 声明router类型
static childContextTypes = {
router: PropTypes.object.isRequired
}- 监听
history的改变
// 监听改变
this.unlisten = history.listen(() => {
this.setState({
match: this.computeMatch(history.location.pathname)
})
})
// 精确匹配
computeMatch(pathname) {
return {
path: '/',
url: '/',
params: {},
isExact: pathname === '/'
}
}- 精确匹配
/路径
state = {
match: this.computeMatch(this.props.history.location.pathname)
}
computeMatch(pathname) {
return {
path: '/',
url: '/',
params: {},
isExact: pathname === '/' //精确匹配
}
}class Route extends React.ComponentRoute 将 URL 地址和页面绑定的组件。也是一个正规的 react 组件。常规用法
Route 组件提供了以下属性
static propTypes = {
computedMatch: PropTypes.object, // private, from <Switch>
path: PropTypes.string, // 路径
exact: PropTypes.bool, // 是否精确匹配
strict: PropTypes.bool, // 是否严格模式
sensitive: PropTypes.bool,
component: PropTypes.func, // 组件
render: PropTypes.func, //
children: PropTypes.oneOfType([
PropTypes.func,
PropTypes.node
]), // 子组件 必须只能有一个
location: PropTypes.object // 地址
}Route组件的功能是path 和组件绑定(组件多种定义(优先级从左导右):component,render,children)。它的实现原理是将 path 与 history的location 或上层route的location进行匹配,匹配成功然后渲染组件。
Route 和Router最大的差别在于Route 重置了 context 中router属性。简单对比一下两个源码:
Router
getChildContext() {
return {
router: {
...this.context.router,
history: this.props.history,
route: {
location: this.props.history.location, //直接从history中获取location
match: this.state.match
}
}
}
}Route
getChildContext() {
return {
router: {
...this.context.router,
route: {
location: this.props.location || this.context.router.route.location,// 从上层route中获取location
match: this.state.match
}
}
}
}这就是为什么我们可以直接通过 this.context.router.route.location 获取当前所在的路径。