antd 源码浅析

导读

github 上面有一些很 “哇塞” 的项目,在工作中呢,我们只是作为 “使用者” 的角色,去把这些开源项目应用在项目中。
看着官网提供的资料,了解了这是什么、这怎么安装、这怎么使用、这有什么东西……
这很常见,从使用者的角度上看,甚至社区上的项目写得好不好跟我们关系也不大。
“能用就用,不能用就换一个,反正有很多选择”,这是很多人使用者的态度。
这当然没问题。
不过作为研发人员,我们可以不止步于此,可以进一步深入了解他们的项目代码。
毕竟作者都开源了,肯定是希望社区上的用户能对他们的代码有更多的了解。
开源给了所有技术人员一个机会 —— 了解其他优秀开发者的项目机会。
遗憾的是大部分开发者都不具备去独立分析一个开源项目的方法。
看到优秀的项目代码,常常没办法搞懂整体设计、功能代码,想了解其实现也望而止步。

这是一个系列,以 Ant Design UI(react)为示例,一起学习如何分析一个开源项目。

通识

从 “编译层” 的维度看(排序:底层依赖 -> 高层实现),大概分成这几个领域:

  1. 机器码
  2. 编程语言
  3. 语言框架
  4. 代码项目
  5. 项目依赖
  6. 项目插件
  7. ……

从项目类型的维度(排序:从难到易)大致可以分为:

  1. 编程语言
  2. 语言框架
  3. 开源软件
  4. 框架 / 类库
  5. 框架 / 类库的插件
  6. 业务项目(demo)
  7. 文档资料

只是粗略的分类,或许你有比我更好的分层方式;而且也不是固定的,比如有些开源项目,本身就是一个语言框架,那他可能就根本没有依赖其他语言框架,所有没有 3 也是正常的。

编译层和项目类型可以为我们快速定位一个开源项目的信息,以及是否值得你去分析;
还有难易度,是否自己满足前置技能有分析其代码的能力。

比如 “文档资料” 是最没分析价值的,或许是文档的排版格式很漂亮,自己也想搞个很漂亮的文档页面;
那应该得去看这个文档是用什么东西生成的,关注点就应该转移到这个文档生成框架了(“开源软件 / 框架 / 类库”)。

虽然我们是以 Ant Design—— 一个 ts 语言、react 框架(这里提前泄漏信息了,在没开始看资料前,应该是不知道这些信息的)的 ui 库为示例。

比如 Spring Boot,是用 Java 写的,那么要去分析 Spring Boot
前置条件是熟悉 Java 语法,以及对相关领域(网络请求、http、api、数据库操作)有所概念。
Django 是用 Python 的写的,分析前也同样要对相关的编程语言、
以及相关领域(同 Spring Boot,因为它们都可以用于 web 开发)有所概念。
甚至是需要确切的用过 Spring BootDjango 开发过项目。
强烈推荐在分析开源代码前,本身得作为 “使用者” 使用过这些代码有实际的运用,哪怕是最简单的运用。

所以这是一篇通用性非常强的文章,基本上拿到任何源码项目,都可以按照此文的套路进行粗略分析。

初识

写过完全没用过 and 的读者,如果用 antd 开发过项目,可以选择跳过此章节。从初窥继续。

如果之前用过 Ant Design,也请忘记掉跟它相关的一切;
现在我们仅有的是的一个源码地址、官方网站而已。

先了解一下它是什么,能做什么,怎么安装,怎么使用。

浏览官网

先看官网,官网的首页一般都是一些 “正确的废话”,很多时候并不能得到有实际作用的信息;
最顶部的菜单:设计 文档 组件 资源 国内镜像 ……,这些有很大的信息量,先不看;
最底部的脚页:相关资源 社区 帮助 更多产品 四大板块,都是外部链接,也先不看;

首页有个很明显的 “开始使用按钮”,是专门写给使用者的,我们要第一个要看的是这个。

