如何阅读大型前端开源项目的源码

简介:

目前网上有很多「XX源码分析」这样的文章,不过这些文章分析源码的范围有限,有时候讲的内容不是读者最关心的。同时我也注意到,源码是在不断更新的,文章里写的源码往往已经过时了。因为这些问题,很多同学都喜欢自己看源码,自己动手,丰衣足食。

这篇文章主要讲的是阅读大型的前端开源项目比如 React、Vue、Webpack、Babel 的源码时的一些技巧。目的是让大家在遇到需要阅读源码才能解决的问题时,可以更快的定位到自己想看的代码。授人以鱼不如授人以渔,希望大家可以通过这篇博客,了解到阅读大型前端项目源码时的切入点。在之后遇到好奇的问题时,可以自己去探索。

问题驱动——不要为了看源码而看源码

首先我们要明确一点,看源码的目的是什么?

我个人的意见是,看源码是为了解决问题。开源项目的源代码并没有什么非常特殊的地方,也都是普通的代码。这些代码的数量级一般都挺大,如果想是从源码中学到东西,直接浏览整个 Codebase 无疑是大海捞针。

但如果是带着问题去看源码,比如想了解一下 React 的合成事件系统的原理,想了解 React 的 setState 前后发生了什么,或者想了解 Webpack 插件系统的原理。也有可能是遇到了一个 bug,怀疑是框架/工具的问题。在这样的情况下,带着一个具体的目标去看源码,就会有的放矢。

看最新版的源码

之前看到一种说法,看源码要从项目的第一个 commit 开始看。如果是为了解决前文中对框架/工具产生的困惑,那自然要看当前项目中用到的框架/工具的版本。

如果是为了学习源码,我也建议看最新的源码。因为一个项目是在不断迭代和重构的。不同版本之间可能是一次完全的重写。比如 Vue 2.x 和 React 16。重构导致了代码架构上的一些变化,Vue 2.x 引入了 Vritual DOM,Pull + Push 的数据变化检测方式让整个代码的结构变的更清晰了,所以 2.x 的代码其实比 1.x 的更容易阅读。React 16 重写了 Reconciler,引入了 fiber 这个概念,整个代码仓库结构也更清晰,所以更推荐阅读。

前置条件

看源码怎么看,当然不能一把梭了。

看源码之前需要对项目的原理有一个基本的了解。所谓原理就是,这个项目有哪些组成部分,为了达到最终的产出,要经过哪几步流程。这些流程里,业界主流的方案有哪几种。

比如前端 View 层框架,要渲染出 UI,组件要经过 mount、 render 等等步骤。数据驱动的前端框架,在 mounted 之后,就会进入一个循环,当用户交互触发组件数据变化时,会更新 UI。其中数据的检测方式又有分 Push 和 Pull 两种方案。渲染 UI 可以是全量的字符串模板替换,也可以是基于 Virtual DOM 的差量 DOM 更新。

又比如前端的一些工具,Webpack 和 Babel 这些工具都是基于插件的。基本的工作流程就是读取文件,解析代码成 AST,调用插件去转换 AST,最后生成代码。要了解 Webpack 的原理,就要知道 Webpack 基于一个叫 tapable 的模块系统。

那我们要如何了解这些呢?要了解这些,可以去各大网站和博客上的《XXX源码解析》系列。通过这些文章,我们可以对我们要看的框架/工具的原理有一个大致的了解。

本地build

不过最终我们还是要直接看源码。笔者真正看源码的第一步就是把项目的代码仓库 clone 到本地。然后按项目 README 上的构建指南,在本地 build 一下。

如果是前端框架,我们可以在 HTML 中里直接引入本地 build 出的 umd bundle(记得用 development build,不然会把代码压缩,可读性差),然后写一个简单的 demo,demo 里引入本地的 build。如果是基于 Nodejs 的工具,我们可以用 npm link 把这个工具的命令 link 到本地。也可以直接看项目的 package.json 的入口文件,直接用 node 运行那个文件。

这里要强调一下,大型的开源项目一般都会有一个 Contribution Guide,目的是让想贡献代码的开发者更快上手。里面就有讲怎么在本地构建代码。

以 React 为例,React 的 Contributing Guide 里就 Development Workflow 这一节。里面有这么一段话:

