前言
有很多人看过很多react源码解读的文章,但是都是别人写好的、截图的知识点。但是自己去看react源码的时候,又被庞大又复杂的项目劝退。 本系列将尝试与大家一起动手开始源码阅读,并同步实践,目标是输出一个属于自己的react。 本系列持续更新react源码解读历程。 拒绝只动眼不动手。 可以随本系列动手开始在自己本地进行实践。
本文主旨
删繁就简,提取react构建之后的核心内容。并能快速对应到源码的位置
下载react源码,并打包到本地
- git clone下载react源码 github.com/facebook/re…
- cd react进入react源码目录
- yarn build react, shared, scheduler, react-reconciler, react-dom --type=NODE
- 需要java环境
- cd build/node_modules/react
- yarn link
- cd build/node_modules/react-dom
- yarn link
- 然后创建一个新项目,比如mini-react
- 在mini-react中执行yarn link react react-dom即可
react 源码使用 rollup 打包,将所有的模块都打包到一个文件中,比如 react.development.js 以及 react-dom.development.js,没有对应
的 sourcemap,导致阅读源码的过程当中无法得知源码位于哪个文件,如下图中红框内的源码无法映射到原文件,阅读体验不好。
同时,react 在打包开发环境的代码时,会引入大量的本地调试代码
这段代码对应的源码在于:packages/react/src/ReactDebugCurrentFrame.js文件中:
react 源码中大量使用 __DEV__ 环境变量判断(参考:__DEV__说明),如果是开发环境,则括号中的代码会被打包进产物中,如果是生产环境,则不会打包进产物中。这样 可以在开发环境注入一些调试代码,比如检查 props 是否合法、创建 element 的时候是否需要校验参数等情况
实际上,这些开发时的校验代码与react主流程没有什么关系,我们不关心这些开发时的场景,只需要专注于主流程,因此如果打包时能够减少这部分代码,对我们阅读 体验来说还是相当不错的。
实现
修改 react 源码打包时 rollup 配置,然后终端运行:
yarn build react, shared, scheduler, react-reconciler, react-dom --type=NODE
打包完成,复制 build/node_modules/react/cjs/react.development.js 以及 build/node_modules/react-dom/cjs/react-dom.development.js,在本地粘贴,本地调试时可以使用这两份源码
// Remove 'use strict' from individual source files.
{
transform(source, id) {
id = id.replace('/Users/lizc/Documents/MYProjects/react/', '')
let sourceStr = source.replace(/['"]use strict["']/g, '');
sourceStr = `/***************** debugger ${id} == start *****************/\n${source}\n/***************** debugger ${id} == end *****************/`
return sourceStr;
},
},
// Turn __DEV__ and process.env checks into constants.
replace({
__DEV__: 'false', // isProduction ? 'false' : 'true',
__PROFILE__: isProfiling || !isProduction ? 'true' : 'false',
__UMD__: isUMDBundle ? 'true' : 'false',
'process.env.NODE_ENV': isProduction ? "'production'" : "'development'",
__EXPERIMENTAL__: false,
// __EXPERIMENTAL__,
// Enable forked reconciler.
// NOTE: I did not put much thought into how to configure this.
__VARIANT__: bundle.enableNewReconciler === true,
}),
效果
最终,优化后,打包出来的代码体积,react.development.js 从原先的2334行,减少到1122行。react-dom.development.js 从原先的26263行 减少到20600行。
源码拆分
react打包出来的源码都在一份文件中,比如 react-dom 打包后的代码接近2万行,对于我,只要将源码位置信息注入到打包后的代码中,阅读体验就非常好了。有些同学可能还是习惯于将源码映射到不同的文件,那么也可以通过在 rollup 配置中,修改 transform 插件的逻辑,比如:
transform(source, id) {
// 修改id
id = id.replace('/Users/lizc/Documents/MYProjects/react/', '/Users/lizc/Documents/MYProjects/react/dist/')
let sourceStr = source.replace(/['"]use strict["']/g, '');
require('fs-extra').outputFile(id, sourceStr)
return sourceStr;
},
这个时候就会在根目录下生成一个dist文件夹,里面就是源码映射的文件
阅读代码
react.development.js react-dom.development.js 即是需要阅读的内容