Happy New Year!
一、更新
[2019-4-21]
Changed
二、前言
给自己拜个早年, 祝自己在2019年:
早上7点起来, 朦朦胧胧, 坐在电脑桌前, 一脸懵逼…
想了想, 今天还是继续之前的源码学习吧, 上一次学习了Switch
组件, 今天主要是来看一下Link
.
三、细说
Link
组件被放置在packages
下的react-router-dom
包中, 与react-router
做了隔离,
PS: 虽然不知道作者这么做的意图, 但是佩服就对了
但这些都是次要的, 打开Link.js
文件, 预览一下大致的结构, 可以看到, 源码行数与Switch
并无两样, 仔细看, Link
类中, 其实只有一个handleClick
处理方法. 所以, 按理说, 并不应该很难理解:
正式开始分析之前, 先来回想一下使用思路? 平时用到最多的可能就是Link
了, 一般都是做Menu
点击跳转之类的, 与之功能类似的有一个history.push
, 两者功能差不多. 与Link
对应的还有一个NavLink
, 可以自定义active
样式, 这个下一篇再说吧.
PS: Link
点击跳转, url
改变, 对应模块
展现, 一气呵成.
带着这个想法, 开始阅读源码:
首先, 可以看到:
1 2 3 4 5
| return ( <RouterContext.Consumer> {...} <RouterContext.Consumer> );
|
还是熟悉的做法, 接收context
做内部操作.
接着:
1 2 3 4 5
| const location = typeof to === "string" ? createLocation(to, null, null, context.location) : to; const href = location ? context.history.createHref(location) : "";
|
分为两步
- 计算location
- 根据location计算href
根据props
传递的to
参数, 如果是string
, 则调用history.push
直接跳转, 如果是object
, 则使用history.createHref
进行包装, 并返回相应的path
.
接着, 再往下看:
1 2 3 4 5 6 7
| return ( <a ... onClick={event => this.handleClick(event, context.history)} ... > );
|
Link
的核心处理函数, vscode
中Ctrl+MouseLeft
直接进入handleClick
函数内部, 可以看到:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| handleClick(event, history) { if (this.props.onClick) this.props.onClick(event);
if ( !event.defaultPrevented && event.button === 0 && (!this.props.target || this.props.target === "_self") && !isModifiedEvent(event) ) { event.preventDefault();
const method = this.props.replace ? history.replace : history.push;
method(this.props.to); } }
|
这是源码所示, 下面将相关注解直接挂载到对应源码上:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| handleClick(event, history) { if (this.props.onClick) this.props.onClick(event);
if ( !event.defaultPrevented && event.button === 0 && (!this.props.target || this.props.target === "_self") && !isModifiedEvent(event) ) { event.preventDefault();
const method = this.props.replace ? history.replace : history.push;
method(this.props.to); } }
|
执行完这一步操作, 基本上Link
的核心已经学习完毕了, 感觉很简单, 这也许是整个react-router-dom
体系中最简单的一个(哦, 还有下一篇将要分析的NavLink
).
PS: 多说不做非君子
下面, 接着来完善自己的yyg-react-router-dom
库.
四、实践
这里只写个大概, 完整源码参考: 这里
4.1 定义interface
PS: 非常重要的一步, 本能反应
打开react-router
的官网, 可以看到Link
的所需props, 🆗, 现在来定义它:
1 2 3 4 5 6 7 8 9
| export interface ILinkProps { to?: string | LocationDescriptorObject<{ [key: string]: any, }>; replace?: boolean; innerRef?: (node: React.Ref<HTMLLinkElement>) => void; children?: React.ReactChildren; };
|
接着, 按照以前的分布策略, 将处理context
的函数提取出来, 再分别进行不同的处理操作, 大致结构是这样的:
1 2 3 4 5 6 7 8 9 10 11 12
| function handleProcess( context: IStaticRouterComponentParams, ): JSX.Element { }
<RouterContext.Consumer> { (context) => handleProcess(context as IStaticRouterComponentParams) } <RouterContext.Consumer>
|
这里只是再三提醒自己注意代码规范, 俗话说:
PS: 好记性不如烂笔头
做的多了, 自然而然就熟练了.
接着, 在handleProcess
函数中, 要进行的则是计算href
, 以及处理a
的onClick
, 所以按照之前的思路, 根据props.to
的值进行location
的计算, 得到createHref
的返回值, 这个返回值就是path
, 所以:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| ... const href = ( to ? _isString(to) ? context.history.createHref(createLocation(to, null, '', context.location)) : context.history.createHref(to as LocationDescriptorObject<{ [key: string]: any, }>) : '' ) as string;
return ( <a {...others} href={href} onClick={(e) => handleLinkClick(e, context, href, replace)} >{children}</a> );
...
|
计算完href
, 就要处理click
了, 将其提取至两一个function, 并传入所需的参数:
1 2 3 4 5 6 7 8
| function handleLinkClick( e: React.MouseEvent<HTMLAnchorElement>, context: IStaticRouteComponentParams, href: string, replace?: boolean, ): void { }
|
最后, 做整合, 优化一下代码, 开始测试环节
五、测试
简单的写完自己的代码, 简单起见, 对to
和replace
这两个常用参数作测试:
在App.tsx
中, 修改测试路由:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| return ( <div> <BrowserRouter> <Switch> <Route path={"/test"} render={() => ( <Switch> <Route exact path="/test" component={One} /> <Route exact path="/test/home" component={Three} /> <Route exact path="/test/product" component={Four} /> </Switch> )} /> </Switch> </BrowserRouter> </div> );
|
进入到One.tsx
中, 添加一些内容, 当然是Link
组件了:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| return ( <div> <h1>Page One</h1> <ul> <li> <Link to="/test/home" >Home</Link> </li> <li> <Link to={{ pathname: '/test/product', state: { id: '19980808', name: 'duan', }, }} >Product</Link> </li> </ul> </div> );
|
测试代码并不是固定的, 自己随意测试即可…
之后, 打开CentBrowser
, 可以看到:
通过点击使用Link
组件, 浏览器url可以正常改变, 并且对应的组件可以正常渲染, 充分证明我们自己封装的Link没有问题.
六、源码
完整源码已上传至 Gayhub
七、总结
最后, 以一张脑图来结束今天的学习: