react router 级联路由_react-router源码分析

1 react-router

基于 history 创建的 history 对象、以及 create-react-context 为上下游组件传递 context 数据,上游的 Router 组件用于监听地址栏的变更,随后将 history 对象以及当前的 state.location 信息写入 context;下游的 Route 组件通过工具函数 matchPath 判断 location.pathname 是否匹配 props.path,以此渲染出内容。

在这种机制下,存在两个问题:如果 location.pathname 与多个 Route 匹配时,react-router 将会渲染出这几个 Route 的内容;如果嵌套使用的 Route 元素均需要与 location.pathname 当前的地址栏信息加以比较的话,那么这个 Route 元素的 props.path 属性也需要感知父 Route 元素的 props.path 信息。针对第一个问题,react-router 提供了 Switch 组件。借助 Switch 组件,react-router 将只渲染首个匹配的 Route 元素,且为子组件注入 props.computedMatch 属性(以避免 matchPath 的多次调用)。针对第二个问题,react-router 通过 Switch 组件或父 Route 组件向子组件注入 props.match 属性,再通过 match.path 设置子 Route 的 props.path 属性(参见 basic 示例)。

除了改变子组件的 props.match 属性外,Switch 和 Route 组件均能改变子组件的 props.location 属性。react-router 以 Animated Transitions 示例 指出 props.location 属性可应用于动效渲染场景。在示例中,react-router 使用指定 location 属性的 Switch 组件包裹前后两个页面级渲染内容,再经由指定 key 键为 location.key 的 CSSTransition 组件包裹(指定 key 键是为了在 CSSTransition 组件重绘期间,使该组件一前一后、卸载再挂载的机制切合视图上的移入移出动效),而后统一由 TransitionGroup 组件管理 CSSTransition 组件的动效切换过程。这样就可以实现:在地址栏变更过程中,前一个 CSSTransition 组件同步驻留,执行完隐藏动效后再移出;后一个 CSSTransition 组件执行完显示动效后再移入。

顺带指出的是,因为 Route 或 Switch 组件都可以指定动态的 props.location 属性,那就可以设想如下的黑魔法:自定义的、且与历史堆栈无甚关联的动态 location 属性将主导子 Route 的渲染状态。当然,这一黑魔法并不规范,理应避免使用。

在 Router 之外,react-router 还提供 MemoryRouter, StaticRouter 组件。MemoryRouter 组件可根据虚拟的缓存历史堆栈控制子 Route 的渲染状态,适用于测试或 native 环境。StaticRouter 组件适用于服务端渲染:react-router 会根据地址栏渲染出指定的内容,因此可以在 node 端设定 controller 对不同的前端路由统一发送 html 内容(通过 ReactDOMServer.renderToString 获得指定路由的 html 内容);但是当地址栏与前端路由不匹配时,我们需要跳转到 404 页面,react-router 就会通过 StaticRouter 组件的机制在 ReactDOMServer.renderToString 方法执行期间,使用 content 引用对象收集待 404 页面地址,然后在 node 端进行重定向。引用对象 content 收集数据的方式有两种:第一种在虚拟跳转页面环节(为此,react-router 为 StaticRouter 组件提供了特定的 push, replace 方法实现);第二种在待渲染页面 render 期间对 content 属性进行赋值。当然,这两种收集方式都在 ReactDOMServer.renderToString 方法执行期间完成。以上机制,可参考官方的 Server Rendering 示例。

上一个段落的 ReactDOMServer.renderToString 方法执行期间,前端代码会虚拟地重定向到 404 页面;node 端再通过 context 感知到这一虚拟重定向过程,然后再发起真实的重定向。在这个过程中,虚拟重定向是由 Redirect 组件完成的。在 react-router4 中,Redirect 组件的机制就是在组件的生命周期中跳转页面、或者在渲染期间改变 context 的属性(服务端渲染时,将待跳转页面上报给 node 服务器),并无其他内容。因此,如果不需要重定向,就需要条件语句控制 Redirect 组件的渲染。参考 Redirects (Auth) 示例。