The easiest way to try your changes is to run yarn build core,dom --type=UMD and then open fixtures/packaging/babel-standalone/dev.html. This file already uses react.development.js from the build folder so it will pick up your changes.

所以 React 仓库中的 fixtures/packaging/babel-standalone/dev.html 就是一个方便的 demo 页。我们可以在这个页面快速查看我们在本地对代码的改动。

你可以尝试着在项目的入口文件中加入一句 log,看看是不是可以在控制台/终端看到这句 log。如果可以的话,恭喜你,你现在可以随便把玩这个项目了!

理清目录结构

在看具体的代码之前,我们需要理清项目的目录结构,这样我们才能更快的知道在哪里地方找相关功能的代码。

我们看看 React 的目录结构。React 是一个 monorepo。也就是一个仓库里包含了多个子仓库。我们在 packages 目录下可以看到很多单独的 package:

在 React 16 之后,React 的代码分为 React Core,Renderer 和 Reconciler 三部分。这是因为 React 的设计让我们可以把负责映射数据到 UI 的 Reconciler 以及负责渲染 Vritual DOM 到各个终端的 Renderer 和 React Core 分开。React Core 包含了 React 的类定义和一些顶级 API。大部分的渲染和 View 层 diff 的逻辑都在 Reconciler 和 Renderer 中。

Babel 也是一个 monorepo。Babel 的核心代码是 babel-core 这个 package,Babel 开放了接口,让我们可以自定义 Visitor,在AST转换时被调用。所以 Babel 的仓库中还包括了很多插件,真正实现语法转换的其实是这些插件,而不是 babel-core 本身。

Vuejs 的代码比较典型,核心代码在 src 目录下,按功能模块划分。因为 Vue 也支持多平台渲染,所以把平台相关的代码都放到了 platform 文件夹下,core 文件夹中是 Vue 的核心代码,compiler 是 Vue 的模板编译器,把 HTML 风格的模板编译为 render function。

Webpack 和 Babel 一样,可以说都是基于插件的系统。Webpack 的主要源码在 lib 目录下,里面的 webpack.js 就是入口文件。

上面说了四个项目的目录结构,那我们遇到一个新的开源项目,应该怎么了解它的目录结构呢?

如果这个项目是一个 monorepo,首先我们要找到核心的那个 package,然后看里面的代码。

不是 monorepo 的话,一般来说,如果这个项目是一个 CLI 的工具,那 bin 目录下放的就是命令行界面相关的入口文件,lib 或者 src 下面就是工具的核心代码。如果这个项目是一个前端 View 层框架,那目录结构就和 Vue 类似。

作为验证,大家可以看一下打包工具 parcel 和前端 View 层库 moon 的目录结构。目录结构这个东西往往是大同小异,多看几个项目就熟悉了。

debugger && 全局搜索大法

运行了本地的 build,了解了目录结构,接下来我们就可以开始看源码了!之前说了,我们要以问题驱动,下面我就以 React 调用 setState 前后发生了什么这个问题作为例子。

我们可以在 setState 的地方打一个断点。首先我们要找到 setState 在什么地方。这个时候之前的准备工作就派上用处了。我们知道 React 的共有 API 在 react 这个 package 下面。我们就在那个 package 里面全局搜索。我们发现这个 API 定义在 src/ReactBaseClasses.js 这个文件里。

于是我们就在这里打一个断点:

Component.prototype.setState = function(partialState, callback) {
 invariant(
 typeof partialState === 'object' ||
 typeof partialState === 'function' ||
 partialState == null,
 'setState(...): takes an object of state variables to update or a ' +
 'function which returns an object of state variables.',
 );
 debugger;
 this.updater.enqueueSetState(this, partialState, callback, 'setState');
};
复制代码

然后运行本地 React build 的 demo 页面,让组件触发 setState,我们就可以在 Devtool 里看到断点了。

我们走进 this.updater.enqueueSetState 这个调用,就来到了 ReactFiberClassComponent 这个函数中的 enqueueSetState,这里调用了 enqueueUpdate 和 scheduleWork 两个函数,如果要深入 setState 之后的流程,我们只需要再点击

走进这两个函数里看具体的代码就可以了。

如果想看 setState 之前发生了什么,我们只需要看 Devtool 右边的调用栈:

