前言
阅读源码是众多提升技术能力的一个途径,像看技术文档、博客文章、书籍等等都是提升技术能力的方式,而大型的源码往往是这些资料的集大成者,当具备相对完整的基础能力之后,再去广泛的阅读源码,能够从各个方面提升我们自己的能力,从而视野也会变得更广更深。
阅读源码的技巧
最好的阅读源码方式是看文章,如果源码的作者有写源码解读文章,这就是最省力的方式。虽然直接看代码可以了解到所有细节,但当你不清楚设计思路时,仅看源码可能会找不到方向,而读源码的最终目的是找到核心的设计理念,如果一个框架没有自己核心设计理念,这个框架也不值得诞生,更不值得被阅读。如果框架的作者已经将框架核心理念写成了文章,那读文章就是最佳方案。
还有一种方式是断点,写一个最小程序,在框架执行入口出打下断点,然后按照执行路径一步步理解。虽然执行路径中会存在大量无关的函数干扰精力,但如果你足够有耐心,当断点走完时一定会有所收获。
带着问题去阅读
当你想要阅读React.js
的源码,那么在阅读源码之前,先列出几个问题,遇到的时候就会格外关注,如:
- 虚拟
DOM
是怎么生成的? - 函数组件和
class
组件渲染有什么不同? - 虚拟
DOM
是否比直接操作原生DOM
要快? - 虚拟
DOM
如何转变成真实的DOM
? - 什么是
jsx
? render()
的原理是什么?hook
都有哪些,是如何工作的?diff
算法是什么?内部是如何判断哪些节点需要变更?React.js
中如何处理事件?fiber
是什么,React.js
为什么要采用这种结构?- 优先级策略是什么?它是怎么影响调度算法的?
- .....
不同类型源码的问题切入点
以下只列举几种我们工作中接触最多且使用最广泛的,不同细分领域的在这些类型中需要掌握的问题切入方式具有一定的共性,可以针对具体的需要去进行深入的阅读和学习。
基础框架类
例如:React.js
、Vue.js
、Svelte.js
、Preact.js
等
- 了解它们的特征,解决了什么问题?
- 例如
Preact.js
的理念是什么,它为什么强调自己"轻"?为什么需要"轻"? Svelte.js
是为了解决什么问题?React.js
的fiber
架构解决了什么问题?它里面哪些思想或者方法可以给我们的业务优化提供指导?Vue.js
的动静结合思想的妙处?- 当我们使用时出现了问题怎样快速定位到源码的具体位置?
- 这些优秀框架的设计模式是怎么用的?
- 数据结构和算法是怎么用的,同时要了解这些数据结构和算法是为了炫技还是真的运用的特别恰当,是否有可取之处?
- ......
如今我们自己去实现一个UI框架完全是有可能的,例如假如只需要做一个简单的表单填写收集的应用,那么我们可以考虑采用Vue.js
双向绑定的思想,开发一个纯粹的满足"双向绑定"的轻量级框架而不是直接使用Vue.js
这种"重量级"框架(这里抛去时间成本等因素考虑它的可行性)。
构建工具类
例如:Webpack
、Rollup
、esbuild
、Vite
、Turbopack
等
- 它们各自适用于什么场景?
- 它们能解决什么问题,不能解决什么问题?
esbuild
提速是怎么实现的?- 近几年的横空出世构建工具是怎么解决"快速"这一问题的?还解决了哪些问题?
- ......
如今我们实现一个简单的构建工具也是完全有可能的,假如我们只需要对JS资源作简单的资源和压缩,而不用考虑其它非JS资源的处理,那么完全可以利用Babel
的一些插件就可以实现这些功能(甚至不用),也是一个"轻量级"的构建工具。当你需要深度定制化构建工具时,这些工具的思想就派上用场了。
视图UI框架类
例如:Iview
、Ant Design
、elementUI
等
- 了解它们的设计架构
- 了解别人是怎么做
CSS
功能划分的,高级用法是怎么用的 - 了解像
Iview
中Modal
、Message
这样的组件怎么做到既可以在模版中使用,也可以函数式使用的 - 了解这些框架中是怎么封装
React
组件或者Vue
组件的,是不是可以借鉴一些思路 - 了解这些框架是怎么管理全局变量,管理工具方法的
- 了解组件设计是怎么运用设计模式的
- .....
如今一些基于业务的但第三方UI框架无法满足的公共组件越来越多的出现在我们的项目中了,我们是不是需要考虑借鉴一下这些框架,将自己的组件设计得灵活性更高,扩展行更强,健壮性更强,再融入一些设计理念,将很多个组件设计得复用性更高。
功能性工具类
例如:Jquery
、Lodash
、Moment
、Axios
等
- 了解工具库是怎样导出让我们使用的的?它们采取的导出方式有什么好处?
- 怎样实现同时导出适用于不同运行环境的方法?
- 功能是如何划分的?
- 设计模式?
- 它们引用了哪些第三方工具?(它们使用的工具往往是久经考验的,我们也可以用,这一点也适用于其它类型的源码)。
- 它们对外提供接口的健壮性是如何把控的?比如它对入参做了哪些限制,内部的容错是怎么处理的,返回结果是怎么处理的?
- 它们对外提供接口的友好性是如何把控的?比如传错了参数它是怎么给用户报错的,比如它给了什么样的提示,参数数量是怎么控制的?
- ....
如今我们要实现一些跨团队/跨业务线的通用型工具库已经非常普遍了,怎样实现通用性、扩展性、稳定性、健壮性、友好性等,应当作为我们自己实现工具库的参考标准。
一个完整的项目
例如:一些框架提供的examples(往往一些例子设计其实很精良)、公司内部的其它项目
- 这个项目是怎么做组件划分的?这样做的原因?优点和缺点?
- 为什么采用
menorepo
、multirepo
、或者其它?为什么不是XX方式? - 这个项目对第三方插件是如何引用的?
- 这个项目对内部工具是如何封装的?
- 这个项目用到了哪些设计模式?是否可以用到XX项目中去?
- 这个项目的整体设计存在哪些问题?应当如何规避?假如如果让你重构你会怎么入手?
通过阅读一些优秀项目(大型项目)的整体到局部的设计与考量,我们在做自己负责的业务时也会有更多的思路去选取更优的方案,而不是千篇一律的永远在复制自己过往使用的方法,应当始终在追求更好和甚至推翻自己过往的经验,才会使自己掌握的技术更深更广。
其它优秀的项目
这些项目不属于以上任何分类,它们可能是任何一个领域的工具或产品,像echarts
、antv
,这些类型的项目如果不是专业需要可以不进行深度学习,因为太过庞大深度阅读RIO相对较低,可以了解代码之外的东西,为我们开发项目提供思考或灵感。
- 这个庞大的项目是怎么做架构设计的?
- 这个工具解决了什么问题?它的优势?
- 它被应用广泛的独到之处?(这个也适用于以上类型)
- 它的周边?不限与源码的文档、教程设计等?
- .....
阅读源码的好处(小结)
通过带着技巧和问题,针对不同类型的源码阅读/学习,我们可以获得很多方面的好处和吸取很多方面的经验:
- 有助于理解抽象的概念,比如虚拟
DOM
、Diff
算法; - 有助于做方案调研,而不仅仅只看 Github star 数量;
- 看到一些陌生的工具函数,还可能激发你对 JS 规范的查阅,这种问题驱动的方式也是笔者推荐的 JS 规范学习方式。
- 能够提升我们对基础语言运用的认知,知道怎么用且用得好,用得精;
- 了解同样一个问题有可能有成千上万种解法,哪一种才是适用于当前项目的;
- 了解"权衡的艺术",有时候侧重于"轻",有时候则侧重于"重",这些思想分别诞生于什么场景,它们又是怎样为未来做铺垫的;
- 深度理解语言底层的能力及缺陷,例如为什么明明有更简单的方法这些设计者不用,是考虑性能,还是考虑兼容性,还是基于框架(或者项目)的整体思想而来的,亦或是其它;
- XX项目是怎么做工具整合的,还能发现一些神奇有用的工具;
- XX项目是怎么做性能优化的,优化策略的可借鉴之处;
- 甚至是开源项目的文档是怎么写的;
- 不胜枚举......
总结
本文通过阅读源码的技巧、不同类型源码阅读时应该提出的问题两个方面提供了这件事情的一些思考及实际意义。相信每个人在基于不同的能力、心态和目的的情况下,阅读源码所获得的感悟也不同。
众多基于技术(可行性)之外的考量与"权衡"无不贯穿在我们无数次"拧螺丝"或者"造火箭"的工作环节中,通过向优秀的、基于前人经验的、经过千千万万个开发者用户使用和迭代过的强大的开源项目的学习,可以让我们既能从技术层面以更深更广的视角了解"技术的艺术",也可以让我们吸收更多脱离技术本身对实用性、通用性、用户思维等方面的思考,与此同时,基于在职业生涯不同阶段自身能力的基础上,在学习源码时也会激发出更多超脱于技术本身的灵感。
如果我们只通过公司所做的项目(比较有限)来提升我们的能力,很容易陷入自我无限CV的循环,那样技术就很难有大的长进,也就是5年的经验都是头一年积累来的。深度阅读和学习一个优秀项目的代码甚至比花几个月写一个CURD的项目要学的东西多。
一千个人眼中有一千个哈姆雷特,源码也有一千种打开方式,你也可以带着一个具体的问题去阅读源码,比如你就好奇"React.js
是怎么用fiber
这个架构把运行时的性能提升到另一个台阶的",那就带着这个问题去了解fiber
,了解任务协调器和调度器之间配合的方式,再将这些思想应用到实际的项目中,那就达到学以致用的目的了。
总之,阅读源码和读书一样,可能它只有一小部分的方法能够真正被我们运用到实践,但那些读过的源码/文字都会成为自己的养分,在写代码的路上能够往更高更远的方向前进。