写 react 项目的小伙伴应该都用过 antd 组件库,但绝大多数同学并没有看过它的源码。

而想深化把握 antd 组件库,只了解参数是不行的,必须要深化到源码层面。

所以今天就来分享下如何调试 antd 的源码。

而且我敢说这种调试源码的方法 90% 的前端都不会。

为什么呢?看到后面你就知道了。

首要,咱们用 create-react-app 创建一个 react 项目:

yarn create react-app antd-react-test

创建成功后,进入到项目里,把 dev server 跑起来。

为什么说 90% 的前端不会调试 Ant Design 源码?

浏览器访问能够看到渲染出的页面:

为什么说 90% 的前端不会调试 Ant Design 源码?

然后咱们装置 antd,在进口组件里引进款式和 Button 组件:

为什么说 90% 的前端不会调试 Ant Design 源码?

页面会显现这个 Button:

为什么说 90% 的前端不会调试 Ant Design 源码?

那怎样调试这个 Button 组件的源码呢?

能够这样:

首要,创建一个 VSCode 调试装备:

为什么说 90% 的前端不会调试 Ant Design 源码?

指定调试的 URL,然后启动调试。

在组件里打个断点,代码会在这儿断住:

为什么说 90% 的前端不会调试 Ant Design 源码?

能够看到调用栈中上一帧是 renderWithHooks,这便是 react 源码里调用函数组件的当地。

点击那个调用栈,你就会看到:

为什么说 90% 的前端不会调试 Ant Design 源码?

它调用了 App 的函数组件,传入了参数,拿到渲染后的 children 做后续处理。

一切函数组件都是在这儿被调用的,而 antd 的组件也全部是函数组件,那么咱们在这儿加个断点,打姓名为 Button 的函数组件被调用的时分断住不就行了?

这种在某种条件下才断住的情况能够用条件断点:

右键挑选添加条件断点:

为什么说 90% 的前端不会调试 Ant Design 源码?

输入断住的条件:

为什么说 90% 的前端不会调试 Ant Design 源码?

当组件姓名包含 Button 的时分才断住。

然后改写:

你会看到 App 组件分明也是函数组件,却没有在这儿断住,而 InternalButton 在这儿断住了。

这便是条件断点的作用。

这个 InternalButton 便是 antd 里的 Button 组件。

step into 进入函数内部:

为什么说 90% 的前端不会调试 Ant Design 源码?

你会发现这的确是 Button 组件的源码,但却是被编译后的,比如 jsx 都被编译成了 React.createElement:

为什么说 90% 的前端不会调试 Ant Design 源码?

这样是能够调试 Button 组件源码的,可是比较别扭。

那能不能直接调试 Button 组件对应的 tsx 源码呢?

能够的,这就要用到 sourcemap 了。

咱们得把 antd 的源码下载下来(我下载的时分是 4.23):

git clone --depth=1 --single-branch git@github.com:ant-design/ant-design.git

下载的时分加个 –single-branch 是下载单个分支, –depth=1 是下载单个 commit, 这样速度会快几十倍,是个有用的加快小技巧。

antd 下载下来,装置完依赖之后,咱们开端 build。

但你会发现 package.json 中有 build 指令,有 dist 指令,该履行哪个呢?

这个就需求了解下 antd 的几种进口了。

去 react 项目的 node_modules 下,找到 antd 的 package.json 看一下,你会发现它有三种进口:

为什么说 90% 的前端不会调试 Ant Design 源码?

  • main 是 commonjs 的进口,也便是 require(‘antd’) 的时分会走这个。

  • module 是 esm 的进口,也便是 import xx from ‘antd’ 的时分会走这个。

  • unpkg 是 UMD 的进口,也便是通过 script 标签引进的时分或者 commonjs 的方法等都能够用。

别离对应了 lib、es、dist 的目录。

所以 antd 项目里的 dist 指令便是单独生成 UMD 代码的,而 build 指令是生成这三种代码。

这三种方式的代码都是可用的,这儿咱们挑选构建 UMD 方式的代码,由于它会用 webpack 打包,而另外两种是通过 gulp 构建的。我对 webpack 更了解一些。

履行 npm run dist,就会构建出 dist 目录,下面是 UMD 的代码:

为什么说 90% 的前端不会调试 Ant Design 源码?

为什么说 90% 的前端不会调试 Ant Design 源码?

你会发现默许的构建便是会生成 sourcemap 的,其实你去那个 react 测验项目里看下,从 npm 下载的 antd 包也带了 sourcemap:

为什么说 90% 的前端不会调试 Ant Design 源码?

那直接用 dist 进口的代码就能调试源码了么?

咱们试一下:

为什么说 90% 的前端不会调试 Ant Design 源码?

把引进组件的当地换成 dist 目录下,也便是用 UMD 方式的进口。

从头跑调试:

你会发现代码的确比之前更像源码了。

之前前面是这样的:

为什么说 90% 的前端不会调试 Ant Design 源码?

现在是这样:

为什么说 90% 的前端不会调试 Ant Design 源码?

也便是没了 babel runtime 的代码,这显着是源码了。

可是你往后看:

之前是这样的:

为什么说 90% 的前端不会调试 Ant Design 源码?

现在是这样:

为什么说 90% 的前端不会调试 Ant Design 源码?

仍然仍是 React.createElement,而不是 jsx,也没有 ts 的代码。

说明它还不是开始的源码。

为什么会出现这种既是源码又不是源码的情况呢?

由于它的编译流程是这样的:

为什么说 90% 的前端不会调试 Ant Design 源码?