这个页面就有很多有效的信息量了,但是表述还是太过官网,个人理解可以用接地点的方法,稍微提取一些:

  1. React UI 组件库(是什么)
  2. 用来做中后台产品(能做什么)
  3. 开箱即用
  4. TypeScript 写的
  5. 多语言支持
  6. 支持主题定制
  7. 最少兼容 IE11
  8. 支持 ssr
  9. 也能被 Electron 使用
  10. 用 npm、yarn 安装 / 浏览器引入 / 按需加载 (什么安装)
  11. 【简单示例 - 见上图】(怎么使用)

那么 “它是什么,能做什么,怎么安装,怎么使用”,其实都知道了,这个时候也要知道使用 antd 的一些前置条件:

  1. React
  2. Typescript/JavaScritpt
  3. npm

这也是 antd 对使用者的前置技能要求,若是不了解,建议先学好前置技能。

链接、谁在使用、如何贡献、社区互助:次要信息,不需要关注。

上图右侧边栏是文档用于文章章节定位;
左侧是导航菜单,看完了此页面(Ant Design of React)就继续往下看;
把整个文档完整看一遍;
可以快速阅读,但不要跳过任何章节。

初用

或许在上一章的时候,你已经跟着文档搭建,启动了一个 antd 的项目;
文档提供了好几种方法(浏览器引入、create-react-app、create-react-app+typescript、umi)。
这里用官网提供的 create-react-app+typescript 方式搭建;
高级配置 暂时用不上,跳过。

一切顺利的话,我们会在 localhost:3000 看到一个蓝色的按钮。

项目提交链接

更多的信息

把关注点放到官网的 “最顶部的菜单”:设计 文档 组件 资源 国内镜像 ……
其实在初用那一章,我们就已经把 “文档” 模块给看完了;
设计 资源 国内镜像 ,这几个点进去一看,
没看到有关的使用信息,还是没必要看;
组件模块,才是 antd 最核心的教程;

把所有的组件都过一遍,试着在刚才的 demo 进行一些实例试用。

初窥

到目前为止,我们都是以 “使用者” 的角色去使用一个开源项目。
通过以上的使用经验,我们是时候开始接触源码层面了。

  • 红色:Code、Issuses、Pull requests —— 此代码项目的 “代码”、“问题讨论”、“推拉请求”。
  • 黄色:master 是当前的分支,分支信息(目前有 19 个分支),标签信息 (529 个);
  • 绿色:“关于”,项目介绍、官网链接、开源许可协议,start、watch、forks 等信息。
  • 蓝色:releases 信息,开源代码发布正式代码产生的信息。
  • 咖啡箭头:Octotree—— 一个浏览器插件,可树形结构显示 Github 项目。

当前查看的代码是默认分支(master,由仓库设置)的代码,想查看其他分支的代码,可以在 master 那个改变分支名。

在此页面滚动下来的 README.md,是根目录下的 README.md 文件,一般也是些文档资料;
关于文档资料,看中文还是英文的,个人建议是:原则是看原文的,即 —— 国内项目看中文,国外项目看英文。

获取源码

现在我们要 clone 源码,点击上图的绿色按钮 “Code”,有三种方式 Clone 代码,最通用的是使用 HTTPS 方式,红色箭头点击可直接复制 url。

不一定要 clone 源码到本地,有时候我们只是想简单看看源码的几个文件,并不需要全盘分析整个项目。有三个工具,都可以很方便查阅代码。

  1. Octotree—— 下载对应的浏览器插件,简单配置好 github auth token,即可在浏览器左侧使用
  2. github1s—— 直接在浏览器地址栏 github 后面加上 1s 即可:https://github1s.com/ant-design/ant-design/
  3. github dev—— 直接在浏览器地址栏.com 换成.dev 即可:https://github.dev/ant-design/ant-design/

clone 下来后,当前的分支 (branch) 是默认分支,建议是把 HEAD 切换到最新的 release 那份代码,这里的最新 release 是 4.21.7。也是此章节蓝框点进去的链接。
如果是业务项目出问题了,对项目依赖库进行问题排查,建议切换到项目实际使用的版本。

  • 绿:这次的发布是从 master 发布的
  • 红:此次发布的代码标签
  • 蓝:此次发布的代码提交 id

