最近在学习react源码,所以准备搭建一个本地环境调试看看,这边记录一下搭建流程
使用版本:17.0.2
第一步 下载react源码
在git地址上先下载源码,以备后续使用
提示: 记得把分支切换到17.0.2
第二步 使用官方脚手架创建项目
// 使用npm添加
npx create-react-app my-debug-react
// 使用yarn添加
yarn create react-app my-app
第三步 暴露eject配置
// 如果npm命令安装,则使用下面的命令暴露配置
npm run eject
// 如果yarn命令安装,则使用下面的命令暴露配置
yarn eject
第四步 引入react包
这一步很简单也很重要,在项目的src中新建react文件夹,将从仓库拷贝的代码中的整个packages包, 复制到src/react目录下,结果如下:
第五步 修改webpack配置
修改config/webpack.config.js
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')
}
}
第六步 修改环境变量
修改config/env.js
const stringified = {
__DEV__: true,
__PROFILE__: true,
__UMD__: true,
__EXPERIMENTAL__: true,
'process.env': Object.keys(raw).reduce((env, key) => {
env[key] = JSON.stringify(raw[key])
return env
}, {})
}
根目录创建.eslintrc.json文件
{
"extends": "react-app",
"globals": {
"__DEV__": true,
"__PROFILE__": true,
"__UMD__": true,
"__EXPERIMENTAL__": true
}
}
添加flow的类型判断
npm i @babel/plugin-transform-flow-strip-types -D
在config/webpack.config.js 中babel-loader的plugin添加配置
{
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这一部分
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
}
},
注释掉esling的插件(eslint-webpack-plugin)
// !disableESLintPlugin &&
// new ESLintPlugin({
// // Plugin options
// extensions: ['js', 'mjs', 'jsx', 'ts', 'tsx'],
// formatter: require.resolve('react-dev-utils/eslintFormatter'),
// eslintPath: require.resolve('eslint'),
// failOnError: !(isEnvDevelopment && emitErrorsAsWarnings),
// context: paths.appSrc,
// cache: true,
// cacheLocation: path.resolve(
// paths.appNodeModules,
// '.cache/.eslintcache'
// ),
// // ESLint class options
// cwd: paths.appPath,
// resolvePluginsRelativeTo: __dirname,
// baseConfig: {
// extends: [require.resolve('eslint-config-react-app/base')],
// rules: {
// ...(!hasJsxRuntime && {
// 'react/react-in-jsx-scope': 'error',
// }),
// },
// },
// }),
第七步 导出HostConfig
修改 src\packages\react-reconciler\src\ReactFiberHostConfig.js, 导出HostConfig
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow
*/
/* eslint-disable react-internal/invariant-args */
import invariant from 'shared/invariant';
// We expect that our Rollup, Jest, and Flow configurations
// always shim this module with the corresponding host config
// (either provided by a renderer, or a generic shim for npm).
//
// We should never resolve to this file, but it exists to make
// sure that if we *do* accidentally break the configuration,
// the failure isn't silent.
// invariant(false, 'This module must be shimmed by a specific renderer.');
export * from './forks/ReactFiberHostConfig.dom'
修改 src\react\packages\shared\ReactSharedInternals.js
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow
*/
import * as React from 'react';
import ReactSharedInternals from '../react/src/ReactSharedInternals'
// const ReactSharedInternals =
// React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED;
export default ReactSharedInternals;
第八步 启动测试
报错
修改文件/src/react/packages/shared/invariant.js
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
*/
/**
* Use invariant() to assert state which your program assumes to be true.
*
* Provide sprintf-style format (only %s is supported) and arguments
* to provide information about what broke and what you were
* expecting.
*
* The invariant message will be stripped in production, but the invariant
* will remain to ensure logic does not differ in production.
*/
export default function invariant(condition, format, a, b, c, d, e, f) {
// throw new Error(
// 'Internal React error: invariant() is meant to be replaced at compile ' +
// 'time. There is no runtime version.',
// );
}
再次启动,报错
可以看出是这个变量没有定义,
修改
/src/react/packages/scheduler/src/SchedulerFeatureFlags.js文件
直接将变量改成一个true
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
*/
export const enableSchedulerDebugging = false;
export const enableIsInputPending = false;
export const enableProfiling = true;
再次报错
修改文件
/src/react/packages/scheduler/src/SchedulerHostConfig.js,
直接把这个抛出异常注释掉
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow
*/
//throw new Error('This module must be shimmed by a specific build.');
ok!成功启动了
参考文章