React 源码学习 - 搭建本地调试环境

7 阅读2分钟

准备工作

用webpack新建一个react本地项目、我这里使用webpack5,配置如下:

const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');

module.exports = {
  entry: './src/index.js', // 入口文件
  output: {
    filename: 'bundle.js', // 输出文件名
    path: path.resolve(__dirname, 'dist') // 输出目录
  },
  devServer: {
    static: {
      directory: path.join(__dirname, 'dist'), // 本地服务器所加载的页面所在的目录
    },
    compress: true, // 启用 gzip 压缩
    port: 9000, // 本地服务器端口号
    open: true // 自动打开浏览器
  },
  module: {
    rules: [
      {
        test: /(.jsx|.js)$/,
        use: ["babel-loader"],
        exclude: /node_modules/
      }
    ]
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: './src/index.html', // 指定自定义 HTML 模板
      filename: 'index.html' // 生成的 HTML 文件名
    }),
  ],
  mode: 'development' // 开发模式
};

index.js

import { createRoot } from 'react-dom/client'
import React from 'react'
import App from './App.jsx'
const Root = createRoot(document.getElementById('app'))
Root.render(<App />)

babel.config.json

{
    "presets": [
        "@babel/preset-react",
    ]
}

相关依赖

    "webpack": "^5.0.0",
    "webpack-cli": "^5.0.0",
    "webpack-dev-server": "^4.0.0"
    "@babel/core": "^7.27.4",
    "@babel/preset-react": "^7.27.1",
    "@babel/runtime": "^7.27.6",
    "babel-loader": "^10.0.0",
    "html-webpack-plugin": "^5.6.3",
    "react": "^18.3.0",
    "react-dom": "^18.3.0",

加入react

现在启动项目应该是能正常启动的,我们在项目中建一个package目录、clone react源码到package下面,同时安装依赖@babel/preset-flow,因为react使用flow作为类型校验

  • 将preset-flow加入babel
{
    "presets": [
        "@babel/preset-react",
        "@babel/preset-flow"
    ]
}
  • 在webpack中设置别名、将react相关引用指向本地
  resolve: {
    alias: {
      '@': path.resolve(__dirname, 'src'),
      'react': path.resolve(__dirname, 'package/react/packages/react'),
      'react-dom': path.resolve(__dirname, 'package/react/packages/react-dom'),
      'react-reconciler': path.resolve(__dirname, 'package/react/packages/react-reconciler'),
      'scheduler': path.resolve(__dirname, 'package/react/packages/scheduler'),
      'shared': path.resolve(__dirname, 'package/react/packages/shared'),
    }
  },

处理错误

这个时候我们启动应用会报一堆错误

  • react引用错误

image.png 更改引用方式

import * as Client from 'react-dom/client'
import * as React from 'react'
const { createRoot } = Client
  • ReactFiberHostConfig.js

image.png

修改react\packages\react-reconciler\src\ReactFiberHostConfig.js

// throw new Error('This module must be shimmed by a specific renderer.');

export * from './forks/ReactFiberHostConfig.dom'

  • Scheduler.js报错
WARNING in ./package/react/packages/react-reconciler/src/Scheduler.js 28:35-64
export 'unstable_yieldValue' (imported as 'Scheduler') was not found in 'scheduler'

WARNING in ./package/react/packages/react-reconciler/src/Scheduler.js 29:45-84
export 'unstable_setDisableYieldValue' (imported as 'Scheduler') was not found in 'scheduler'

修改package\react\packages\scheduler\index.js

export { unstable_yieldValue, unstable_setDisableYieldValue } from './src/forks/SchedulerMock';

export * from './src/forks/Scheduler';
  • ReactSharedInternals.js 报错 修改package\react\packages\shared\ReactSharedInternals.js

// import * as React from 'react';


// const ReactSharedInternals =
  // React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED;
import ReactSharedInternals from 'react/src/ReactSharedInternals';

export default ReactSharedInternals;
  • 环境变量缺少
Uncaught ReferenceError: __EXPERIMENTAL__ is not defined
    at eval (ReactFeatureFlags.js:155:21)

使用webpackDefinePlugin定义变量

const webpack = require('webpack')
new webpack.DefinePlugin({
  __DEV__: true,
  __PROFILE__: true,
  __UMD__: true,
  __EXPERIMENTAL__: true,
  __VARIANT__: false,
})

现在运行你的启动命令就大功告成啦,项目结构如下

image.png