antd 的源码仓库 1G+,实在太大了,如果真的 clone 不来,可以去 release 页面,翻到最下面的 “Source Code” 选择压缩包下载;代价就是失去 git 管理的信息。

> $ git clone https://github.com/ant-design/ant-design.git
Cloning into 'ant-design'...
remote: Enumerating objects: 341830, done.
remote: Counting objects: 100% (892/892), done.
remote: Compressing objects: 100% (626/626), done.
remote: Total 341830 (delta 396), reused 546 (delta 191), pack-reused 340938
Receiving objects: 100% (341830/341830), 937.73 MiB | 3.88 MiB/s, done.
Resolving deltas: 100% (227846/227846), done.

> $ cd ant-design

> $ git reset --hard 10b153f

> $ git checkout -b v4.21.7

重置 HEAD 到最新 release 的 commit,并切出一条新的分支 v4.21.7

版本

软件工程领域中,版本的问题就跟软件名字一样重要,不同的小版本,也会有一些微小的区别,强烈建议版本要完全匹配;
由于官网一般用的是最新发布的文档,所以我们在上一步,获取的是最新 release 的版本,已经完成了匹配。

这里顺便看看 github 仓库的分支、tag 信息 ——Branches · ant-design/ant-design

根目录分析

现在开始正式分析源码,首先看项目根目录。
根目录有很多东西,这里按照一个比较通用的分类。
对根路径下的目录文件进行简单介绍,先不看文件 / 目录的具体内容。
文件 / 目录的命名,一般特定的文件都有约定俗成的名称。

# 项目工程

## git/github
.git
.github
.gitignore
.gitpod.yml
.husky

## docker
.dockerignore
Dockerfile.ui-test
docker-compose.yml

## 项目配置
.editorconfig
.npmignore
tsconfig.json
webpack.config.js
package.json
renovate.json
index-style-only.js
index-with-locales.js

## 代码格式
.eslintignore
.eslintrc.js
.prettierignore
.prettierrc
.stylelintrc.json

## 测试相关
.jest.image.js
.jest.js
.jest.node.js
.jest.site.js
jest-puppeteer.config.js

## 其他
.codesandbox
.depslintrc.js
.remarkrc.js

# 文档类
AUTHORS.txt
CHANGELOG.en-US.md
CHANGELOG.zh-CN.md
CNAME
CODE_OF_CONDUCT.md
LICENSE
README-ja_JP.md
README-pt_BR.md
README-sp_MX.md
README-uk_UA.md
README-zh_CN.md
SECURITY.md

# 代码入口文件
index.js

# 目录
components
docs
tests
typings
scripts
site

我们大概知道了根路径下的这些文件是什么东西,但是还有一小部分不知道,有些东西并不重要,可以忽略之;
比如以上的项目工程 ->git、github、docker、测试、代码格式相关,对于 antd 的实现没有太大的关系,不建议细看。

项目起点

根目录有两个很重要的文件 ——pckage.jsonindex.js,后者很有可能是整个项目的启动文件,但是我们目前还无法确定,现在就要来验证一下是否为项目的启动文件。

熟悉前端开发的小伙伴应该都明白 package.json 的重要性;
每个现代前端项目,都会有 package.json
里面的信息都是项目级别的元信息;
这是最重要的文件了,我愿称之为项目起点文件。

打开常用的编辑器,检阅大致内容,关注以下配置;

  • bin 命令行相关,引用的文件会被添加到 PATH。
  • scripts 定义了一组可以运行的 node 脚本。
  • dependencies 设置了作为依赖安装的 npm 软件包的列表。
  • devDependencies 设置了作为开发依赖安装的 npm 软件包的列表。

package.json 指南

scripts 中常用 startdev、server 等关键字来启动项目,重点关注这几个字眼;
dependenciesdevDependencies 可以知晓项目的一级依赖,大
概知道其技术栈;

其实当我们看到 package.json 就应该下意识去安装依赖。

