阅读源码时,有许多变量在程序运行过程当中不断的产生,其中存放着什么东西,一直是一个比较头疼的问题。不停的推导增长了验算的负担,随着代码逐渐的深刻,也会产生必定的记忆负担。若是靠脑壳去记,简单点的代码还好。复杂的代码。。。你懂的。
随着react被普遍使用,不少人会好奇react是怎么实现的。会有一探源码的想法。若是直接阅读react.development.js是很简单,页面引入就行了。可是react.development.js终因而通过编译工具编译过的代码,不少的代码看起来并不直观。理想的状况是直接引用源文件,也就是github上react仓库中,packages目录下的代码,直接阅读es6的代码。
可是es6代码浏览器支持并不友好。因此须要配置webpack打包成es5。同时须要配上sourceMap。这样,既可让源码跑在浏览器环境,也能够直接读es6的代码,并且能够随时打断点,查看变量里保存的值。html
那么,闲言少叙,开始本章的主题。node
在配置调试环境的过程当中,参考了许多相关资料,这里先列出来,感兴趣的同窗能够参考。react
主流程相关的资料,点击跳转,同时致敬如下用户webpack
主流程无关可是收益颇丰的资料git
本人所在测试环境为mac,其余环境相似,调试版本React Version 16.9.0
须要准备的一些环境: Node/npm/create-react-app/git。
ps:本文是对参考资料的梳理以及优化,力求言简意赅。es6
git@github.com:pws019/react.git
).npx create-react-app my-app
(利用create-react-app
建立本身的demo项目)cd my-app
(进入上一步建立出来的my-app目录)yarn run eject
(将webpack的配置提取出来,执行完后项目中会多一个config文件夹,存放webpack相关脚本)src
目录,git clone git@github.com:pws019/react.git(替换为你刚fork的react路径)
/config/webpack.config.js
中的resolve
选项增长alias
以下(为了让项目中的引用的react是源码包里的react):({ xxxx: 'xxx', resolve: { alias: { // Support React Native Web // https://www.smashingmagazine.com/2016/08/a-glimpse-into-the-future-with-react-native-for-web/ // 'react-native': 'react-native-web', 'react': path.resolve(__dirname, '../src/react/packages/react'), 'react-dom': path.resolve(__dirname, '../src/react/packages/react-dom'), 'legacy-events': path.resolve(__dirname, '../src/react/packages/legacy-events'), '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'), // 'scheduler': path.resolve(__dirname, '../src/react/packages/scheduler'), }, }, xxxx: 'xxx', })
devtool
的值改成source-map
(为了让打出的包有sourcemap)/config/env.js
中的stringifed
对象增长属性:const stringified = { 'xxxx': 'xxx', "__DEV__": true, "__PROFILE__": true, "__UMD__": true };
flow
这个东东作类型检查,执行yarn add @babel/plugin-transform-flow-strip-types -D
,安装对应的babel插件忽略flow的类型检查,而且在webpack的babel-loader
中增长该插件{ 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-named-asset-import'), { loaderMap: { svg: { ReactComponent: '@svgr/webpack?-svgo,+titleProp,+ref![path]', }, }, }, ], [require.resolve('@babel/plugin-transform-flow-strip-types')] //*************这一行是新加的 ], // 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, cacheCompression: isEnvProduction, compact: isEnvProduction, }, },
webpack
中module.rules[1]
,也就是eslint的配置,由于我也没搞明白,怎么配,总报错。这时候执行npm start
启动项目,会发现报错,主要有三处,依次解决之。github
/src/react/packages/react-reconciler/src/ReactFiberHostConfig.js
。注释中说明,这块还须要根据环境去导出HostConfig。export * from './forks/ReactFiberHostConfig.dom';
/src/react/packages/shared/ReactSharedInternals.js
。react此时未export内容,直接从ReactSharedInternals拿值// import React from 'react'; // const ReactSharedInternals = React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED; import ReactSharedInternals from '../react/src/ReactSharedInternals';
src/react/packages/shared/invariant
中的函数,直接抛错,这里纠结了很久才找到解法,改下这个函数的内容为export default function invariant(condition, format, a, b, c, d, e, f) { if(condition) return; throw new Error( 'Internal React error: invariant() is meant to be replaced at compile ' + 'time. There is no runtime version.', ); }
配这个环境的时候,github用户nannongrousong
的那篇文章给了我不少帮助,基本是按着他的配的,可是在最后,老是在react-dom的checkReact中总报错,这个问题困扰了我好久。web
后来忽然想起来,官方的文档里提到当 invariant 判别条件为 false 时,会将 invariant 的信息做为错误抛出,而我调试的那个地方,值为true依然报错了。npm
后来查看了github用户nannongrousong
提供的调试环境成品库,主要差距就是src/react/packages/shared/invariant
里函数的实现。json
后来我去翻了该文件的commit history
,发现了这个文件的改动历史,发现是因为有同窗想减小包的大小,更好的归拢错误提示,而吧这里抽出来,由自动化工具去替换。
感兴趣的能够看下这里,
经过这里的相关代码,能够看到,他是利用ast语法树的分析&替换,去经过工具处理了错误,这个思路值得咱们学习借鉴。
点我跳转这个仓库是已经配好的环境,安装完依赖包就能够开始。