最近看了很多react源码的讲解视频、文章,不过纸上得来终觉浅,看一百次不如自己调试一次印象来得深。
拉取react源码安装依赖
js
git clone https://github.com/facebook/react.git
yarn
拉取源码后可以看到react的模块都放在packages目录下,因为react的类型检查用的是flow,肯定是不能直接运行在浏览器里的所以我们需要使用babel转换成普通的js。packages下有许多模块有的模块涉及到服务端渲染、开发工具、跨平台,这里我们只需要浏览器相关的模块 react react-client react-dom react-dom-bindings react-reconciler scheduler shared 接下来就可以使用babel转换这些模块,转换的时候会遇到报错有些报错文件并不会转换成功,先不用管,后面启动项目时遇到缺少的文件或目录再一一单独转换就行
js
// 这里只举例了react, 上述其他模块也是如此
./node_modules/.bin/babel ./packages/react --out-dir ./build/react
启动项目
- 先创建一个react项目,然后把react源码build目录拷到react-debug下改名为react-source。
js
pnpm create vite react-debug --template react
- 修改vite.config.js
js
import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";
import path from "path";
// https://vite.dev/config/
export default defineConfig({
plugins: [
// 修改 jsx 方法的引用路径,会指向到react-source/react/jsx-dev-runtime.js
react({
jsxRuntime: "automatic",
jsxImportSource: path.resolve(import.meta.dirname, "react-source/react/"),
}),
],
// 定义react中的变量
define: {
__EXPERIMENTAL__: false,
__PROFILE__: false,
__DEV__: false,
},
resolve: {
// 设置文件系统别名
alias: [
{
find: "react",
replacement: path.resolve(import.meta.dirname, "react-source/react/"),
},
{
find: /^react-dom\/(.*)?/,
replacement: path.resolve(
import.meta.dirname,
"react-source/react-dom/$1"
),
},
{
find: /^react-source\/(.*)?/,
replacement: path.resolve(import.meta.dirname, "react-source/$1"),
},
{
find: /^react-dom-bindings\/(.*)?/,
replacement: path.resolve(
import.meta.dirname,
"react-source/react-dom-bindings/$1"
),
},
{
find: /^shared\/(.*)?/,
replacement: path.resolve(
import.meta.dirname,
"react-source/shared/$1"
),
},
{
find: /^react-reconciler\/(.*)?/,
replacement: path.resolve(
import.meta.dirname,
"react-source/react-reconciler/$1"
),
},
{
find: "scheduler",
replacement: path.resolve(
import.meta.dirname,
"react-source/scheduler/"
),
},
{
find: /^react-client\/(.*)?/,
replacement: path.resolve(
import.meta.dirname,
"react-source/react-client/$1"
),
},
],
},
});
- 修改 react/src/jsx/ReactJSX.js。(PS: 可能会因为在第一步执行babel失败在react-source中并没有jsx目录,再到react源码中执行
./node_modules/.bin/babel ./packages/react/src/jsx --out-dir ./build/react/src/jsx
然后再把jsx目录复制到react-source就行了,其他babel执行失败的文件也是一样 )
js
// react-source/react/src/jsx/ReactJSX.js
var jsxDEV = __DEV__ ? _jsxDEV : jsxProd
- 把引入路径都成从 react-source 中引入
js
// App.jsx
import { useState } from "react-source/react"
function App() {
const [count, setCount] = useState(0)
return (
<div>
I am React
<button onClick={() => {
setCount(count + 1)
}}>{count}</button>
</div>
)
}
export default App
- 最后就可以开始"愉快"得调试源码啦,现在在createRoot的地方打断点就会进到react-source里了