> $ npm i

# 省略掉npm的输出信息

> antd@4.21.7 prepare
> husky install

husky - Git hooks installed

added 3655 packages in 7m
 

根目录会出现两个新玩意:

  1. package-lock.json—— 此项目的依赖的所有 npm 包,亦理解为描述 node_modules 的目录树文件(比 package.jsondependencies 更强大,可以在这个文件查看所有、层级依赖)
  2. node_modules—— 项目依赖包

启动项目

环境信息:

  • 操作系统 —— macOs v12.4
  • node —— v14.19.3
  • npm —— v8.12.1

package.json 中的第 93 行,”start” 中调用了 antd-tools 这个东西,但是我们并不知道这个东西是从哪来的。

{
  ...
  "script": {
    "start": "antd-tools run clean && cross-env NODE_ENV=development concurrently \"bisheng start -c ./site/bisheng.config.js\"",
  }
  ...
}

node 的可执行文件,都会放在 node_modules/.bin 下面,所有我们要查看这个目录。

> $ ls node_modules/.bin
## 太多东西了,省略掉输出信息

> $ ls -l node_modules/.bin | grep 'antd-tools'
antd-tools -> ../@ant-design/tools/bin/antd-tools.js
antd-tools-run -> ../@ant-design/tools/bin/antd-tools-run.js
# node_modules/.bin/antd-tools是一个软链接,指向 node_modules/@ant-design/tools/bin/antd-tools.js

> $ cat node_modules/@ant-design/tools/package.json
{
  "name": "@ant-design/tools",
  ...
  "homepage": "http://github.com/ant-design/antd-tools",
  ...
  "bin": {
    "antd-tools": "./bin/antd-tools.js",
    "antd-tools-run": "./bin/antd-tools-run.js"
  }
  ...
}

通过 node_modules/@ant-design/tools/package.json,我们得知了:

点开”homepage”,就是能看到 @ant-design/tools 的源码了;如果去分析其代码实现,我们的关注点就变了,所以这里建议看完 README.md 等一些文档资料马上关闭掉这个网页。

package 的 script=>start 配置很长,我们需要分成几个部分看看:

  1. antd-tools run clean
  2. cross-env NODE_ENV=development
  3. concurrently
  4. bisheng start -c ./site/bisheng.config.js

还是用刚才的方法,从 node_modules/.bin 查看命令行由哪个依赖包提供,查看依赖包的 package.json,从 homepage 找到一些资料,关于这个依赖包的说明。

当然,知道了包名(node_modes 下的目录都是包名)也可以直接去 npm 官网查找这个包的资料。
node_moudles/@ant-design/tools/ => https://www.npmjs.com/package/@ant-design/tools

> $ ll node_modules/.bin | grep -E 'antd-tools | cross-env | concurrently | bisheng' 

antd-tools -> ../@ant-design/tools/bin/antd-tools.js
bisheng -> ../bisheng/bin/bisheng
concurrently -> ../concurrently/dist/bin/concurrently.js
cross-env -> ../cross-env/src/bin/cross-env.js

结论是:

  1. antd-tools —— 专门为 antd 开发的 cli 工具
  2. bisheng —— 将 Mardown 转换成用 React 构建的静态网站
  3. concurrently —— 同时运行多个命令
  4. cross-env —— 跨平台设置环境变量

所以一条简单的 npm start,至少使用了 4 个依赖包……
那就先启动项目吧。

Antd 官网的贡献指南 - 开发流程章节也有对重要 script 命令简单介绍。

> $ npm start

> antd@4.21.7 prestart
> npm run version


> antd@4.21.7 version
> node ./scripts/generate-version


> antd@4.21.7 start
> antd-tools run clean && cross-env NODE_ENV=development concurrently "bisheng start -c ./site/bisheng.config.js"

