最近拜读了react-router源码(对应文档,版本为v3.2.1),记录一下源码中个人认为非常棒的细节。
1. 入口文件
- components:功能性组件,会进行渲染;
- components(configuration):配置性组件,配合
Router组件使用,生成路由表 - utils:工具函数,配合路由组件解析
- histories:history模块的封装
从上面可以看出,react-router将功能划分非常明确,文件划分值得学习。
2. 功能入口
Router组件为react-router的功能入口,在componentWillMount>createTransitionManager中可以发现:react-router利用transitionManager对history模块进行了封装。
这么做有个大好处:Router本身不依赖当前环境,只对history模块的location变化做监听,将得到的location进行解析,进行后续的匹配。如此设计,Router只需对location这一个对象进行分析,不再关心地址何时变化,如何对变化进行监听。甚至,你可以自行传入一个自定义对象(history),在内存中模拟"地址"变化,将"地址"变化维护在栈中,实现"地址"切换的功能。
其实,react-router整体就两大块:Router和history。history告诉Router当前地址是多少,Router告诉React需要渲染哪些组件。
3. 细节记录
3.1 AsyncUtils
此文件中有两个函数:loopAsync,mapAsync。均为批量执行一系列任务,这些任务中可能存在异步任务。设计这种功能函数有两个关键点:
1.error first:错误优先。即执行回调函数时,第一个参数一定是错误信息;
2.end immediately:立即结束。允许调用者在某个任务中直接结束,进入所有任务结束回调。
3.2 Link
displayName:组件在调试时的名字。包含:react报错时提示的组件名,以及react-devtools调试面板展示的组件名。propTypes:组件接收属性的类型验证(可自定义类型)。当类型不对时,react会抛出警告。添加此属性后,IDE也会有智能提示。算是为弱类型的js提供一个健壮性方案。
3.3 isActive
deepEqual在比较数组内元素是否相同时,提前判断两个数组的长度,在数组长度不相同时,会提前停止判断,提高比较速度。
3.4 PatternUtils
compilePattern函数。解析一段规则会很耗时,使用CompiledPatternsCache将解析结果做了缓存,典型的用空间去换取时间。这里也提现了纯函数的好处:
相同输入永远得到同样的输出
也正因为如此,才能将耗性能的解析过程做缓存。
_compilePattern函数,真正的解析函数。
- 对正则表达式matcher的使用
- 通过路由配置,生成正则
regexpSource,降低开发者对正则的使用难度
上面对输入字符进行扫描,生成tokens的过程,让我想起了代码转换过程:从一个形态转换为另一个形态。
github上有一个开源项目the-super-tiny-compiler,用极简的解释了如何写一个代码编译器。只要步骤分为:
1.Parsing将原始代码转换为抽象的表示。
2.Transformation操作这种抽象的表示,做编译、转换目的。
3.Code Generation代码生成,根据新的抽象表示,转换为新的代码。
简述上述过程,即:【code】>【AST】>【code】。
以上是babel对代码降级的核心思路,也是从一种语言转换为另一种语言的核心思路。
【End】