搭建本地React源码调试环境

搭建本地React源码调试环境

一般来说,我们想要去学习 React 的源码的时候,可能会先Build一下,然后使用源码包下的fixtures
内的测试样例进行调试,但是,不管是productionbuild还是Developmentbuild
,源码都是被打包在一个文件中的,结构混乱,即使代码没有被压缩,也很难看得懂具体那一个方法是属于哪一个模块的。所以,为何不使用源码包直接来调试源代码呢?

本文将会介绍如何直接使用React源码包来调试源码。

相关代码以全部放置在:

https://gitee.com/cyrusky/StudyReact.git

创建一个项目

1
create-react-app reading_source

其中,reading_source是自己起的名字,你也可以使用自己的名字。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
Creating a new React app in ./reading_source.

Installing packages. This might take a couple of minutes.
Installing react, react-dom, and react-scripts with cra-template...

yarn add v1.19.2
[1/4] 🔍 Resolving packages...
[2/4] 🚚 Fetching packages...
[3/4] 🔗 Linking dependencies...
[4/4] 🔨 Building fresh packages...
[1/4] ⡀ fsevents

...(省略)
...(省略)
...(省略)

We suggest that you begin by typing:

cd reading_source
yarn start

Happy hacking!

这个时候,我们就创建好了一个空白的项目,最好之前可以启动一下:

1
2
cd reading_source
yarn start

此时会自动打开一个页面,显示下面的样子,说明,我们的项目创建成功了。

Eject 配置

因为我们需要对项目中的依赖进行自定义配置,所以,我们需要暴露出 React 项目的配置文件,执行:

1
$ yarn eject

我们就会得到 React 项目的配置文件以及一些构建脚本:

克隆 React 源码

克隆一个指定版本的 React 源码,到src/react目录下,当然这里也可以使用master
分支,但是不建议。如果你需要将你自己对代码的修改保存到版本控制中,你最好自己fork一份React官方的repo,到自己的账号。

话说 github 是在是太慢了,所以,我fork了一份,到我的github仓库,然后,强制同步到了gitee仓库了。

我选择的分支是16.8.6,截止发稿日期的最新版本。

1
git clone git@gitee.com:cyrusky/react.git -b 16.8.6 src/react

开始修改配置

webpack 中将包链接到源代码

修改/config/webpack.config.js

1
2
3
4
5
6
7
8
9
10
resolve: {
alias: {
'react-native': 'react-native-web',
+ 'react': path.resolve(__dirname, '../src/react/packages/react'),
+ 'react-dom': path.resolve(__dirname, '../src/react/packages/react-dom'),
+ 'shared': path.resolve(__dirname, '../src/react/packages/shared'),
+ 'react-reconciler': path.resolve(__dirname, '../src/react/packages/react-reconciler'),
'react-events': path.resolve(__dirname, '../src/react/packages/events')
}
}

需要注意的一点是:react-eventsmaster分支中已经变更为legacy-events了,不需要在此处添加了。

修改环境变量

修改/config/env.js

1
2
3
4
5
6
7
8
9
const stringified = {
__DEV__: true,
__PROFILE__: true,
__UMD__: true,
'process.env': Object.keys(raw).reduce((env, key) => {
env[key] = JSON.stringify(raw[key])
return env
}, {})
}

根目录创建.eslintrc.json文件

1
2
3
4
5
6
7
8
{
"extends": "react-app",
"globals": {
"__DEV__": true,
"__PROFILE__": true,
"__UMD__": true
}
}

忽略 flow 下 type

1
$ yarn add @babel/plugin-transform-flow-strip-types -D

同时在/config/webpack.config.jsbabel-loaderplugins中添加该插件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
{
test: /\.(js|mjs|jsx|ts|tsx)$/,
include: paths.appSrc,
loader: require.resolve('babel-loader'),
options: {
customize: require.resolve(
'babel-preset-react-app/webpack-overrides'
),

plugins: [
+ require.resolve('@babel/plugin-transform-flow-strip-types'),
[
require.resolve('babel-plugin-named-asset-import'),
{
loaderMap: {
svg: {
ReactComponent:
'@svgr/webpack?-svgo,+titleProp,+ref![path]'
}
}
}
]
],
// This is a feature of `babel-loader` for webpack (not Babel itself).
// It enables caching results in ./node_modules/.cache/babel-loader/
// directory for faster rebuilds.
cacheDirectory: true,
// See #6846 for context on why cacheCompression is disabled
cacheCompression: false,
compact: isEnvProduction
}
},

就是避免这个错误:

1
2
3
4
5
6
7
8
9
10
11
12
Failed to compile.

./src/react/packages/react-dom/src/client/ReactDOM.js
SyntaxError: ./reading_source/src/react/packages/react-dom/src/client/ReactDOM.js: Unexpected token (10:12)

8 | */
9 |
> 10 | import type {ReactNodeList} from 'shared/ReactTypes';
| ^
11 | // TODO: This type is shared between the reconciler and ReactDOM, but will
12 | // eventually be lifted out to the renderer.
13 | import type {

解决 event 冲突(master 分支请忽略)

webpack.config.js中的alias中添加react-events后,需要修改react源码包中相应引用event的部分,具体如下:

替换源码中所有的import XXX from 'events/...'import XXX from 'react-events/...',其中react-events就是alias
中的命名。

导出 HostConfig

修改文件/src/react/packages/react-reconciler/src/ReactFiberHostConfig.js
。注释中说明,这块还需要根据环境去导出HostConfig

1
2
// invariant(false, 'This module must be shimmed by a specific renderer.');
export * from './forks/ReactFiberHostConfig.dom'

保持 import first,根据编译信息修改

修改文件/src/react/packages/shared/ReactSharedInternals.jsreact此时未export内容,直接从ReactSharedInternals拿值

1
2
3
4
//  import React from 'react';
import ReactSharedInternals from '../react/src/ReactSharedInternals'

// const ReactSharedInternals = React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED;

关闭 ESlint 对 fbjs 插件的扩展

修改src/react/.eslingrc.js,在module.exports中删去extends: 'fbjs'

1
2
3
module.exports = {
// extends: 'fbjs',
......

vscode 配置

安装flowtype.flow-for-vscode插件:

进行配置,一般来说,我们只会在看源码的时候用到flow,所以我们将配置写在workspace的配置文件下:

测试

至此,我们的源码调试环境就搭建完毕了。我们可以测试一下:

我们修改一下src/react/packages/react-dom/src/client/ReactDOMComponents.js中的createElement,添加一个断点:

1
2
3
4
5
6
7
8
9
export function createElement(
type: string,
props: Object,
rootContainerElement: Element | Document,
parentNamespace: string,
): Element {
debugger
let isCustomComponentTag;
......

一般来说,如果你操作了yarn start,在修改代码后,会热加载。所以,如果你的development tools是打开的,这个时候,应该就可以停在断点处了,具体如下:

来愉快的进行调试把!!

搭建本地React源码调试环境

https://www.borgor.cn/posts/f48cc00c.html

作者

Cyrusky

发布于

2019-12-09

更新于

2024-11-18

许可协议

评论