antd-tools run clean
[0] ℹ Compiling 🚚  Bisheng
[0] ℹ 「wds」: Project is running at http://localhost:8001/
[0] ℹ 「wds」: webpack output is served from undefined
[0] ℹ 「wds」: Content not from webpack is served from /Users/wu/Documents/code/ant-design/_site
[0] ℹ 「wds」: 404s will fallback to /index.html
[0] ✔ 🚚  Bisheng: Compiled successfully in 55.77s
[0]  DONE  Compiled successfully in 55766ms11:51:08 AM
[0] 
[0]  WAIT  Compiling...11:51:09 AM
[0] 
[0] ℹ Compiling 🚚  Bisheng
[0] ✔ 🚚  Bisheng: Compiled successfully in 801.56ms
[0]  DONE  Compiled successfully in 804ms11:51:11 AM
[0] 

以上的输出信息都比较重要,建议自行翻译理解。

两次 “DONE” 之后,浏览器上 http://localhost:8001 / 就能看到启动的官网。
所以 npm start 其实是启动了一个官网项目的网站。
这个官网由 bisheng(毕升)驱动的,bisheng 的指定了配置文件。
site/bisheng.config.jsnpm start 使用了这个配置文件;
我们也应该打开看看,35~41 行source 的配置;
得去 bisheng 的 REAMD.md 看看有没有关于配置文件 source 的资料;
source 是存放 Markdown 文件的配置,官网提供的是字符串例子;
可是 antd 却使用了对象方式去配置,遗憾的是没找到对象配置的例子。
不再研究 bisheng 的配置,继续看 bisheng 的配置文件;

source: {
  components: './components',
  docs: './docs',
  changelog: ['CHANGELOG.zh-CN.md', 'CHANGELOG.en-US.md'],
  'components/form/v3': ['components/form/v3.zh-CN.md', 'components/form/v3.en-US.md'],
  'docs/resources': ['./docs/resources.zh-CN.md', './docs/resources.en-US.md'],
}

changelog、’components/form/v3’、’docs/resources’,都是配置的是 MarkDown 文件路径,
都扫一眼,发现没啥特殊是,只是网站组成的页面一部分;
docs/components 都是目录,先看看目录有什么东西;

> $ ls docs/ components/

components:
## 省略输出

docs/:
react              resources.en-US.md resources.zh-CN.md spec

components 下有个我们熟悉的 index.tsxcomponents/index.tsx
这个模块导出了很多组件,组件名都能跟官网的组件总览一一对应
可以得知这个文件时组件的入口文件;
我们用最简单的 Button 示例, http://localhost:8001/components/button-cn/
这个页面有 “按钮用于开始一个即时操作。” 的文本,用 vscode 全局搜索,得知这段话来源于 ——
components/button/index.zh-CN.md ,试着修改这段文本,在本地的官网项目查看效果。

然而页面下面的 “代码演示” 的源码内容并没有包含在此文件里;
通过 bisheng 的主题配置去找 “生成规则” 太浪费时间了(还是得注意我们的关注点在 antd,其他都不关注);
用 VScode 搜索 Primary Button 字段,尝试修改 components/button/demo/basic.md

小结:

  1. components/index.tsx 组件的总入口文件
  2. components/[name] 一个目录对应一个组件
  3. components/[name]/index.tsx 组件模块级别的入口文件,导出组件级别模块
  4. components/[name]/index.zh-CN.md 组件页面入口文件
  5. components/[name]/demo 组件页面入口 “代码示例”

调试代码

  1. console.log
  2. 浏览器断点调试
  3. 源码断点调试

React JavaScript Tutorial in Visual Studio Code

包别名

npm-link | npm Docs

体系家族

Ant Desigin 有很庞大的体系,通过之前的阅读源码我们触及到了其体系下的多个 npm 包 ——“@ant-design/tools”、rc-\* 组件,还有官网 footer 下的一些相关资源推荐。

Ant Design Team · GitHub

初心

分析开源项目是一件比较费时费力的事情,我们在开始分析代码前,最好确定一下为什么要分析源码,以及真的有必要吗。

  • 学习优秀的同类项目
  • 使用开源项目过程中报错,定位 bug 是否在此项目
  • 技术调研(成本偏高)