代码通过了 tsc 的编译,然后又通过了 babel 的编译,最后再通过 webpack 打包成 bundle.js。

tsc 和 babel 的编译都会生成 sourcemap,而 webpack 也会生成一个 sourcemap。

webpack 的 sourcemap 默许只会依据最后一个 loader 的 sourcemap 来生成。

所以说上面咱们用了 sourcemap 之后只能相关到 babel 处理之前的代码,像 ts 语法、jsx 代码这些都没有了。

由于没有相关更上一级的 ts-loader 的 sourcemap,自然是没法直接映射回源码的。

所以想映射回开始的 tsx 源码,只需相关了每一级 loader 的 sourcemap 就能够了。而这个是能够装备的,便是 devtool。

devtool 能够设置 soruce-map,便是生成 sourcemap,可是这个不会相关 loader 的 sourcemap。

还能够设置 cheap-module-source-map,这个 module 便是相关 loader 的 soruce-map 的意思。(那个 cheap 是只保存行的 sourcemap,生成速度会更快)

思路理清楚了,咱们去改下编译装备:

antd 的编译东西链在 @ant-design/tools 这个包里,从 antd/node_modules/@antd-design/tools/lib/getWebpackConfig.js 就能够找到 webpack 的装备:

为什么说 90% 的前端不会调试 Ant Design 源码?

搜一下 ts-loader,你就会看到这段装备:

为什么说 90% 的前端不会调试 Ant Design 源码?

的确就像咱们分析的,tsx 会通过 ts-loader 和 babel-loader 的处理。

搜一下 devtool,你会发现它的装备是 source-map:

为什么说 90% 的前端不会调试 Ant Design 源码?

这便是 antd 虽然有 sourcemap,可是相关不到 tsx 源码的原因。

那咱们给它改一下:

把 devtool 改为 cheap-module-source-map。

并且改一下 babel 装备,设置 sourceMap 为 true,让它生成 sourcemap。

为什么说 90% 的前端不会调试 Ant Design 源码?

ts也相同要生成 sourcemap,不过那个是在根目录的 tsconfig.json里改:

为什么说 90% 的前端不会调试 Ant Design 源码?

改完这三点之后,再从头跑 npm run dist。

dist 目录下会生成新的 antd.js 和 antd.js.map。

为什么说 90% 的前端不会调试 Ant Design 源码?

把它复制到 react 项目的 node_modules/antd/dist 下,掩盖之前的。

清一下 babel-loader 的缓存,删去整个 .cache 目录:

为什么说 90% 的前端不会调试 Ant Design 源码?

从头跑 dev server。

注意,这儿要用 dist 下的代码:

为什么说 90% 的前端不会调试 Ant Design 源码?

然后再跑到断点的位置,进入组件源码,你会进入一个新世界:

为什么说 90% 的前端不会调试 Ant Design 源码?

ts 类型、jsx 的语法,了解的感觉又回来了,这不便是 antd 组件的源码么!

为什么说 90% 的前端不会调试 Ant Design 源码?

你能够断点调试 antd 的参数是怎样处理的,什么参数会走什么逻辑等。

这个完全不影响正常开发,也便是把 antd 换成了从 antd/dist/antd 引进罢了,开发完了换回去就行。

现在开发 antd 组件还有看文档么?

直接看源码它不更香么!

有的同学或许会忧虑 node_modules 下的改动保存不下来。

这个也不是问题,能够履行下 npx patch-package antd,会生成这样一个 patch 文件:

为什么说 90% 的前端不会调试 Ant Design 源码?

patch 文件里记录了你对 antd 包的改动,这个能够上传到 git 仓库,其他小伙伴拉下来再履行 npx patch-package 就会主动使用这些改动。

至此,咱们成功的调试了 antd 组件的 tsx 源码。

为什么说 90% 的前端不会调试它的源码呢?

主要是触及的技能比较多:

  • VSCode Chrome Debugger 调试网页,这个知道的人就不多
  • react 源码里 renderWithHooks 是调用函数组件的当地
  • 条件断点能够在满足条件的时分断住
  • antd 的 esm、commonjs、UMD 三种进口
  • sourcemap 是干啥的,虽然常常触摸,但仍是有许多前端没用过
  • webpack 的 cheap-module-source-map 的意义,为什么需求相关 loader 的 sourcemap

而调试 antd 的组件源码需求综合运用这些技能,难度仍是比较高的。

总结

antd 是 react 干流组件库,咱们常常使用它但或许并没有调试过它的源码。

咱们能够在 renderWithHooks 里调用函数组件的当地打个条件断点,在调用想调试的组件时断住,这样咱们就能够 step into 到该组件定义的当地。

可是这样调试的并不是开始的源码,没有 jsx 和 ts 语法。

想调试开始的 tsx 源码需求用 sourcemap。

antd 有三种进口:es 目录对应 esm 进口,lib 目录对应 commonjs 进口,dist 目录对应 UMD 进口。

把 antd 代码下载下来,履行 npm run dist 就能够生成 UMD 方式的代码。

想要 sourcemap 映射到 tsx 源码,需求把 devtool 设置成 cheap-module-source-map,然后开启 babel-loader 和 ts-loader 的 sourcemap。

把产品掩盖 antd 的 dist 下的产品,再调试就能够直接调试 antd 组件的 tsx 源码了。

用 antd 组件写事务逻辑之余,对什么组件感兴趣,能够顺便去看看它的源码,它不香么?

最后打个小广告,更多调试相关技能能够看我的调试小册 《前端调试通关秘籍》