前天学习了Link组件源码, 今天再来看一下与之相似的NavLink
一、更新
[2019-4-21]
Changed
- 改进文章排版👌
二、前言
经过上一篇文章Link的战火洗礼, 可以了解到, Link作为沟通react-router和react之间的桥梁, 经历click -> url变化 -> history(onListen) -> Switch -> Route render的过程, 据此, 有关Link的面试题便可迎刃而解.
正式开始分析之前, 先来回想一下我们平时是如何使用NavLink的?
PS: 传入对应的
activeStyle或者activeClass, 点击之后, 如果对应的组件成功渲染, 则该NavLink的样式也会改变.
带着平时使用的思路去分析源码, 会又事半功倍的效果.
OK, 正式开始对NavLink的探索.
三、细说
先来看一下NavLink的大致结构, 可以看到, 其实NavLink是一个functional组件, functional相较于classes的优势很多:
- 高度可拓展
- 易于优化重构
- 渲染性能较优
在react v16.7之后的hooks加持之下, 可用性更是飞步提升.
接着来看一下NavLink接收了哪些props?
1 | function NavLink({ |
对应的props的功能如下表所示:
| aria-current | 残障人士专用, 可参考这里 |
| activeClassName | 匹配时添加的类名 |
| activeStyle | 匹配时添加的样式 |
| className | NavLink的类名, 会附加到Link |
| exact | 是否实行完全匹配 |
| strict | 是否实行严格匹配 |
| isActive | 自行计算高亮条件 |
| location | 与当前to进行比较的location, 默认是context.location |
| style | 默认样式 |
| to | 跳转的url |
| …rest | 额外参数 |
接着, 可以看到下面:
1 | const path = typeof to === "object" ? to.pathname : to; |
值得注意的是, 这里的excapedPath很奇怪, 一直想不通为什么要进行这个操作? 怀着好奇的心理点开注释的链接, 可以看到:

该函数会将字符串中的特殊字符进行转义处理, 那么这到底有咩用? 假设有一个特殊的path序列是这样的:
1 | const path: string = '/user\duan/profile/se+cr*et'; |
用户想传递该path, 该path中含有特殊字符, 那么escaped会将转义字符再此进行转义, 便于传递.
接着往下看:
1 | return ( |
通过一个Route组件包裹, 根据其的match判断是否active, 然后在Route中返回对应的Link组件, 将处理后的rest等参数传递给Link组件.
这一步, 我刚开始看了好几遍都没看懂, 很难理解, 难点在于为什么要用一个Route??? 它的本质是为了获取match, 根据match匹配成功与否, 进行动态添加样式, 理解了这点, 就没啥可说的了.
另外, 还有一种思路 —— 通过获取Link的innerRef, 然后通过matchPath自行比对location.pathname === to, 根据判断的结果, 直接操作DOM, 这么做也是可以的, 不过值得思考的是, 这不正好违背了Route组件的设计原则:
PS: 匹配路径规则, 渲染组件
既然有更专业的Route来帮我们作这件事, 何乐而不为呢? 为什么还要再次比对呢? 得不偿失.
🆗, NavLink的源码思路解析大致已经完成, 接下来继续完善自己的yyg-react-router-dom库.
四、实践
老样子, 首先定义我们所需的一切props, 具体的props可以看这里.
1 | // src/yyg-react-router-dom/components/NavLink.tsx |
接着, 到了下面, 根据判断match是否为null来断定path是否匹配:
1 | return ( |
注意! 上面有一个值得注意的点, 那就是Route的children和render是不同的, 两者的具体差别可以看这里, 所以, 这里只能使用children来渲染.
五、测试
完成了自己的yyg-react-router-dom库, 可以来做一个简单的测试.
PS: 当然, 测试用例是随意的
打开App.tsx, 修改render中的测试逻辑如下:
1 | //src/App.tsx |
然后, 在vscode的指引下, 进入One.tsx组件, 同样修改render中的逻辑:
1 | return ( |
可以看到, 总共有三个锚点, 第一个为Link, 为了与NavLink的效果加以区分, 然后在最下方放置需要router-view, 也就是需要渲染的组件. 大致结构与在项目中使用的差不多的…
接着, 在浏览器中可以看到效果:

点击Home按钮, 视图变了, 但是链接颜色没变化? 当然了, 因为这只是普通的Link组件, 你想它有啥效果? 接着看下面两个, prefect, 两个锚点样式随着url变化而改变.
此时, 对于NavLink的测试工作已经完美结束了
六、源码
源码已上传, 点这里
七、总结
PS: 路漫漫其修远兮, 吾必反复求其知
最后, 一张思维脑图来结束今天的源码学习
