react-router学习笔记
author: @TiffanysBear
基本介绍
React Router
是完整的 React 路由解决方案
React Router
保持 UI 与 URL 同步。它拥有简单的 API 与强大的功能例如代码缓冲加载、动态路由匹配、以及建立正确的位置过渡处理。
基础部分
路由配置
index路由配置:添加首页,设置默认页面,使用 IndexRoute
1 | import { IndexRoute } from 'react-router' |
重定向 Redireact
通过 <Redirect>
中的 from 和 to 进行路由的重定向。
1 | import { Redirect } from 'react-router' |
进入和离开的 Hook
Route 可以定义 onEnter 和 onLeave 两个 hook ,这些hook会在页面跳转确认时触发一次。这些 hook 对于一些情况非常的有用,例如权限验证或者在路由跳转前将一些数据持久化保存起来。
在路由跳转过程中,onLeave hook 会在所有将离开的路由中触发,从最下层的子路由开始直到最外层父路由结束。然后onEnter hook会从最外层的父路由开始直到最下层子路由结束。
继续我们上面的例子,如果一个用户点击链接,从 /messages/5 跳转到 /about,下面是这些 hook 的执行顺序:
- /messages/:id 的 onLeave
- /inbox 的 onLeave
- /about 的 onEnter
路由配置方式
可以使用标签形式,也可以使用路由配置的方式进行:
1 | const routeConfig = [ |
路由匹配原理
如何看是否匹配一个 URL 呢?
- 嵌套关系:深度优先遍历整个路由配置
- 路径语法:相对路径的话,会根据嵌套关系,与自身路径进行拼接;绝对路径会忽略嵌套关系
- 优先级:路由算法会根据定义的顺序自顶向下匹配路由,要注意前一个路由不会被后一个路由匹配所忽略替换。
History
React Router 是建立在 history 上的,简而言之,一个 history 知道如何去监听浏览器地址栏的变化, 并解析这个 URL 转化为 location 对象, 然后 router 使用它匹配到路由,最后正确地渲染对应的组件。
常见的 history 有三种形式:
- browserHistory
- hashHistory
- createMemoryHistory
三者之间的区别:
browserHistory
Browser history 是使用 React Router 的应用推荐的 history。它使用浏览器中的 History API 用于处理 URL,创建一个像example.com/some/path这样真实的 URL 。
真实路由需要服务器也进行相应的配置。
hashHistory
Hash history 使用 URL 中的 hash(#)部分去创建形如 example.com/#/some/path 的路由。
Hash history 不需要服务器任何配置就能运行,但是不推荐在实际线上环境中使用。
像这样 ?_k=ckuvup 没用的在 URL 中是什么?(用来作为恢复 location state 的唯一 key 标识)
当一个 history 通过应用程序的 push 或 replace 跳转时,它可以在新的 location 中存储 “location state” 而不显示在 URL 中,这就像是在一个 HTML 中 post 的表单数据。
在 DOM API 中,这些 hash history 通过 window.location.hash = newHash 很简单地被用于跳转,且不用存储它们的location state。但我们想全部的 history 都能够使用location state,因此我们要为每一个 location 创建一个唯一的 key,并把它们的状态存储在 session storage 中。当访客点击“后退”和“前进”时,我们就会有一个机制去恢复这些 location state。
createMemoryHistory
Memory history 不会在地址栏被操作或读取。这就解释了我们是如何实现服务器渲染的。同时它也非常适合测试和其他的渲染环境(像 React Native )。
和另外两种history的一点不同是你必须创建它,这种方式便于测试。
默认路由 IndexRoute
1 | <Router> |
IndexLink
如果你在这个 app 中使用 Home , 它会一直处于激活状态,因为所有的 URL 的开头都是 / 。 这确实是个问题,因为我们仅仅希望在 Home 被渲染后,激活并链接到它。
如果需要在 Home 路由被渲染后才激活的指向 / 的链接,请使用
高级用法
动态路由
代码分拆,按需加载。
React Router 里的路径匹配以及组件加载都是异步完成的,不仅允许你延迟加载组件,并且可以延迟加载路由配置。在首次加载包中你只需要有一个路径定义,路由会自动解析剩下的路径。
配合webpack,根据路由拆分组件,按需加载。
跳转前确认
React Router 提供一个 routerWillLeave 生命周期钩子,这使得 React 组件可以拦截正在发生的跳转,或在离开 route 前提示用户。routerWillLeave 返回值有以下两种:
return false 取消此次跳转
return 返回提示信息,在离开 route 前提示用户进行确认。
你可以在 route 组件 中引入 Lifecycle mixin 来安装这个钩子。
1 | import { Lifecycle } from 'react-router' |
服务端渲染
服务端渲染与客户端渲染有些许不同,因为你需要:
发生错误时发送一个 500 的响应
需要重定向时发送一个 30x 的响应
在渲染之前获得数据 (用 router 帮你完成这点)
为了迎合这一需求,你要在
使用 match 在渲染之前根据 location 匹配 route
使用 RoutingContext 同步渲染 route 组件
它看起来像一个虚拟的 JavaScript 服务器:
1 | import { renderToString } from 'react-dom/server' |
至于加载数据,你可以用 renderProps 去构建任何你想要的形式——例如在 route 组件中添加一个静态的 load 方法,或如在 route 中添加数据加载的方法——由你决定。
这块需要仔细了解一下具体的实现和原理。
组件生命周期
在路由切换期间,组件生命周期的变化。
https://react-guide.github.io/react-router-cn/docs/guides/advanced/ComponentLifecycle.html
所有的之前已经被挂载的组件,组件内部 props 参数的更新,走的是 componentWillReceiveProps
,所以只是从 router 更新了 props。
在组件外部使用导航
组件内部导航使用 this.context.router
,外部的使用 history 实现组件外部的导航。
1 | // your main file that renders a Router |
使用技巧
代码分割
通过 react-loadable,可以做到路由级别动态加载,或者更细粒度的模块级别动态加载:
1 | const AsyncHome = Loadable({ |
当然上面展示的是 ReactRouter 中的用法,AsyncHome 可以在任何 JSX 中引用,这样就提升到了模块级别的动态加载。
注意,无论是 webpack 的 Tree Shaking,还是动态加载,都只能以 Commonjs 的源码为分析目标,对 node_modules 中代码不起作用,所以 npm 包请先做好拆包。或者类似 antd 按照约定书写组件,并提供一种 webpack-loader 自动完成按需加载。
转场动画 - 路由切换时转场动画
通过 React Router Transition (Ant Motion 也很好用) 可以实现路由级别的动画:
1 | <Router> |
并提供了一些生命周期的回调,具体可以参考文档。现在动画的思路比较靠谱的也大致是这种:通过添加/移除 class 的方式,利用 css3 做动效。
滚动条复位
当页面回退时,将滚动条恢复到页面最顶部,可以让单页路由看起来更加正常。由于 React Router4.0 中,路由是一种组件,我们可以利用 componentDidUpdate 简单完成滚动条复位的功能:
1 | <Router history={history}> |
非通过 Route 渲染的组件,可以通过 withRouter 拿到路由信息,仅当其为 Router 的子元素时有效。
嵌套路由
React Router4.0 嵌套路由与 3.0 不同,是通过组件 Route 的嵌套实现的。
在任何组件,都可以使用如下代码实现嵌套路由:
1 | <Route path={`${this.props.match.url}/:id`} component={NestComponent} /> |
这样将路由功能切分到各个组件中,我现在的项目甚至已经没有 route.js 文件了,路由由 layout 与各个组件自身承担。这种设计思路与 Nestjs 的描述性路由具有相同的思想 - 在 nodejs 中,我们可以通过装饰器,在任意一个 Action 上描述其访问的 URL:
1 | @POST("/api/service") |
常见的使用和属性
: 渲染第一个被匹配到的路由 - withRouter : 为组件注入