在 Redirect 组件之外,借助 history 库封装的 block 方法,Prompt 组件用于拦截地址栏的变更,如默认会使用 window.confirm 提醒用户是否要进行跳转。Prompt 组件按两种条件决定是否需要对用户进行提示,其一是 props.when 属性,当其为真值进入条件二(因此可以将 state 状态赋值给 props.when 以设定条件);其二是 props.message 属性,即 history 库中的 prompt,该值可以为函数,允许开发者根据待变更地址的 location, action 动态加以判断,是否需要提示用户。

我们用下图表示上文中组件的层级关系(虚线表示可有无可):

v2-7583c125cf63b5a04a669480440c10df_b.jpg

以下列表简要概括各组价的输出能力和特点:

  • StaticRouter: 服务器端渲染时,配合收集跳转页面地址并完成服务器端重定向。
  • MemoryRouter: native 环境使用虚拟历史堆栈实现前端路由。
  • Router: 将真实或虚拟的路由信息注入到子组件中,以控制 Route 内容的渲染。
  • Switch: 控制渲染首个匹配路由信息的 Route 内容。
  • Route: 设定单个路由规则,匹配时渲染内容。渲染内容可以是 children, Component, render 懒加载。其中,children 可以 React 元素,或 render props 形式。
  • Render: 作为 Route 下的渲染内容。
  • Redirect: 渲染时即重定向。
  • Prompt: 拦截地址栏变更。

1.1 __RouterContext

基于 create-react-context,__RouterContext 用于为上下游组件传递 context。

1.2 withRouter

withRouter 装饰器将构造 HOC 组件,用于将 context 内容注入到子组件的 props 中。并且,HOC 组件基于 hoist-non-react-statics,拷贝了原始组件的非 react 类静态属性或方法。

1.3 工具函数

  • Lifecycle: 生命周期方法管理组件。
  • generatePath(path, params): 基于 path-to-regexp,将路径规则 path 和路由参数 params 解析为实际的路径。实现上,根据路径规则的解析函数会以对象形式缓存在内存中。
  • matchPath(pathname, { path, exact, strict, sensitive }): 基于 path-to-regexp,将实际路径 pathname 解析为 { path, url, isExact, params } 形式。其中,path 为路径规则;url 为匹配的路径内容;isExact 指实际路径 path 和路径规则 pathname 是否相等;params 为路由参数。

2 react-router-config

react-router-config 输出如下两个工具函数:

  • matchRoutes(routes, pathname): 从类树形结构的路由中获取当前匹配的路由节点分叉。主要针对 react-router4 没有在全局层面缓存全量的路由配置信息,路由配置散落在 Route 组件中。
  • renderRoutes(routes, extraProps, switchProps): 使用 Switch, Route 组件渲染单层结构的数组路由 routes。

3 react-router-dom

基于 react-router 包,react-router-dom 针对浏览器环境提供了如下组件:

  • BrowserRouter: 使用 history 库输出的 createBrowserHistory,构建前端路由。
  • HashRouter: 使用 history 库输出的 createBrowserHistory, 构建 hash 路由。
  • Link: 基于 __RouterContext,绘制 a 标签跳转链接。在配置 props.onClick 方法的情景中,可以采用采用浏览器机制跳转。
  • NavLink: 根据路由匹配情况,为 Link 元素设置特殊的样式。

4 react-router-native

基于 react-router 包,react-router-dom 针对 native 环境提供了如下组件:

  • NativeRouter: 基于 MemoryRouter 组件,构建前端路由。默认的 props.getUserConfirmation 方法通过 react-native 输出的 Alert 实现。
  • BackButton: 基于 react-native 输出的 BackHandler 绑定 hardwareBackPress 事件,实现点击 back 按钮回退页面的功能。
  • Link: 通过为 props.Component 组件绑定 onPress 事件并渲染,实现页面跳转。
  • DeepLinking: 基于 react-native 输出的 Linking 绑定 url 事件,以使 Linking.openURL 调用过程中移除参数 url 中 '://' 及其前的内容。关于深度链接,可参看 浅析移动应用深度链接 (Deeplinking), Linking。

5 总结

本文的写作基于反向演绎,缺少遇到问题时正向推理的顺畅感,又介于笔者水平有限,文中难免谬误,仍望海涵。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值