点击每一个 frame 就可以跳到对应的函数中,并且恢复当时的上下文。

结合一步一步的代码调试,我们可以看到框架的函数调用栈。对于每个重要的函数,我们可以在仓库里搜索到源码,进一步研究。

Node 工具的调试方法也是相似的,我们可以在运行 node 命令时加上 --inspect 参数。具体可以看 Debugging Node.js with Chrome DevTools 这篇博客。

其实大家都知道单步调试这种办法,但在哪里打断点才是最关键的。我们在熟悉框架的原理之后,就可以在框架的关键链路上打断点,比如前端 View 层框架的声明周期钩子和 render 方法,Node 工具的插件函数,这些代码都是框架运行的必经之地,是不错的切入点。

如果是为了了解一个特定的问题,大家可以直接在自己觉得有问题的地方打断点。然后把源码运行起来,想办法让代码运行到那个地方。我们在断点可以看到局部变量等等信息,有助于定位问题。

来自开发团队的资源

其实开源项目的开发团队也都致力于让更多的人参与到项目中来,降低项目的门槛。所以我们在线上其实可以找到很多来自开发团队的资源。这些资源可以帮助我们去理解项目的原理。

关注核心开发者

每个项目都有一些核心开发者,比如 React 的 Dan Abramov, Andrew ClarkSebastian Markbåge。Webpack 的 Tobias KoppersSean Larkin。Vue 的 Evan You。我们可以在 Twitter 上关注他们,了解项目的动态。

关注官方博客和演讲视频

如果我们关注了上面的核心开发者,就会发现他们时常会发布一些和源码/项目原理有关的博客或者视频。

React 的官方博客最近就有很多和项目开发有关的博客。

Andrew Clark 一开始就写了一篇介绍 fiber 架构的文档。 Dan Abramov 最近在 JSConf 上对 React 未来的一些新特性的介绍 - Beyond React 16。React 博客中的 Sneak Peek: Beyond React 16 也是对这次 Talk 的介绍。

Evan You 介绍前端框架数据变化侦测原理的 Talk。Vue 文档中也有 Reactivity in Depth 这样的介绍原理的章节。

Sean Larkin 的 Everything is a plugin! Mastering webpack from the inside out 介绍了 Webpack 的核心组件 Tapable。

James Kyle 的 How to Build a Compiler 可以让我们了解 Babel 转译代码的基本流程。

写在最后

本文最核心的观点就是,看源码的目的是为了解决问题。我们鼓励大家在本地把大型项目的源码跑起来,自己随意把玩,研究。因为源码也是普通的代码,并没有太多门槛。唯一的门槛可能就来源于开源项目作者和普通开发者之间的信息不对称,普通开发者对项目的原理和目录结构不够了解。

我们可以从开发者那里获取资源,同时也可以多阅读社区里的源码分析文章,这些都有助于我们理解项目的原理,为后续的源码分析打下基础。


