React源码调试环境搭建

719 阅读3分钟

最近在学习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目录下,结果如下:

image.png

第五步 修改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-loaderplugin添加配置

{
              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;

第八步 启动测试

报错

image.png

修改文件/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.',
  // );
}

再次启动,报错

image.png 可以看出是这个变量没有定义, 修改/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;

再次报错

image.png 修改文件/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!成功启动了

image.png

参考文章