作者:蚂蚁金服数据体验技术
链接:https://juejin.im/post/5afe3735518825426539afce
来源:掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
相关文章
|
7天前
|
人工智能 前端开发 小程序
2024年12月30日蜻蜓蜻蜓AI工具系统v1.0.0发布-优雅草科技本产品前端源代码已对外开源可免费商用-优雅草老八
2024年12月30日蜻蜓蜻蜓AI工具系统v1.0.0发布-优雅草科技本产品前端源代码已对外开源可免费商用-优雅草老八
2024年12月30日蜻蜓蜻蜓AI工具系统v1.0.0发布-优雅草科技本产品前端源代码已对外开源可免费商用-优雅草老八
|
7天前
|
开发框架 小程序 前端开发
圈子社交app前端+后端源码,uniapp社交兴趣圈子开发,框架php圈子小程序安装搭建
本文介绍了圈子社交APP的源码获取、分析与定制,PHP实现的圈子框架设计及代码编写,以及圈子小程序的安装搭建。涵盖环境配置、数据库设计、前后端开发与接口对接等内容,确保平台的安全性、性能和功能完整性。通过详细指导,帮助开发者快速搭建稳定可靠的圈子社交平台。
72 17
|
2月前
|
监控 前端开发 数据可视化
3D架构图软件 iCraft Editor 正式发布 @icraft/player-react 前端组件, 轻松嵌入3D架构图到您的项目,实现数字孪生
@icraft/player-react 是 iCraft Editor 推出的 React 组件库,旨在简化3D数字孪生场景的前端集成。它支持零配置快速接入、自定义插件、丰富的事件和方法、动画控制及实时数据接入,帮助开发者轻松实现3D场景与React项目的无缝融合。
181 8
3D架构图软件 iCraft Editor 正式发布 @icraft/player-react 前端组件, 轻松嵌入3D架构图到您的项目,实现数字孪生
|
19天前
|
机器学习/深度学习 前端开发 算法
婚恋交友系统平台 相亲交友平台系统 婚恋交友系统APP 婚恋系统源码 婚恋交友平台开发流程 婚恋交友系统架构设计 婚恋交友系统前端/后端开发 婚恋交友系统匹配推荐算法优化
婚恋交友系统平台通过线上互动帮助单身男女找到合适伴侣,提供用户注册、个人资料填写、匹配推荐、实时聊天、社区互动等功能。开发流程包括需求分析、技术选型、系统架构设计、功能实现、测试优化和上线运维。匹配推荐算法优化是核心,通过用户行为数据分析和机器学习提高匹配准确性。
60 3
|
3月前
|
JavaScript 前端开发 Docker
前端全栈之路Deno篇(二):几行代码打包后接近100M?别慌,带你掌握Deno2.0的安装到项目构建全流程、剖析构建物并了解其好处
在使用 Deno 构建项目时,生成的可执行文件体积较大,通常接近 100 MB,而 Node.js 构建的项目体积则要小得多。这是由于 Deno 包含了完整的 V8 引擎和运行时,使其能够在目标设备上独立运行,无需额外安装依赖。尽管体积较大,但 Deno 提供了更好的安全性和部署便利性。通过裁剪功能、使用压缩工具等方法,可以优化可执行文件的体积。
172 3
前端全栈之路Deno篇(二):几行代码打包后接近100M?别慌,带你掌握Deno2.0的安装到项目构建全流程、剖析构建物并了解其好处
|
2月前
|
前端开发 测试技术
前端工程化的分支策略要如何与项目的具体情况相结合?
前端工程化的分支策略要紧密结合项目的实际情况,以实现高效的开发、稳定的版本控制和顺利的发布流程。
31 1
|
2月前
|
前端开发 JavaScript 开发者
超实用开源前端商城模板,助力电商项目飞速启航!免费直接可用!
分享一款精心设计的开源前端商城模板,涵盖商品展示、购物车、订单处理、用户登录注册等核心功能,使用HTML、CSS、JS和jQuery构建,结构清晰,适合新手和资深开发者,助力电商项目快速启动。
106 0
超实用开源前端商城模板,助力电商项目飞速启航!免费直接可用!
|
2月前
|
前端开发 Unix 测试技术
揭秘!前端大牛们如何高效管理项目,确保按时交付高质量作品!
【10月更文挑战第30天】前端开发项目涉及从需求分析到最终交付的多个环节。本文解答了如何制定合理项目计划、提高团队协作效率、确保代码质量和应对项目风险等问题,帮助你学习前端大牛们的项目管理技巧,确保按时交付高质量的作品。
45 2
|
3月前
|
缓存 前端开发 JavaScript
前端架构思考:代码复用带来的隐形耦合,可能让大模型造轮子是更好的选择-从 CDN 依赖包被删导致个站打不开到数年前因11 行代码导致上千项目崩溃谈谈npm黑洞 - 统计下你的项目有多少个依赖吧!
最近,我的个人网站因免费CDN上的Vue.js包路径变更导致无法访问,引发了我对前端依赖管理的深刻反思。文章探讨了NPM依赖陷阱、开源库所有权与维护压力、NPM生态问题,并提出减少不必要的依赖、重视模块设计等建议,以提升前端项目的稳定性和可控性。通过“left_pad”事件及个人经历,强调了依赖管理的重要性和让大模型代替人造轮子的潜在收益
|
3月前
|
前端开发 JavaScript 开发工具
前端代码规范和质量是确保项目可维护性、可读性和可扩展性的关键(三)
前端代码规范和质量是确保项目可维护性、可读性和可扩展性的关键(三)
47 0