├── .vscode └── settings.json ├── src ├── scheduler │ ├── index.js │ └── src │ │ └── forks │ │ ├── SchedulerPriorities.js │ │ ├── SchedulerMinHeap.js │ │ └── Scheduler.js ├── react-dom │ ├── client.js │ └── src │ │ └── client │ │ └── ReactDOMRoot.js ├── react │ ├── jsx-dev-runtime.js │ ├── index.js │ └── src │ │ ├── React.js │ │ ├── ReactSharedInternals.js │ │ ├── ReactCurrentDispatcher.js │ │ ├── jsx │ │ └── ReactJSXElement.js │ │ └── ReactHooks.js ├── shared │ ├── hasOwnProperty.js │ ├── ReactSharedInternals.js │ ├── logger.js │ ├── isArray.js │ ├── assign.js │ ├── ReactSymbols.js │ └── ReactFeatureFlags.js ├── react-dom-bindings │ └── src │ │ ├── events │ │ ├── EventSystemFlags.js │ │ ├── getEventTarget.js │ │ ├── EventListener.js │ │ ├── EventRegistry.js │ │ ├── getListener.js │ │ ├── DOMEventProperties.js │ │ ├── plugins │ │ │ └── SimpleEventPlugin.js │ │ ├── ReactDOMEventListener.js │ │ ├── SyntheticEvent.js │ │ └── DOMPluginEventSystem.js │ │ └── client │ │ ├── setTextContent.js │ │ ├── DOMPropertyOperations.js │ │ ├── CSSPropertyOperations.js │ │ ├── ReactDOMComponentTree.js │ │ ├── ReactDOMHostConfig.js │ │ └── ReactDOMComponent.js ├── react-reconciler │ └── src │ │ ├── Scheduler.js │ │ ├── ReactHookEffectTags.js │ │ ├── ReactFiberReconciler.js │ │ ├── ReactWorkTags.js │ │ ├── ReactFiberSyncTaskQueue.js │ │ ├── ReactFiberFlags.js │ │ ├── ReactEventPriorities.js │ │ ├── ReactFiberConcurrentUpdates.js │ │ ├── ReactFiberRoot.js │ │ ├── ReactFiberBeginWork.js │ │ ├── ReactFiberCompleteWork.js │ │ ├── ReactFiberClassUpdateQueue.js │ │ ├── ReactFiber.js │ │ ├── ReactFiberLane.js │ │ ├── ReactFiberCommitWork.js │ │ ├── ReactChildFiber.js │ │ ├── ReactFiberWorkLoop.js │ │ └── ReactFiberHooks.js └── main.jsx ├── img ├── buma.png ├── fanma.gif ├── fiber.jpg ├── react.png ├── yuanma.gif ├── beginWork.png ├── domDiff.jpeg ├── fiber-hook.png ├── fiberFlow.png ├── hookUpdate.jpg ├── lifeCycle.png ├── min_heap.jpg ├── setState.png ├── taskQueue.jpeg ├── timeSlice.jpeg ├── completeWork.png ├── createRoot.jpeg ├── domDiff_move.jpg ├── eventBubble.png ├── fiberAnverse.png ├── fiberSimple.png ├── fiberUpdate.jpg ├── firstRender.jpg ├── lifeoffram.jpeg ├── lifeofframe.jpeg ├── moreDomDiff.png ├── updatequeue.png ├── dispatch-event.png ├── renderFiber1.jpeg ├── renderRootFiber.jpg ├── renderWithHook.jpg ├── requestIdleback.png ├── singleDomDiff.jpg ├── useLayoutEffect.png ├── useStateMount.png ├── collectEffectList.jpg ├── createUpdateQueue.png ├── fiberConstructor.png ├── reactelement-tree.png ├── registerEventName.png ├── scheduleCallback.jpg ├── completely_twoTree.jpeg ├── memoizedStateQueue.png ├── reactfiberworkloop.png ├── eventuse_1665894712077.jpeg ├── di_gui_gou_jian_fiber_shu.jpeg ├── mountReducer_1678679227351.png ├── queuepending_1644750048819.png ├── extractEvents2_1678678999496.png ├── liu_lan_qi_zhen_1643277067067.jpeg ├── useLayoutEffect_1666851036689.jpeg ├── zui_xiao_dui_1_1643275468911.jpeg ├── dan_jie_dian_diff_1678677255463.png ├── flushPassiveEffects_1666783551920.jpeg ├── initializeUpdateQueue_1664039386818.png ├── processDispatchQueue1_1678679016915.png └── shi_jian_qie_pian_diao_du_1643278352662.jpeg ├── .gitignore ├── index.html ├── vite.config.js ├── LICENSE ├── markdown ├── dfs.md ├── messageChannel.md ├── &|.md └── minHeap.md ├── package.json └── tsconfig.json /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | } -------------------------------------------------------------------------------- /src/scheduler/index.js: -------------------------------------------------------------------------------- 1 | export * from './src/forks/Scheduler'; -------------------------------------------------------------------------------- /src/react-dom/client.js: -------------------------------------------------------------------------------- 1 | export { createRoot } from './src/client/ReactDOMRoot'; -------------------------------------------------------------------------------- /src/react/jsx-dev-runtime.js: -------------------------------------------------------------------------------- 1 | export { jsxDEV } from './src/jsx/ReactJSXElement'; -------------------------------------------------------------------------------- /img/buma.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/changcheng1/react18-source-implementation/HEAD/img/buma.png -------------------------------------------------------------------------------- /img/fanma.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/changcheng1/react18-source-implementation/HEAD/img/fanma.gif -------------------------------------------------------------------------------- /img/fiber.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/changcheng1/react18-source-implementation/HEAD/img/fiber.jpg -------------------------------------------------------------------------------- /img/react.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/changcheng1/react18-source-implementation/HEAD/img/react.png -------------------------------------------------------------------------------- /img/yuanma.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/changcheng1/react18-source-implementation/HEAD/img/yuanma.gif -------------------------------------------------------------------------------- /src/shared/hasOwnProperty.js: -------------------------------------------------------------------------------- 1 | const { hasOwnProperty } = Object.prototype; 2 | export default hasOwnProperty; -------------------------------------------------------------------------------- /img/beginWork.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/changcheng1/react18-source-implementation/HEAD/img/beginWork.png -------------------------------------------------------------------------------- /img/domDiff.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/changcheng1/react18-source-implementation/HEAD/img/domDiff.jpeg -------------------------------------------------------------------------------- /img/fiber-hook.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/changcheng1/react18-source-implementation/HEAD/img/fiber-hook.png -------------------------------------------------------------------------------- /img/fiberFlow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/changcheng1/react18-source-implementation/HEAD/img/fiberFlow.png -------------------------------------------------------------------------------- /img/hookUpdate.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/changcheng1/react18-source-implementation/HEAD/img/hookUpdate.jpg -------------------------------------------------------------------------------- /img/lifeCycle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/changcheng1/react18-source-implementation/HEAD/img/lifeCycle.png -------------------------------------------------------------------------------- /img/min_heap.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/changcheng1/react18-source-implementation/HEAD/img/min_heap.jpg -------------------------------------------------------------------------------- /img/setState.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/changcheng1/react18-source-implementation/HEAD/img/setState.png -------------------------------------------------------------------------------- /img/taskQueue.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/changcheng1/react18-source-implementation/HEAD/img/taskQueue.jpeg -------------------------------------------------------------------------------- /img/timeSlice.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/changcheng1/react18-source-implementation/HEAD/img/timeSlice.jpeg -------------------------------------------------------------------------------- /img/completeWork.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/changcheng1/react18-source-implementation/HEAD/img/completeWork.png -------------------------------------------------------------------------------- /img/createRoot.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/changcheng1/react18-source-implementation/HEAD/img/createRoot.jpeg -------------------------------------------------------------------------------- /img/domDiff_move.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/changcheng1/react18-source-implementation/HEAD/img/domDiff_move.jpg -------------------------------------------------------------------------------- /img/eventBubble.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/changcheng1/react18-source-implementation/HEAD/img/eventBubble.png -------------------------------------------------------------------------------- /img/fiberAnverse.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/changcheng1/react18-source-implementation/HEAD/img/fiberAnverse.png -------------------------------------------------------------------------------- /img/fiberSimple.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/changcheng1/react18-source-implementation/HEAD/img/fiberSimple.png -------------------------------------------------------------------------------- /img/fiberUpdate.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/changcheng1/react18-source-implementation/HEAD/img/fiberUpdate.jpg -------------------------------------------------------------------------------- /img/firstRender.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/changcheng1/react18-source-implementation/HEAD/img/firstRender.jpg -------------------------------------------------------------------------------- /img/lifeoffram.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/changcheng1/react18-source-implementation/HEAD/img/lifeoffram.jpeg -------------------------------------------------------------------------------- /img/lifeofframe.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/changcheng1/react18-source-implementation/HEAD/img/lifeofframe.jpeg -------------------------------------------------------------------------------- /img/moreDomDiff.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/changcheng1/react18-source-implementation/HEAD/img/moreDomDiff.png -------------------------------------------------------------------------------- /img/updatequeue.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/changcheng1/react18-source-implementation/HEAD/img/updatequeue.png -------------------------------------------------------------------------------- /img/dispatch-event.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/changcheng1/react18-source-implementation/HEAD/img/dispatch-event.png -------------------------------------------------------------------------------- /img/renderFiber1.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/changcheng1/react18-source-implementation/HEAD/img/renderFiber1.jpeg -------------------------------------------------------------------------------- /img/renderRootFiber.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/changcheng1/react18-source-implementation/HEAD/img/renderRootFiber.jpg -------------------------------------------------------------------------------- /img/renderWithHook.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/changcheng1/react18-source-implementation/HEAD/img/renderWithHook.jpg -------------------------------------------------------------------------------- /img/requestIdleback.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/changcheng1/react18-source-implementation/HEAD/img/requestIdleback.png -------------------------------------------------------------------------------- /img/singleDomDiff.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/changcheng1/react18-source-implementation/HEAD/img/singleDomDiff.jpg -------------------------------------------------------------------------------- /img/useLayoutEffect.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/changcheng1/react18-source-implementation/HEAD/img/useLayoutEffect.png -------------------------------------------------------------------------------- /img/useStateMount.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/changcheng1/react18-source-implementation/HEAD/img/useStateMount.png -------------------------------------------------------------------------------- /src/react-dom-bindings/src/events/EventSystemFlags.js: -------------------------------------------------------------------------------- 1 | export const IS_CAPTURE_PHASE = 1 << 2; 2 | // 0b0001 3 | // 0b0100 -------------------------------------------------------------------------------- /img/collectEffectList.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/changcheng1/react18-source-implementation/HEAD/img/collectEffectList.jpg -------------------------------------------------------------------------------- /img/createUpdateQueue.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/changcheng1/react18-source-implementation/HEAD/img/createUpdateQueue.png -------------------------------------------------------------------------------- /img/fiberConstructor.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/changcheng1/react18-source-implementation/HEAD/img/fiberConstructor.png -------------------------------------------------------------------------------- /img/reactelement-tree.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/changcheng1/react18-source-implementation/HEAD/img/reactelement-tree.png -------------------------------------------------------------------------------- /img/registerEventName.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/changcheng1/react18-source-implementation/HEAD/img/registerEventName.png -------------------------------------------------------------------------------- /img/scheduleCallback.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/changcheng1/react18-source-implementation/HEAD/img/scheduleCallback.jpg -------------------------------------------------------------------------------- /img/completely_twoTree.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/changcheng1/react18-source-implementation/HEAD/img/completely_twoTree.jpeg -------------------------------------------------------------------------------- /img/memoizedStateQueue.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/changcheng1/react18-source-implementation/HEAD/img/memoizedStateQueue.png -------------------------------------------------------------------------------- /img/reactfiberworkloop.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/changcheng1/react18-source-implementation/HEAD/img/reactfiberworkloop.png -------------------------------------------------------------------------------- /img/eventuse_1665894712077.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/changcheng1/react18-source-implementation/HEAD/img/eventuse_1665894712077.jpeg -------------------------------------------------------------------------------- /img/di_gui_gou_jian_fiber_shu.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/changcheng1/react18-source-implementation/HEAD/img/di_gui_gou_jian_fiber_shu.jpeg -------------------------------------------------------------------------------- /img/mountReducer_1678679227351.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/changcheng1/react18-source-implementation/HEAD/img/mountReducer_1678679227351.png -------------------------------------------------------------------------------- /img/queuepending_1644750048819.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/changcheng1/react18-source-implementation/HEAD/img/queuepending_1644750048819.png -------------------------------------------------------------------------------- /img/extractEvents2_1678678999496.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/changcheng1/react18-source-implementation/HEAD/img/extractEvents2_1678678999496.png -------------------------------------------------------------------------------- /img/liu_lan_qi_zhen_1643277067067.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/changcheng1/react18-source-implementation/HEAD/img/liu_lan_qi_zhen_1643277067067.jpeg -------------------------------------------------------------------------------- /img/useLayoutEffect_1666851036689.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/changcheng1/react18-source-implementation/HEAD/img/useLayoutEffect_1666851036689.jpeg -------------------------------------------------------------------------------- /img/zui_xiao_dui_1_1643275468911.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/changcheng1/react18-source-implementation/HEAD/img/zui_xiao_dui_1_1643275468911.jpeg -------------------------------------------------------------------------------- /img/dan_jie_dian_diff_1678677255463.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/changcheng1/react18-source-implementation/HEAD/img/dan_jie_dian_diff_1678677255463.png -------------------------------------------------------------------------------- /img/flushPassiveEffects_1666783551920.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/changcheng1/react18-source-implementation/HEAD/img/flushPassiveEffects_1666783551920.jpeg -------------------------------------------------------------------------------- /img/initializeUpdateQueue_1664039386818.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/changcheng1/react18-source-implementation/HEAD/img/initializeUpdateQueue_1664039386818.png -------------------------------------------------------------------------------- /img/processDispatchQueue1_1678679016915.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/changcheng1/react18-source-implementation/HEAD/img/processDispatchQueue1_1678679016915.png -------------------------------------------------------------------------------- /src/react-dom-bindings/src/client/setTextContent.js: -------------------------------------------------------------------------------- 1 | 2 | function setTextContent(node, text) { 3 | node.textContent = text; 4 | } 5 | export default setTextContent; -------------------------------------------------------------------------------- /img/shi_jian_qie_pian_diao_du_1643278352662.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/changcheng1/react18-source-implementation/HEAD/img/shi_jian_qie_pian_diao_du_1643278352662.jpeg -------------------------------------------------------------------------------- /src/shared/ReactSharedInternals.js: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | const ReactSharedInternals = React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED 3 | export default ReactSharedInternals 4 | 5 | -------------------------------------------------------------------------------- /src/react-dom-bindings/src/events/getEventTarget.js: -------------------------------------------------------------------------------- 1 | function getEventTarget(nativeEvent) { 2 | const target = nativeEvent.target || nativeEvent.srcElement || window; 3 | return target; 4 | } 5 | export default getEventTarget; -------------------------------------------------------------------------------- /src/react-dom-bindings/src/client/DOMPropertyOperations.js: -------------------------------------------------------------------------------- 1 | 2 | export function setValueForProperty(node, name, value) { 3 | if (value === null) { 4 | node.removeAttribute(name); 5 | } else { 6 | node.setAttribute(name, value); 7 | } 8 | } -------------------------------------------------------------------------------- /src/react-dom-bindings/src/client/CSSPropertyOperations.js: -------------------------------------------------------------------------------- 1 | 2 | export function setValueForStyles(node, styles) { 3 | const { style } = node; 4 | //styles={ color: "red" } 5 | for (const styleName in styles) { 6 | if (styles.hasOwnProperty(styleName)) { 7 | const styleValue = styles[styleName]; 8 | style[styleName] = styleValue; 9 | } 10 | } 11 | } -------------------------------------------------------------------------------- /src/react-dom-bindings/src/events/EventListener.js: -------------------------------------------------------------------------------- 1 | 2 | 3 | export function addEventCaptureListener(target, eventType, listener) { 4 | target.addEventListener(eventType, listener, true); 5 | return listener; 6 | } 7 | export function addEventBubbleListener(target, eventType, listener) { 8 | target.addEventListener(eventType, listener, false); 9 | return listener; 10 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # production 12 | /build 13 | 14 | # misc 15 | .DS_Store 16 | .env.local 17 | .env.development.local 18 | .env.test.local 19 | .env.production.local 20 | 21 | npm-debug.log* 22 | yarn-debug.log* 23 | yarn-error.log* 24 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | react18.2 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /src/react/index.js: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: changcheng 364000100@#qq.com 3 | * @Date: 2025-10-24 11:20:15 4 | * @LastEditors: changcheng 364000100@#qq.com 5 | * @LastEditTime: 2025-10-24 14:30:43 6 | * @FilePath: /myself-space/mini_React/src/react/index.js 7 | */ 8 | export { 9 | __SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED, 10 | useReducer, 11 | useState, 12 | useEffect, 13 | useLayoutEffect, 14 | useRef, 15 | } from "./src/React"; 16 | -------------------------------------------------------------------------------- /src/react-reconciler/src/Scheduler.js: -------------------------------------------------------------------------------- 1 | import * as Scheduler from 'scheduler'; 2 | export const scheduleCallback = Scheduler.unstable_scheduleCallback; 3 | export const shouldYield = Scheduler.unstable_shouldYield; 4 | export const ImmediatePriority = Scheduler.unstable_ImmediatePriority; 5 | export const UserBlockingPriority = Scheduler.unstable_UserBlockingPriority; 6 | export const NormalPriority = Scheduler.unstable_NormalPriority; 7 | export const IdlePriority = Scheduler.unstable_IdlePriority; 8 | export const cancelCallback = Scheduler.unstable_cancelCallback; 9 | export const now = Scheduler.now; -------------------------------------------------------------------------------- /src/shared/logger.js: -------------------------------------------------------------------------------- 1 | import * as ReactWorkTags from "react-reconciler/src/ReactWorkTags"; 2 | const ReactWorkTagsMap = new Map(); 3 | for (let tag in ReactWorkTags) { 4 | ReactWorkTagsMap.set(ReactWorkTags[tag], tag); 5 | } 6 | export default function (prefix, workInProgress) { 7 | let tagValue = workInProgress.tag; 8 | let tagName = ReactWorkTagsMap.get(tagValue); 9 | let str = ` ${tagName} `; 10 | if (tagName === "HostComponent") { 11 | str += ` ${workInProgress.type}`; 12 | } else if (tagName === "HostText") { 13 | str += ` ${workInProgress.pendingProps}`; 14 | } 15 | console.log(`${prefix} ${str}`); 16 | } 17 | 18 | let indent = { number: 0 }; 19 | export { indent }; 20 | -------------------------------------------------------------------------------- /src/react-dom-bindings/src/events/EventRegistry.js: -------------------------------------------------------------------------------- 1 | export const allNativeEvents = new Set(); 2 | /** 3 | * 注册两个阶段的事件 4 | * 当我在页面中触发click事件的时候,会走事件处理函数 5 | * 事件处理函数需要找到DOM元素对应的要执行React事件 onClick onClickCapture 6 | * @param {*} registrationName React事件名 onClick 7 | * @param {*} dependencies 原生事件数组 [click] 8 | */ 9 | export function registerTwoPhaseEvent(registrationName, dependencies) { 10 | //注册冒泡事件的对应关系 11 | registerDirectEvent(registrationName, dependencies); 12 | //注意捕获事件的对应的关系 13 | registerDirectEvent(registrationName + 'Capture', dependencies); 14 | } 15 | export function registerDirectEvent(registrationName, dependencies) { 16 | for (let i = 0; i < dependencies.length; i++) { 17 | allNativeEvents.add(dependencies[i]);//click 18 | } 19 | } -------------------------------------------------------------------------------- /vite.config.js: -------------------------------------------------------------------------------- 1 | import { defineConfig } from "vite"; 2 | import react from "@vitejs/plugin-react"; 3 | import path from "path"; 4 | export default defineConfig({ 5 | server: { 6 | port: 8000, 7 | }, 8 | resolve: { 9 | alias: { 10 | react: path.posix.resolve("src/react"), 11 | "react-dom": path.posix.resolve("src/react-dom"), // 12 | "react-dom-bindings": path.posix.resolve("src/react-dom-bindings"), 13 | "react-reconciler": path.posix.resolve("src/react-reconciler"), 14 | scheduler: path.posix.resolve("src/scheduler"), 15 | shared: path.posix.resolve("src/shared"), 16 | }, 17 | }, 18 | plugins: [react()], 19 | optimizeDeps: { 20 | force: true, 21 | }, 22 | }); 23 | // window和linux路分割符不一样 window \ linux / 24 | -------------------------------------------------------------------------------- /src/react/src/React.js: -------------------------------------------------------------------------------- 1 | /** 2 | * React Core Package - Main Entry Point 3 | * 4 | * This module exports the public React API including hooks and internal utilities. 5 | * It serves as the main interface that developers interact with when using React. 6 | * 7 | * @module React 8 | */ 9 | 10 | import { 11 | useReducer, 12 | useState, 13 | useEffect, 14 | useLayoutEffect, 15 | useRef, 16 | } from "./ReactHooks"; 17 | import ReactSharedInternals from "./ReactSharedInternals"; 18 | 19 | // Export public React API 20 | export { 21 | useReducer, 22 | useState, 23 | useEffect, 24 | useLayoutEffect, 25 | useRef, 26 | // Internal utilities for React DevTools and other React packages 27 | ReactSharedInternals as __SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED, 28 | }; 29 | -------------------------------------------------------------------------------- /src/shared/isArray.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Array Type Check Utility - Reliable Array Detection 3 | * 4 | * This module provides a reference to Array.isArray for consistent array 5 | * type checking throughout React. Array.isArray is the most reliable way 6 | * to check if a value is an array, as it works correctly across different 7 | * execution contexts (frames, workers, etc.). 8 | * 9 | * This is preferred over instanceof Array or Object.prototype.toString 10 | * checks for cross-frame compatibility. 11 | * 12 | * @module isArray 13 | */ 14 | 15 | /** 16 | * Array.isArray reference 17 | * 18 | * Determines whether the passed value is an Array. 19 | * Returns true if the value is an Array; otherwise, false. 20 | * 21 | * Usage: isArray(value) 22 | */ 23 | const { isArray } = Array; 24 | 25 | export default isArray; 26 | -------------------------------------------------------------------------------- /src/shared/assign.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Object Assign Utility - Cross-Browser Object Property Assignment 3 | * 4 | * This module provides a reference to Object.assign for use throughout React. 5 | * Object.assign is used to copy properties from source objects to a target object. 6 | * 7 | * In modern environments, this uses the native Object.assign implementation. 8 | * This module provides a centralized place to handle any polyfills if needed 9 | * for older browser support. 10 | * 11 | * @module assign 12 | */ 13 | 14 | /** 15 | * Object.assign reference 16 | * 17 | * Copies all enumerable own properties from one or more source objects 18 | * to a target object and returns the target object. 19 | * 20 | * Usage: assign(target, source1, source2, ...) 21 | */ 22 | const { assign } = Object; 23 | 24 | export default assign; 25 | -------------------------------------------------------------------------------- /src/shared/ReactSymbols.js: -------------------------------------------------------------------------------- 1 | /** 2 | * React Symbols - Type Identification for React Elements 3 | * 4 | * This module defines symbols used throughout React to identify different 5 | * types of objects and elements. Symbols provide a way to create unique 6 | * identifiers that cannot be accidentally duplicated. 7 | * 8 | * These symbols are used for: 9 | * - Type checking React elements 10 | * - Security (preventing XSS through fake React elements) 11 | * - Internal React object identification 12 | * 13 | * @module ReactSymbols 14 | */ 15 | 16 | /** 17 | * REACT_ELEMENT_TYPE Symbol 18 | * 19 | * Identifies objects as React elements (virtual DOM nodes). 20 | * This symbol is attached to all React elements created by JSX or React.createElement. 21 | * It helps React distinguish between real React elements and plain objects, 22 | * which is important for security and proper rendering. 23 | */ 24 | export const REACT_ELEMENT_TYPE = Symbol.for("react.element"); 25 | -------------------------------------------------------------------------------- /src/react-dom-bindings/src/client/ReactDOMComponentTree.js: -------------------------------------------------------------------------------- 1 | const randomKey = Math.random().toString(36).slice(2); 2 | const internalInstanceKey = '__reactFiber$' + randomKey; 3 | const internalPropsKey = "__reactProps$" + randomKey; 4 | /** 5 | * 从真实的DOM节点上获取它对应的fiber节点 6 | * @param {*} targetNode 7 | */ 8 | export function getClosestInstanceFromNode(targetNode) { 9 | const targetInst = targetNode[internalInstanceKey] 10 | if (targetInst) { 11 | return targetInst; 12 | } 13 | return null; 14 | //如果真实DOM上没有fiber,就不要返回undefined,而是要返回null 15 | } 16 | /** 17 | * 提前缓存fiber节点的实例到DOM节点上 18 | * @param {*} hostInst fiber实例 19 | * @param {*} node 真实DOM 20 | */ 21 | export function precacheFiberNode(hostInst, node) { 22 | node[internalInstanceKey] = hostInst; 23 | } 24 | 25 | export function updateFiberProps(node, props) { 26 | node[internalPropsKey] = props; 27 | } 28 | export function getFiberCurrentPropsFromNode(node) { 29 | return node[internalPropsKey] || null; 30 | } -------------------------------------------------------------------------------- /src/react-dom-bindings/src/events/getListener.js: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: changcheng 364000100@#qq.com 3 | * @Date: 2025-10-24 11:20:15 4 | * @LastEditors: changcheng 364000100@#qq.com 5 | * @LastEditTime: 2025-10-24 14:31:12 6 | * @FilePath: /myself-space/mini_React/src/react-dom-bindings/src/events/getListener.js 7 | * @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE 8 | */ 9 | import { getFiberCurrentPropsFromNode } from "../client/ReactDOMComponentTree"; 10 | 11 | /** 12 | * get the callback function of the fiber 13 | * @param {*} inst 14 | * @param {*} registrationName 15 | */ 16 | export default function getListener(inst, registrationName) { 17 | const { stateNode } = inst; 18 | if (stateNode === null) return null; 19 | const props = getFiberCurrentPropsFromNode(stateNode); 20 | if (props === null) return null; 21 | const listener = props[registrationName]; //props.onClick 22 | return listener; 23 | } 24 | -------------------------------------------------------------------------------- /src/shared/ReactFeatureFlags.js: -------------------------------------------------------------------------------- 1 | /** 2 | * React Feature Flags - Runtime Feature Configuration 3 | * 4 | * This module contains feature flags that control the behavior of React features. 5 | * Feature flags allow React to enable/disable features at runtime, which is useful 6 | * for gradual rollouts, A/B testing, and maintaining backward compatibility. 7 | * 8 | * In a production React build, these flags are typically replaced with constants 9 | * during the build process to enable dead code elimination. 10 | * 11 | * @module ReactFeatureFlags 12 | */ 13 | 14 | /** 15 | * Allow Concurrent by Default 16 | * 17 | * Controls whether concurrent rendering features are enabled by default. 18 | * When true, React will use concurrent rendering mode, which enables: 19 | * - Time slicing and interruptible rendering 20 | * - Priority-based scheduling 21 | * - Concurrent features like Suspense and Transitions 22 | * 23 | * This flag is essential for React 18's concurrent features. 24 | */ 25 | export const allowConcurrentByDefault = true; 26 | -------------------------------------------------------------------------------- /src/main.jsx: -------------------------------------------------------------------------------- 1 | /** 2 | * React 18 Implementation - Main Entry Point 3 | * 4 | * This file demonstrates the basic usage of our React 18 implementation, 5 | * showcasing component rendering, state management with hooks, and event handling. 6 | * 7 | * @author React 18 Implementation Team 8 | * @version 1.0.0 9 | */ 10 | import * as React from "react"; 11 | import { createRoot } from "react-dom/client"; 12 | 13 | /** 14 | * Example functional component demonstrating useState hook and event handling 15 | * @returns {JSX.Element} A clickable div that displays and increments a counter 16 | */ 17 | function FunctionComponent() { 18 | const [numbers, setNumbers] = React.useReducer((state, action) => { 19 | return state + action; 20 | }, 0); 21 | React.useEffect(() => { 22 | console.log("numbers changed"); 23 | }, [numbers]); 24 | return
setNumbers(1)}>{numbers}
; 25 | } 26 | 27 | // Create root container and render the application 28 | const root = createRoot(document.getElementById("root")); 29 | root.render(); 30 | -------------------------------------------------------------------------------- /src/react/src/ReactSharedInternals.js: -------------------------------------------------------------------------------- 1 | /** 2 | * React Shared Internals - Internal API Access Point 3 | * 4 | * This module provides access to React's internal APIs that need to be shared 5 | * between different React packages (react, react-dom, react-reconciler, etc.). 6 | * It serves as a controlled interface for internal communication. 7 | * 8 | * These internals are: 9 | * - Not part of the public API 10 | * - Subject to change between versions 11 | * - Used for coordination between React packages 12 | * - Accessed via the __SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED export 13 | * 14 | * @module ReactSharedInternals 15 | */ 16 | 17 | import ReactCurrentDispatcher from "./ReactCurrentDispatcher"; 18 | 19 | /** 20 | * React Shared Internals Object 21 | * 22 | * Contains internal React APIs that need to be accessible across packages. 23 | * Currently includes the dispatcher system for hooks management. 24 | */ 25 | const ReactSharedInternals = { 26 | ReactCurrentDispatcher, // Hooks dispatcher management 27 | }; 28 | 29 | export default ReactSharedInternals; 30 | -------------------------------------------------------------------------------- /src/react/src/ReactCurrentDispatcher.js: -------------------------------------------------------------------------------- 1 | /** 2 | * React Current Dispatcher - Hook Dispatcher Management 3 | * 4 | * This module manages the current hooks dispatcher, which determines which 5 | * implementation of hooks (mount vs update) should be used during rendering. 6 | * The dispatcher is switched between different phases of the component lifecycle. 7 | * 8 | * Key concepts: 9 | * - Mount phase: First render, creates new hook objects 10 | * - Update phase: Re-renders, updates existing hook objects 11 | * - The dispatcher ensures hooks are called in the correct context 12 | * 13 | * @module ReactCurrentDispatcher 14 | */ 15 | 16 | /** 17 | * React Current Dispatcher Object 18 | * 19 | * Holds the current hooks dispatcher implementation. This is switched between: 20 | * - HooksDispatcherOnMount: During initial component render 21 | * - HooksDispatcherOnUpdate: During component re-renders 22 | * - null: When not rendering (hooks should not be called) 23 | */ 24 | const ReactCurrentDispatcher = { 25 | current: null, // Current dispatcher implementation 26 | }; 27 | 28 | export default ReactCurrentDispatcher; 29 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 React 18 Source Implementation 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /src/react-reconciler/src/ReactHookEffectTags.js: -------------------------------------------------------------------------------- 1 | /** 2 | * React Hook Effect Tags - Effect Type Classification 3 | * 4 | * This module defines binary flags for classifying different types of hook effects. 5 | * These tags are used to determine when and how effects should be executed during 6 | * the commit phase. The binary flag system allows efficient combination and 7 | * checking of multiple effect types. 8 | * 9 | * Effect execution timing: 10 | * - Layout effects: Run synchronously after DOM mutations (useLayoutEffect) 11 | * - Passive effects: Run asynchronously after paint (useEffect) 12 | * 13 | * @module ReactHookEffectTags 14 | */ 15 | 16 | export const NoFlags = 0b0000; // No effect flags 17 | 18 | // Core effect control flag 19 | export const HasEffect = 0b0001; // 1 - Effect should be executed (must be present) 20 | 21 | // Effect timing flags 22 | export const Layout = 0b0100; // 4 - Layout effect (useLayoutEffect) 23 | // Runs synchronously before browser paint 24 | // Similar to microtask timing 25 | 26 | export const Passive = 0b1000; // 8 - Passive effect (useEffect) 27 | // Runs asynchronously after browser paint 28 | // Similar to macrotask timing 29 | -------------------------------------------------------------------------------- /src/react-reconciler/src/ReactFiberReconciler.js: -------------------------------------------------------------------------------- 1 | import { createFiberRoot } from "./ReactFiberRoot"; 2 | import { createUpdate, enqueueUpdate } from "./ReactFiberClassUpdateQueue"; 3 | import { 4 | scheduleUpdateOnFiber, 5 | requestUpdateLane, 6 | requestEventTime, 7 | } from "./ReactFiberWorkLoop"; 8 | export function createContainer(containerInfo) { 9 | return createFiberRoot(containerInfo); 10 | } 11 | /** 12 | * Update container, convert virtual DOM element to real DOM and insert into container 13 | * @param {*} element Virtual DOM 14 | * @param {*} container DOM container FiberRootNode containerInfo div#root 15 | */ 16 | export function updateContainer(element, container) { 17 | // Get the current root fiber 18 | const current = container.current; 19 | const eventTime = requestEventTime(); 20 | // Request an update lane 16 21 | const lane = requestUpdateLane(current); 22 | // Create update 23 | const update = createUpdate(lane); 24 | // Virtual DOM to be updated 25 | update.payload = { element }; //h1 26 | // Add this update object to the update queue of the current root Fiber, return the root node 27 | const root = enqueueUpdate(current, update, lane); 28 | scheduleUpdateOnFiber(root, current, lane, eventTime); 29 | } 30 | -------------------------------------------------------------------------------- /src/react-reconciler/src/ReactWorkTags.js: -------------------------------------------------------------------------------- 1 | /** 2 | * React Work Tags - Fiber Node Type Definitions 3 | * 4 | * This module defines constants for different types of React fiber nodes. 5 | * Each fiber node has a 'tag' property that identifies what kind of component 6 | * or element it represents. This enables React to handle different node types 7 | * appropriately during reconciliation and rendering. 8 | * 9 | * The tag system allows React to: 10 | * - Determine how to process each fiber during work loops 11 | * - Apply appropriate lifecycle methods and effects 12 | * - Handle different rendering strategies for different component types 13 | * - Optimize performance based on component characteristics 14 | * 15 | * @module ReactWorkTags 16 | */ 17 | 18 | // Component types 19 | export const FunctionComponent = 0; // Function components (hooks-based) 20 | export const ClassComponent = 1; // Class components (lifecycle-based) 21 | export const IndeterminateComponent = 2; // Components whose type is not yet determined 22 | 23 | // Host types (DOM-related) 24 | export const HostRoot = 3; // Root container node (div#root) 25 | export const HostComponent = 5; // Native DOM elements (span, div, h1, etc.) 26 | export const HostText = 6; // Text nodes (pure text content) 27 | -------------------------------------------------------------------------------- /src/react-dom-bindings/src/events/DOMEventProperties.js: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: changcheng 3 | * @LastEditTime: 2023-09-21 18:28:21 4 | */ 5 | import { registerTwoPhaseEvent } from "./EventRegistry"; 6 | const simpleEventPluginEvents = ["click"]; 7 | export const topLevelEventsToReactNames = new Map(); 8 | function registerSimpleEvent(domEventName, reactName) { 9 | //onClick在哪里可以取到 10 | //workInProgress.pendingProps=React元素或者说虚拟DOM.props 11 | //const newProps = workInProgress.pendingProps; 12 | //在源码里 让真实DOM元素 updateFiberProps(domElement, props); 13 | //const internalPropsKey = "__reactProps$" + randomKey; 14 | //真实DOM元素[internalPropsKey] = props; props.onClick 15 | //把原生事件名和处理函数的名字进行映射或者说绑定,click=>onClick 16 | topLevelEventsToReactNames.set(domEventName, reactName); 17 | registerTwoPhaseEvent(reactName, [domEventName]); //'onClick' ['click'] 18 | } 19 | export function registerSimpleEvents() { 20 | for (let i = 0; i < simpleEventPluginEvents.length; i++) { 21 | const eventName = simpleEventPluginEvents[i]; //click 22 | const domEventName = eventName.toLowerCase(); //click 23 | const capitalizeEvent = eventName[0].toUpperCase() + eventName.slice(1); // Click 24 | registerSimpleEvent(domEventName, `on${capitalizeEvent}`); //click,onClick 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/scheduler/src/forks/SchedulerPriorities.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Scheduler Priorities - Task Priority Level Definitions 3 | * 4 | * This module defines the priority levels used by React's scheduler to determine 5 | * the order in which tasks should be executed. Higher priority tasks can interrupt 6 | * lower priority ones, ensuring that critical updates (like user interactions) 7 | * are processed quickly. 8 | * 9 | * Priority hierarchy (from highest to lowest): 10 | * 1. Immediate - Critical tasks that must run synchronously 11 | * 2. UserBlocking - User interactions that should feel responsive 12 | * 3. Normal - Regular updates and state changes 13 | * 4. Low - Non-critical updates that can be deferred 14 | * 5. Idle - Background work that runs when nothing else is pending 15 | * 16 | * @module SchedulerPriorities 17 | */ 18 | 19 | // Priority level constants (lower numbers = higher priority) 20 | export const NoPriority = 0; // No priority assigned 21 | export const ImmediatePriority = 1; // Immediate execution (highest priority) 22 | export const UserBlockingPriority = 2; // User interactions (clicks, input) 23 | export const NormalPriority = 3; // Normal updates (default priority) 24 | export const LowPriority = 4; // Low priority updates (can be deferred) 25 | export const IdlePriority = 5; // Idle work (lowest priority) 26 | -------------------------------------------------------------------------------- /src/react/src/jsx/ReactJSXElement.js: -------------------------------------------------------------------------------- 1 | import hasOwnProperty from 'shared/hasOwnProperty'; 2 | import { REACT_ELEMENT_TYPE } from 'shared/ReactSymbols'; 3 | const RESERVED_PROPS = { 4 | key: true, 5 | ref: true, 6 | __self: true, 7 | __source: true 8 | } 9 | 10 | function hasValidRef(config) { 11 | return config.ref !== undefined; 12 | } 13 | function ReactElement(type, key, ref, props) { 14 | return {//这就是React元素,也被称为虚拟DOM 15 | $$typeof: REACT_ELEMENT_TYPE, 16 | type,//h1 span 17 | key,//唯一标识 18 | ref,//后面再讲,是用来获取真实DOM元素 19 | props//属性 children,style,id 20 | } 21 | } 22 | //React17以前老版的转换函数中key 是放在config里的,第三个参数放children 23 | //React17之后新版的转换函数中key是在第三个参数中的,children是放在config里的 24 | export function jsxDEV(type, config, maybeKey) { 25 | let propName;//属性名 26 | const props = {};//属性对象 27 | let key = null;//每个虚拟DOM可以有一个可选的key属性,用来区分一个父节点下的不同子节点 28 | let ref = null;//引入,后面可以通过这实现获取真实DOM的需求 29 | if (typeof maybeKey !== 'undefined') { 30 | key = maybeKey; 31 | } 32 | if (hasValidRef(config)) { 33 | ref = config.ref; 34 | } 35 | for (propName in config) { 36 | if (hasOwnProperty.call(config, propName) 37 | && !RESERVED_PROPS.hasOwnProperty(propName) 38 | ) { 39 | props[propName] = config[propName] 40 | } 41 | } 42 | return ReactElement(type, key, ref, props); 43 | } -------------------------------------------------------------------------------- /src/react-reconciler/src/ReactFiberSyncTaskQueue.js: -------------------------------------------------------------------------------- 1 | import { 2 | DiscreteEventPriority, 3 | getCurrentUpdatePriority, 4 | setCurrentUpdatePriority, 5 | } from "./ReactEventPriorities"; 6 | 7 | // Synchronous queue 8 | let syncQueue = null; 9 | // Whether the synchronous queue is being executed 10 | let isFlushingSyncQueue = false; 11 | 12 | export function scheduleSyncCallback(callback) { 13 | if (syncQueue === null) { 14 | syncQueue = [callback]; 15 | } else { 16 | syncQueue.push(callback); 17 | } 18 | } 19 | 20 | export function flushSyncCallbacks() { 21 | if (!isFlushingSyncQueue && syncQueue !== null) { 22 | isFlushingSyncQueue = true; 23 | let i = 0; 24 | // Store the current update priority 25 | const previousUpdatePriority = getCurrentUpdatePriority(); 26 | try { 27 | const isSync = true; 28 | const queue = syncQueue; 29 | // Set priority to synchronous priority 30 | setCurrentUpdatePriority(DiscreteEventPriority); 31 | for (; i < queue.length; i++) { 32 | let callback = queue[i]; 33 | do { 34 | callback = callback(isSync); 35 | } while (callback !== null); 36 | } 37 | syncQueue = null; 38 | } finally { 39 | setCurrentUpdatePriority(previousUpdatePriority); 40 | isFlushingSyncQueue = false; 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/react-reconciler/src/ReactFiberFlags.js: -------------------------------------------------------------------------------- 1 | /** 2 | * React Fiber Flags - Side Effect Tracking System 3 | * 4 | * This module defines binary flags used to track side effects on fiber nodes. 5 | * Flags are used during the render phase to mark what operations need to be 6 | * performed during the commit phase. Using binary flags allows efficient 7 | * bitwise operations for combining and checking multiple effects. 8 | * 9 | * The flag system enables: 10 | * - Efficient side effect tracking during reconciliation 11 | * - Batching of similar operations during commit 12 | * - Quick checks for specific types of work needed 13 | * - Minimal memory overhead for effect tracking 14 | * 15 | * @module ReactFiberFlags 16 | */ 17 | 18 | // Basic flags for different types of side effects 19 | export const NoFlags = 0b00000000000000000000000000; // 0 - No side effects 20 | export const Placement = 0b00000000000000000000000010; // 2 - Node needs to be inserted 21 | export const Update = 0b00000000000000000000000100; // 4 - Node needs to be updated 22 | export const ChildDeletion = 0b00000000000000000000001000; // 8 - Child nodes need deletion 23 | export const Ref = 0b00000000000000000100000000; // 256 - Ref needs to be attached/detached 24 | 25 | // Composite masks for efficient batch processing 26 | export const MutationMask = Placement | Update | ChildDeletion | Ref; // All mutation effects 27 | 28 | // Effect-related flags 29 | export const Passive = 0b00000000000000010000000000; // 1024 - Has passive effects (useEffect) 30 | export const LayoutMask = Update; // Effects that run during layout phase 31 | -------------------------------------------------------------------------------- /src/react-dom-bindings/src/events/plugins/SimpleEventPlugin.js: -------------------------------------------------------------------------------- 1 | import { registerSimpleEvents, topLevelEventsToReactNames } from '../DOMEventProperties'; 2 | import { IS_CAPTURE_PHASE } from '../EventSystemFlags'; 3 | import { accumulateSinglePhaseListeners } from '../DOMPluginEventSystem'; 4 | import { SyntheticMouseEvent } from '../SyntheticEvent'; 5 | /** 6 | * 把要执行回调函数添加到dispatchQueue中 7 | * @param {*} dispatchQueue 派发队列,里面放置我们的监听函数 8 | * @param {*} domEventName DOM事件名 click 9 | * @param {*} targetInst 目标fiber 10 | * @param {*} nativeEvent 原生事件 11 | * @param {*} nativeEventTarget 原生事件源 12 | * @param {*} eventSystemFlags 事件系统标题 0 表示冒泡 4表示捕获 13 | * @param {*} targetContainer 目标容器 div#root 14 | */ 15 | function extractEvents( 16 | dispatchQueue, 17 | domEventName, 18 | targetInst, 19 | nativeEvent, 20 | nativeEventTarget,//click => onClick 21 | eventSystemFlags, 22 | targetContainer) { 23 | const reactName = topLevelEventsToReactNames.get(domEventName);//click=>onClick 24 | let SyntheticEventCtor;//合成事件的构建函数 25 | switch (domEventName) { 26 | case 'click': 27 | SyntheticEventCtor = SyntheticMouseEvent; 28 | break; 29 | default: 30 | break; 31 | } 32 | const isCapturePhase = (eventSystemFlags & IS_CAPTURE_PHASE) !== 0;//是否是捕获阶段 33 | const listeners = accumulateSinglePhaseListeners( 34 | targetInst, 35 | reactName, 36 | nativeEvent.type, 37 | isCapturePhase 38 | ); 39 | //如果有要执行的监听函数的话[onClickCapture,onClickCapture]=[ChildCapture,ParentCapture] 40 | if (listeners.length > 0) { 41 | const event = new SyntheticEventCtor( 42 | reactName, domEventName, null, nativeEvent, nativeEventTarget); 43 | dispatchQueue.push({ 44 | event,//合成事件实例 45 | listeners//监听函数数组 46 | }); 47 | } 48 | } 49 | 50 | export { registerSimpleEvents as registerEvents, extractEvents } -------------------------------------------------------------------------------- /markdown/dfs.md: -------------------------------------------------------------------------------- 1 | 5 | 6 | ### Fiber uses depth-first traversal (last in, first out) 7 | 8 | Depth-first search is abbreviated as DFS, which stands for Depth First Search 9 | 10 | The process is to go as deep as possible along each possible branch path, and each node can only be visited once 11 | Application scenarios 12 | 13 | React virtual DOM construction 14 | 15 | React fiber tree construction 16 | 17 | Advantages: Low memory usage, Disadvantages: Inefficient when depth is very deep 18 | 19 | ```javaScript 20 | let root = { 21 | name: "A", 22 | children: [ 23 | { 24 | name: "B", 25 | children: [{ name: "B1" }, { name: "B2" }], 26 | }, 27 | { 28 | name: "C", 29 | children: [{ name: "C1" }, { name: "C2" }], 30 | }, 31 | ], 32 | }; 33 | function dfs(node) { 34 | console.log(node.name); 35 | node.children?.forEach(dfs); 36 | } 37 | dfs(root); 38 | ``` 39 | 40 | ### Breadth-first (BFS) (first in, first out) 41 | 42 | Breadth-first search algorithm (also known as breadth-first search), its full English name is Breadth First Search 43 | 44 | The algorithm first searches all vertices at distance k, then searches other vertices at distance k+1 45 | 46 | Advantages: Small search depth, Disadvantages: High memory usage 47 | 48 | ```javaScript 49 | let root = { 50 | name: "A", 51 | children: [ 52 | { 53 | name: "B", 54 | children: [{ name: "B1" }, { name: "B2" }], 55 | }, 56 | { 57 | name: "C", 58 | children: [{ name: "C1" }, { name: "C2" }], 59 | }, 60 | ], 61 | }; 62 | function bfs(node) { 63 | const stack = []; 64 | stack.push(node); 65 | let current; 66 | while ((current = stack.shift())) { 67 | console.log(current.name); 68 | current.children?.forEach(child => { 69 | stack.push(child); 70 | }); 71 | } 72 | } 73 | bfs(root) 74 | ``` 75 | -------------------------------------------------------------------------------- /src/react-dom/src/client/ReactDOMRoot.js: -------------------------------------------------------------------------------- 1 | /** 2 | * React DOM Root - Client-side Rendering Entry Point 3 | * 4 | * This module implements the new React 18 createRoot API, which replaces 5 | * the legacy ReactDOM.render method. It provides better support for 6 | * concurrent features and improved error handling. 7 | * 8 | * @module ReactDOMRoot 9 | */ 10 | 11 | import { 12 | createContainer, 13 | updateContainer, 14 | } from "react-reconciler/src/ReactFiberReconciler"; 15 | import { listenToAllSupportedEvents } from "react-dom-bindings/src/events/DOMPluginEventSystem"; 16 | 17 | /** 18 | * ReactDOMRoot Constructor 19 | * 20 | * Creates a ReactDOMRoot instance that wraps the internal fiber root. 21 | * This provides the public API for rendering React elements. 22 | * 23 | * @param {FiberRoot} internalRoot - Internal fiber root node 24 | */ 25 | function ReactDOMRoot(internalRoot) { 26 | this._internalRoot = internalRoot; 27 | } 28 | 29 | /** 30 | * Render Method 31 | * 32 | * Renders React elements into the root container. This method can be called 33 | * multiple times to update the rendered content. It clears the container 34 | * and renders the new content. 35 | * 36 | * @param {ReactElement} children - React elements to render 37 | */ 38 | ReactDOMRoot.prototype.render = function (children) { 39 | const root = this._internalRoot; 40 | 41 | // Clear existing content (in production, this might be more sophisticated) 42 | root.containerInfo.innerHTML = ""; 43 | 44 | // Update the container with new content 45 | updateContainer(children, root); 46 | }; 47 | 48 | /** 49 | * Create Root 50 | * 51 | * Creates a new React root for the given container element. This is the 52 | * entry point for React 18's concurrent rendering features. 53 | * 54 | * @param {Element} container - DOM element to render into (e.g., div#root) 55 | * @returns {ReactDOMRoot} Root instance with render method 56 | */ 57 | export function createRoot(container) { 58 | // Create the internal fiber root structure 59 | const root = createContainer(container); 60 | 61 | // Set up event delegation for the container 62 | listenToAllSupportedEvents(container); 63 | 64 | // Return public root API 65 | return new ReactDOMRoot(root); 66 | } 67 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react18-source-implementation", 3 | "version": "1.0.0", 4 | "description": "🚀 Complete React 18 implementation from scratch with Fiber architecture, Concurrent features, Hooks system, and Scheduler. Perfect for understanding React internals and modern frontend architecture patterns.", 5 | "main": "src/main.jsx", 6 | "type": "module", 7 | "scripts": { 8 | "dev": "vite", 9 | "build": "vite build", 10 | "preview": "vite preview", 11 | "test": "vitest", 12 | "test:ui": "vitest --ui", 13 | "lint": "eslint src --ext js,jsx --report-unused-disable-directives --max-warnings 0", 14 | "lint:fix": "eslint src --ext js,jsx --fix" 15 | }, 16 | "keywords": [ 17 | "react", 18 | "react18", 19 | "fiber", 20 | "hooks", 21 | "concurrent", 22 | "scheduler", 23 | "frontend", 24 | "javascript", 25 | "source-code", 26 | "implementation", 27 | "tutorial", 28 | "learning", 29 | "architecture", 30 | "performance", 31 | "web-development", 32 | "framework", 33 | "virtual-dom", 34 | "reconciliation", 35 | "time-slicing", 36 | "priority-scheduling" 37 | ], 38 | "author": { 39 | "name": "React 18 Implementation Team", 40 | "email": "contact@react18-impl.dev" 41 | }, 42 | "license": "MIT", 43 | "repository": { 44 | "type": "git", 45 | "url": "https://github.com/username/react18-source-implementation.git" 46 | }, 47 | "bugs": { 48 | "url": "https://github.com/username/react18-source-implementation/issues" 49 | }, 50 | "homepage": "https://github.com/username/react18-source-implementation#readme", 51 | "devDependencies": { 52 | "@types/react": "^18.2.15", 53 | "@types/react-dom": "^18.2.7", 54 | "@vitejs/plugin-react": "^4.0.3", 55 | "eslint": "^8.45.0", 56 | "eslint-plugin-react": "^7.32.2", 57 | "eslint-plugin-react-hooks": "^4.6.0", 58 | "eslint-plugin-react-refresh": "^0.4.3", 59 | "vite": "^4.4.5", 60 | "vitest": "^0.34.0" 61 | }, 62 | "engines": { 63 | "node": ">=16.0.0", 64 | "npm": ">=8.0.0" 65 | }, 66 | "browserslist": { 67 | "production": [ 68 | ">0.2%", 69 | "not dead", 70 | "not op_mini all" 71 | ], 72 | "development": [ 73 | "last 1 chrome version", 74 | "last 1 firefox version", 75 | "last 1 safari version" 76 | ] 77 | } 78 | } -------------------------------------------------------------------------------- /markdown/messageChannel.md: -------------------------------------------------------------------------------- 1 | 5 | ### messageChannel 6 | 7 | 1. React利用 MessageChannel模拟了requestIdleCallback,将回调延迟到绘制操作之后执行 8 | 9 | 2. 源码里会判断如果支持`messageChannel`用`messageChannel`,不支持就用`setTimeout` 10 | 11 | 12 | 13 | ```javaScript 14 | var channel = new MessageChannel(); 15 | var port1 = channel.port1; 16 | var port2 = channel.port2 17 | port1.onmessage = function(event) { 18 | console.log("port1收到来自port2的数据:" + event.data); 19 | } 20 | port2.onmessage = function(event) { 21 | console.log("port2收到来自port1的数据:" + event.data); 22 | } 23 | port1.postMessage("发送给port2"); 24 | port2.postMessage("发送给port1"); 25 | ``` 26 | 27 | ### requestIdleCallback 28 | 29 | `requestIdleCallback`用此方法,判断当前帧是否有空余时间,执行某个任务,在React中,可以模拟任务调度,根据任务优先级不同,根据浏览器剩余时间调度不同的任务,缺点就是浏览器兼容问题了。 30 | 31 | ```javaScript 32 | // 挂起任务 33 | function sleep(duration) { 34 | let time = Date.now(); 35 | while (duration + time > Date.now()) {} 36 | } 37 | // 工作任务 38 | let works = [ 39 | () => { 40 | console.log("A1开始"); 41 | sleep(30); // 挂起30ms 42 | console.log("A1结束"); 43 | }, 44 | () => { 45 | console.log("B1开始"); 46 | console.log("B1结束"); 47 | }, 48 | () => { 49 | console.log("B2开始"); 50 | console.log("B2结束"); 51 | }, 52 | ]; 53 | 54 | // 取出当前的第一个任务执行 55 | function performUnitWord() { 56 | let work = works.shift(); 57 | work(); 58 | } 59 | 60 | function workLoop(deadLine) { 61 | // deadLine.timeRemaining:函数的返回值表示当前空闲时间还剩下多少时间 62 | console.log("本帧剩余时间ms", parseInt(deadLine.timeRemaining())); 63 | while ( 64 | // 如果有剩余时间或者过期了,过期的话 deadLine.timeout属性就会为true 65 | (deadLine.timeRemaining() > 0 || deadLine.timeout) && 66 | works.length > 0 67 | ) { 68 | // 执行任务 69 | performUnitWord(); 70 | } 71 | if (works.length > 0) { 72 | // 浏览器有空继续调用 73 | console.log( 74 | `只剩下${deadLine.timeRemaining()},时间片已经到期了,等待下次调度` 75 | ); 76 | requestIdleCallback(workLoop); 77 | } 78 | } 79 | 80 | // 告诉浏览器有空闲时间执行任务,但是如果已经过期,不管有没有空,都帮我执行,执行方法就是requestIdleCallback(callBack,timeOut) 81 | requestIdleCallback(workLoop, { timeout: 1000 }); 82 | ``` 83 | -------------------------------------------------------------------------------- /src/react-dom-bindings/src/client/ReactDOMHostConfig.js: -------------------------------------------------------------------------------- 1 | import { setInitialProperties, diffProperties, updateProperties } from './ReactDOMComponent'; 2 | import { precacheFiberNode, updateFiberProps } from './ReactDOMComponentTree'; 3 | import { DefaultEventPriority } from 'react-reconciler/src/ReactEventPriorities'; 4 | import { getEventPriority } from '../events/ReactDOMEventListener'; 5 | 6 | export function shouldSetTextContent(type, props) { 7 | return typeof props.children === "string" || typeof props.children === "number"; 8 | } 9 | 10 | export function createTextInstance(content) { 11 | return document.createTextNode(content); 12 | } 13 | /** 14 | * 在原生组件初次挂载的时候,会通过此方法创建真实DOM 15 | * @param {*} type 类型 span 16 | * @param {*} props 属性 17 | * @param {*} internalInstanceHandle 它对应的fiber 18 | * @returns 19 | */ 20 | export function createInstance(type, props, internalInstanceHandle) { 21 | const domElement = document.createElement(type); 22 | //预先缓存fiber节点到DOM元素上 23 | precacheFiberNode(internalInstanceHandle, domElement); 24 | //把属性直接保存在domElement的属性上 25 | updateFiberProps(domElement, props); 26 | return domElement; 27 | } 28 | 29 | export function appendInitialChild(parent, child) { 30 | parent.appendChild(child); 31 | } 32 | export function finalizeInitialChildren(domElement, type, props) { 33 | setInitialProperties(domElement, type, props); 34 | } 35 | export function appendChild(parentInstance, child) { 36 | parentInstance.appendChild(child); 37 | } 38 | /** 39 | * 40 | * @param {*} parentInstance 父DOM节点 41 | * @param {*} child 子DOM节点 42 | * @param {*} beforeChild 插入到谁的前面,它也是一个DOM节点 43 | */ 44 | export function insertBefore(parentInstance, child, beforeChild) { 45 | parentInstance.insertBefore(child, beforeChild); 46 | } 47 | export function prepareUpdate(domElement, type, oldProps, newProps) { 48 | return diffProperties(domElement, type, oldProps, newProps); 49 | } 50 | export function commitUpdate(domElement, updatePayload, type, oldProps, newProps) { 51 | updateProperties(domElement, updatePayload, type, oldProps, newProps); 52 | updateFiberProps(domElement, newProps); 53 | } 54 | export function removeChild(parentInstance, child) { 55 | parentInstance.removeChild(child); 56 | } 57 | 58 | export function getCurrentEventPriority() { 59 | const currentEvent = window.event; 60 | if (currentEvent === undefined) { 61 | return DefaultEventPriority; 62 | } 63 | return getEventPriority(currentEvent.type); 64 | } -------------------------------------------------------------------------------- /src/react-reconciler/src/ReactEventPriorities.js: -------------------------------------------------------------------------------- 1 | /** 2 | * React Event Priorities - Event-Based Priority Management 3 | * 4 | * This module manages the mapping between different types of events and their 5 | * corresponding priority levels in React's scheduling system. It bridges the 6 | * gap between user interactions and React's internal lane-based priority system. 7 | * 8 | * Priority levels (from highest to lowest): 9 | * - Discrete: User interactions that need immediate response (click, input) 10 | * - Continuous: Events that fire frequently (mousemove, scroll) 11 | * - Default: Normal updates and state changes 12 | * - Idle: Background work that can be deferred 13 | * 14 | * @module ReactEventPriorities 15 | */ 16 | 17 | import { 18 | NoLane, 19 | SyncLane, 20 | InputContinuousLane, 21 | DefaultLane, 22 | IdleLane, 23 | getHighestPriorityLane, 24 | includesNonIdleWork, 25 | } from "./ReactFiberLane"; 26 | 27 | // Event priority constants mapped to corresponding lanes 28 | export const DiscreteEventPriority = SyncLane; // 1 - Discrete events (click, change) 29 | export const ContinuousEventPriority = InputContinuousLane; // 4 - Continuous events (mousemove) 30 | export const DefaultEventPriority = DefaultLane; // 16 - Default priority events 31 | export const IdleEventPriority = IdleLane; // Idle priority events 32 | 33 | // Global state for current update priority 34 | let currentUpdatePriority = NoLane; 35 | 36 | export function getCurrentUpdatePriority() { 37 | return currentUpdatePriority; 38 | } 39 | export function setCurrentUpdatePriority(newPriority) { 40 | currentUpdatePriority = newPriority; 41 | } 42 | 43 | /** 44 | * 判断eventPriority是不是比lane要小,更小意味着优先级更高 45 | * @param {*} a 46 | * @param {*} b 47 | * @returns 48 | */ 49 | export function isHigherEventPriority(eventPriority, lane) { 50 | return eventPriority !== 0 && eventPriority < lane; 51 | } 52 | /** 53 | * 把lane转成事件优先级 54 | * lane 31 55 | * 事件优先级是4 56 | * 调度优先级5 57 | * @param {*} lanes 58 | * @returns 59 | */ 60 | export function lanesToEventPriority(lanes) { 61 | //获取最高优先级的lane 62 | let lane = getHighestPriorityLane(lanes); 63 | if (!isHigherEventPriority(DiscreteEventPriority, lane)) { 64 | return DiscreteEventPriority; //1 65 | } 66 | if (!isHigherEventPriority(ContinuousEventPriority, lane)) { 67 | return ContinuousEventPriority; //4 68 | } 69 | if (includesNonIdleWork(lane)) { 70 | return DefaultEventPriority; //16 71 | } 72 | return IdleEventPriority; 73 | } 74 | -------------------------------------------------------------------------------- /src/react-reconciler/src/ReactFiberConcurrentUpdates.js: -------------------------------------------------------------------------------- 1 | import { HostRoot } from "./ReactWorkTags"; 2 | import { mergeLanes } from "./ReactFiberLane"; 3 | 4 | const concurrentQueues = []; 5 | let concurrentQueuesIndex = 0; 6 | 7 | /** 8 | * Cache updates to the concurrentQueue array first 9 | * @param {*} fiber 10 | * @param {*} queue 11 | * @param {*} update 12 | */ 13 | function enqueueUpdate(fiber, queue, update, lane) { 14 | //012 setNumber1 345 setNumber2 678 setNumber3 15 | concurrentQueues[concurrentQueuesIndex++] = fiber; // The fiber corresponding to the function component 16 | concurrentQueues[concurrentQueuesIndex++] = queue; // The update queue corresponding to the hook to be updated 17 | concurrentQueues[concurrentQueuesIndex++] = update; // Update object 18 | concurrentQueues[concurrentQueuesIndex++] = lane; // Lane corresponding to the update 19 | // When we add an update to a fiber, we need to merge the lane of this update into the lane of this fiber 20 | fiber.lanes = mergeLanes(fiber.lanes, lane); 21 | } 22 | 23 | export function finishQueueingConcurrentUpdates() { 24 | const endIndex = concurrentQueuesIndex; //9 Just a boundary condition 25 | concurrentQueuesIndex = 0; 26 | let i = 0; 27 | while (i < endIndex) { 28 | const fiber = concurrentQueues[i++]; 29 | const queue = concurrentQueues[i++]; 30 | const update = concurrentQueues[i++]; 31 | const lane = concurrentQueues[i++]; 32 | if (queue !== null && update !== null) { 33 | const pending = queue.pending; 34 | if (pending === null) { 35 | update.next = update; 36 | } else { 37 | update.next = pending.next; 38 | pending.next = update; 39 | } 40 | queue.pending = update; 41 | } 42 | } 43 | } 44 | /** 45 | * Add the update queue to the update queue 46 | * @param {*} fiber The fiber corresponding to the function component 47 | * @param {*} queue The update queue corresponding to the hook to be updated 48 | * @param {*} update Update object 49 | */ 50 | export function enqueueConcurrentHookUpdate(fiber, queue, update, lane) { 51 | enqueueUpdate(fiber, queue, update, lane); 52 | return getRootForUpdatedFiber(fiber); 53 | } 54 | /** 55 | * Enqueue the update 56 | * @param {*} fiber The fiber to be enqueued, root fiber 57 | * @param {*} queue shareQueue The queue to be effective 58 | * @param {*} update Update 59 | * @param {*} lane The lane of this update 60 | */ 61 | export function enqueueConcurrentClassUpdate(fiber, queue, update, lane) { 62 | enqueueUpdate(fiber, queue, update, lane); 63 | return getRootForUpdatedFiber(fiber); 64 | } 65 | function getRootForUpdatedFiber(sourceFiber) { 66 | let node = sourceFiber; 67 | let parent = node.return; 68 | while (parent !== null) { 69 | node = parent; 70 | parent = node.return; 71 | } 72 | return node.tag === HostRoot ? node.stateNode : null; //FiberRootNode div#root 73 | } 74 | -------------------------------------------------------------------------------- /src/react-reconciler/src/ReactFiberRoot.js: -------------------------------------------------------------------------------- 1 | /** 2 | * React Fiber Root - Root Container Management 3 | * 4 | * This module manages the root container of a React application. The FiberRootNode 5 | * represents the top-level container that holds the entire React fiber tree. 6 | * It manages scheduling, priority lanes, and the connection between React and the DOM. 7 | * 8 | * Key responsibilities: 9 | * - Root container lifecycle management 10 | * - Priority lane tracking and scheduling 11 | * - Connection between React fiber tree and DOM container 12 | * - Update queue initialization 13 | * 14 | * @module ReactFiberRoot 15 | */ 16 | 17 | import { createHostRootFiber } from "./ReactFiber"; 18 | import { initialUpdateQueue } from "./ReactFiberClassUpdateQueue"; 19 | import { NoLanes, NoLane, createLaneMap, NoTimestamp } from "./ReactFiberLane"; 20 | 21 | /** 22 | * Fiber Root Node Constructor 23 | * 24 | * Creates the root node that represents the entire React application container. 25 | * This node sits above the React fiber tree and manages scheduling and updates. 26 | * 27 | * @param {Element} containerInfo - DOM container element (e.g., div#root) 28 | */ 29 | function FiberRootNode(containerInfo) { 30 | this.containerInfo = containerInfo; // DOM container element (div#root) 31 | 32 | // Scheduling and priority management 33 | this.pendingLanes = NoLanes; // Lanes with pending work 34 | this.callbackNode = null; // Current scheduler callback 35 | this.callbackPriority = NoLane; // Priority of current callback 36 | 37 | // Expiration tracking 38 | this.expirationTimes = createLaneMap(NoTimestamp); // Expiration time for each lane 39 | this.expiredLanes = NoLanes; // Lanes that have expired 40 | } 41 | 42 | /** 43 | * Create Fiber Root 44 | * 45 | * Creates and initializes a complete fiber root structure. This includes: 46 | * 1. Creating the FiberRootNode (container management) 47 | * 2. Creating the HostRootFiber (root of the fiber tree) 48 | * 3. Establishing bidirectional connection between them 49 | * 4. Initializing the update queue 50 | * 51 | * This function sets up the foundation for React's rendering system. 52 | * 53 | * @param {Element} containerInfo - DOM container element 54 | * @returns {FiberRootNode} Complete fiber root structure 55 | */ 56 | export function createFiberRoot(containerInfo) { 57 | // Create the root container node 58 | const root = new FiberRootNode(containerInfo); 59 | 60 | // Create the root fiber (HostRoot represents the container element) 61 | const uninitializedFiber = createHostRootFiber(); 62 | 63 | // Establish bidirectional connection for double buffering 64 | root.current = uninitializedFiber; // Root points to current fiber tree 65 | uninitializedFiber.stateNode = root; // Root fiber points back to container 66 | 67 | // Initialize update queue for the root fiber 68 | initialUpdateQueue(uninitializedFiber); 69 | 70 | return root; 71 | } 72 | -------------------------------------------------------------------------------- /src/react-dom-bindings/src/events/ReactDOMEventListener.js: -------------------------------------------------------------------------------- 1 | /** 2 | * ReactDOMEventListener - Event Listener for ReactDOM 3 | * 4 | * This module provides the event listener functionality for ReactDOM. 5 | * It handles the creation and dispatch of synthetic events. 6 | * 7 | * @module ReactDOMEventListener 8 | */ 9 | import getEventTarget from "./getEventTarget"; 10 | import { getClosestInstanceFromNode } from "../client/ReactDOMComponentTree"; 11 | import { dispatchEventForPluginEventSystem } from "./DOMPluginEventSystem"; 12 | import { 13 | ContinuousEventPriority, 14 | DefaultEventPriority, 15 | DiscreteEventPriority, 16 | getCurrentUpdatePriority, 17 | setCurrentUpdatePriority, 18 | } from "react-reconciler/src/ReactEventPriorities"; 19 | export function createEventListenerWrapperWithPriority( 20 | targetContainer, 21 | domEventName, 22 | eventSystemFlags 23 | ) { 24 | const listenerWrapper = dispatchDiscreteEvent; 25 | return listenerWrapper.bind( 26 | null, 27 | domEventName, 28 | eventSystemFlags, 29 | targetContainer 30 | ); 31 | } 32 | /** 33 | * dispatch the discrete event of the listener function 34 | * @param {*} domEventName click 35 | * @param {*} eventSystemFlags phase 0 bubble 4 capture 36 | * @param {*} container container div#root 37 | * @param {*} nativeEvent native event 38 | */ 39 | function dispatchDiscreteEvent( 40 | domEventName, 41 | eventSystemFlags, 42 | container, 43 | nativeEvent 44 | ) { 45 | //when you click the button, you need to set the update priority 46 | //get the current old update priority 47 | const previousPriority = getCurrentUpdatePriority(); 48 | try { 49 | //set the current update priority to the discrete event priority 1 50 | setCurrentUpdatePriority(DiscreteEventPriority); 51 | dispatchEvent(domEventName, eventSystemFlags, container, nativeEvent); 52 | } finally { 53 | setCurrentUpdatePriority(previousPriority); 54 | } 55 | } 56 | /** 57 | * this method is to delegate to the container's callback, when the container#root handles the event in the capture or bubble phase, this function will be executed 58 | * @param {*} domEventName 59 | * @param {*} eventSystemFlags phase 0 bubble 4 capture 60 | * @param {*} container container div#root 61 | * @param {*} nativeEvent native event 62 | */ 63 | export function dispatchEvent( 64 | domEventName, 65 | eventSystemFlags, 66 | targetContainer, 67 | nativeEvent 68 | ) { 69 | // get the event target, it is a real DOM 70 | const nativeEventTarget = getEventTarget(nativeEvent); 71 | const targetInst = getClosestInstanceFromNode(nativeEventTarget); 72 | dispatchEventForPluginEventSystem( 73 | domEventName, //click 74 | eventSystemFlags, //0 4 75 | nativeEvent, //native event 76 | targetInst, //the fiber of the real DOM 77 | targetContainer //target container 78 | ); 79 | } 80 | /** 81 | * get the event priority 82 | * @param {*} domEventName click 83 | */ 84 | export function getEventPriority(domEventName) { 85 | switch (domEventName) { 86 | case "click": 87 | return DiscreteEventPriority; 88 | case "drag": 89 | return ContinuousEventPriority; 90 | default: 91 | return DefaultEventPriority; 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /markdown/&|.md: -------------------------------------------------------------------------------- 1 | 5 | 6 | #### Bitwise AND (&) 7 | 8 | When every bit is 1, the result is 1, otherwise it is 0 9 | 10 | ```javaScript 11 | const a = 5; // 00000000000000000000000000000101 12 | const b = 3; // 00000000000000000000000000000011 13 | 14 | console.log(a & b); // 00000000000000000000000000000001 15 | // Expected output: 1 16 | ``` 17 | 18 | #### Bitwise OR (|) 19 | 20 | When every bit is 0, the result is 0, otherwise it is 1 21 | 22 | ```javaScript 23 | const a = 5; // 00000000000000000000000000000101 24 | const b = 3; // 00000000000000000000000000000011 25 | 26 | console.log(a | b); // 00000000000000000000000000000111 27 | // Expected output: 7 28 | ``` 29 | 30 | #### Bitwise AND assignment (&=) 31 | 32 | The bitwise AND assignment operator (&=) uses the binary representation of two operands, performs a bitwise AND operation on them, and assigns the result to the variable. 33 | 34 | ```javaScript 35 | let a = 5; // 00000000000000000000000000000101 36 | a &= 3; // 00000000000000000000000000000011 37 | 38 | console.log(a); // 00000000000000000000000000000001 39 | // Expected output: 1 40 | ``` 41 | 42 | #### Bitwise OR assignment (|=) 43 | 44 | The bitwise OR assignment (|=) operator uses the binary representation of two operands, performs a bitwise OR operation on them, and assigns the result to the variable. 45 | 46 | ```javaScript 47 | let a = 5; // 00000000000000000000000000000101 48 | a |= 3; // 00000000000000000000000000000011 49 | 50 | console.log(a); // 00000000000000000000000000000111 51 | // Expected output: 7 52 | 53 | ``` 54 | 55 | #### Bitwise NOT (~) 56 | 57 | Bitwise negation 58 | 59 | ```javaScript 60 | const a = 5; // 00000000000000000000000000000101 61 | const b = -3; // 11111111111111111111111111111101 62 | 63 | console.log(~a); // 11111111111111111111111111111010 64 | // Expected output: -6 65 | 66 | console.log(~b); // 00000000000000000000000000000010 67 | // Expected output: 2 68 | 69 | ``` 70 | 71 | Used in React to record side effect flags 72 | 73 | Side effect flags can optimize performance, because in React, side effects (**add, delete, modify**) bubble up to the parent level. If the parent has no side effects, there's no need to traverse child nodes in depth-first order, saving performance 74 | 75 | ```javaScript 76 | 77 | //Define constants 78 | const Placement = 0b001; // 1 79 | const Update = 0b010; // 2 80 | //Define operations 81 | let flags = 0b000; // 0 82 | //Add operation 83 | flags |= Placement; 84 | flags |= Update; 85 | console.log(flags.toString(2)); //0b011 86 | 87 | //Delete operation 0b011 & 0b110 => 0b010 88 | flags = flags & ~Placement; 89 | console.log(flags.toString(2)); //0b010 90 | console.log(flags); //2 91 | 92 | //Check if contains 93 | console.log((flags & Placement) === Placement); // false 94 | console.log((flags & Update) === Update); // true 95 | 96 | //Check if not contains 97 | console.log((flags & Placement) === 0); // true 98 | console.log((flags & Update) === 0); // fasle 99 | 100 | ``` 101 | -------------------------------------------------------------------------------- /src/react/src/ReactHooks.js: -------------------------------------------------------------------------------- 1 | /** 2 | * React Hooks - Public API Layer 3 | * 4 | * This module provides the public hooks API that developers use in their components. 5 | * It uses the dispatcher pattern to delegate to different implementations based on 6 | * the current rendering phase (mount vs update). 7 | * 8 | * @module ReactHooks 9 | */ 10 | 11 | import ReactCurrentDispatcher from "./ReactCurrentDispatcher"; 12 | 13 | /** 14 | * Resolve Current Dispatcher 15 | * 16 | * Gets the current dispatcher implementation. The dispatcher changes based on 17 | * the rendering phase - different implementations for mount and update phases. 18 | * 19 | * @returns {Object} Current dispatcher with hook implementations 20 | */ 21 | function resolveDispatcher() { 22 | return ReactCurrentDispatcher.current; 23 | } 24 | 25 | /** 26 | * useReducer Hook 27 | * 28 | * Hook for managing complex state logic. Similar to useState but accepts a reducer 29 | * function that specifies how the state gets updated in response to actions. 30 | * 31 | * @param {Function} reducer - Reducer function that takes (state, action) and returns new state 32 | * @param {any} initialArg - Initial state value 33 | * @returns {Array} Array containing [state, dispatch] where dispatch triggers state updates 34 | */ 35 | export function useReducer(reducer, initialArg) { 36 | const dispatcher = resolveDispatcher(); 37 | return dispatcher.useReducer(reducer, initialArg); 38 | } 39 | 40 | /** 41 | * useState Hook 42 | * 43 | * Hook for managing component state. Returns current state value and a function 44 | * to update it. State updates are asynchronous and may be batched. 45 | * 46 | * @param {any} initialState - Initial state value or function that returns initial state 47 | * @returns {Array} Array containing [state, setState] where setState updates the state 48 | */ 49 | export function useState(initialState) { 50 | const dispatcher = resolveDispatcher(); 51 | return dispatcher.useState(initialState); 52 | } 53 | 54 | /** 55 | * useEffect Hook 56 | * 57 | * Hook for performing side effects in function components. Runs after render 58 | * and can optionally clean up before the next effect or component unmount. 59 | * 60 | * @param {Function} create - Effect function, optionally returns cleanup function 61 | * @param {Array} deps - Dependency array, effect runs when dependencies change 62 | */ 63 | export function useEffect(create, deps) { 64 | const dispatcher = resolveDispatcher(); 65 | return dispatcher.useEffect(create, deps); 66 | } 67 | 68 | /** 69 | * useLayoutEffect Hook 70 | * 71 | * Similar to useEffect but runs synchronously after all DOM mutations. 72 | * Use this for DOM measurements or synchronous DOM updates. 73 | * 74 | * @param {Function} create - Effect function, optionally returns cleanup function 75 | * @param {Array} deps - Dependency array, effect runs when dependencies change 76 | */ 77 | export function useLayoutEffect(create, deps) { 78 | const dispatcher = resolveDispatcher(); 79 | return dispatcher.useLayoutEffect(create, deps); 80 | } 81 | 82 | /** 83 | * useRef Hook 84 | * 85 | * Hook for creating a mutable ref object. The ref object persists across renders 86 | * and doesn't cause re-renders when its value changes. 87 | * 88 | * @param {any} initialValue - Initial value for the ref.current property 89 | * @returns {Object} Ref object with current property 90 | */ 91 | export function useRef(initialValue) { 92 | const dispatcher = resolveDispatcher(); 93 | return dispatcher.useRef(initialValue); 94 | } 95 | -------------------------------------------------------------------------------- /src/scheduler/src/forks/SchedulerMinHeap.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Scheduler Min Heap - Priority Queue Implementation 3 | * 4 | * This module implements a binary min heap data structure used by React's scheduler 5 | * to manage tasks by priority. The heap ensures that the highest priority task 6 | * (lowest sortIndex value) is always at the root and can be accessed in O(1) time. 7 | * 8 | * Key properties: 9 | * - Parent node value ≤ child node values (min heap property) 10 | * - Complete binary tree structure 11 | * - Efficient insertion and extraction: O(log n) 12 | * - Peek operation: O(1) 13 | * 14 | * Used for: 15 | * - Task queue management in scheduler 16 | * - Priority-based task execution 17 | * - Efficient task scheduling and dequeuing 18 | * 19 | * @module SchedulerMinHeap 20 | */ 21 | 22 | /** 23 | * Push - Insert Element into Heap 24 | * 25 | * Adds a new node to the heap while maintaining the min heap property. 26 | * The element is added at the end and then "bubbled up" to its correct position. 27 | * 28 | * Time complexity: O(log n) 29 | * 30 | * @param {Array} heap - The heap array 31 | * @param {Object} node - Node to insert (must have sortIndex property) 32 | */ 33 | export function push(heap, node) { 34 | const index = heap.length; 35 | heap.push(node); 36 | siftUp(heap, node, index); 37 | } 38 | 39 | /** 40 | * Peek - Get Minimum Element 41 | * 42 | * Returns the minimum element (root) without removing it from the heap. 43 | * In a min heap, this is always the first element. 44 | * 45 | * Time complexity: O(1) 46 | * 47 | * @param {Array} heap - The heap array 48 | * @returns {Object|null} Minimum element or null if heap is empty 49 | */ 50 | export function peek(heap) { 51 | return heap.length === 0 ? null : heap[0]; 52 | } 53 | 54 | /** 55 | * Pop - Remove and Return Minimum Element 56 | * 57 | * Removes and returns the minimum element (root) from the heap. 58 | * The last element replaces the root and is "bubbled down" to maintain heap property. 59 | * 60 | * Time complexity: O(log n) 61 | * 62 | * @param {Array} heap - The heap array 63 | * @returns {Object|null} Minimum element or null if heap is empty 64 | */ 65 | export function pop(heap) { 66 | if (heap.length === 0) { 67 | return null; 68 | } 69 | 70 | const first = heap[0]; 71 | const last = heap.pop(); 72 | 73 | if (last !== first) { 74 | heap[0] = last; 75 | siftDown(heap, last, 0); 76 | } 77 | 78 | return first; 79 | } 80 | function siftUp(heap, node, i) { 81 | let index = i; 82 | while (index > 0) { 83 | const parentIndex = (index - 1) >>> 1; 84 | const parent = heap[parentIndex]; 85 | if (compare(parent, node) > 0) { 86 | heap[parentIndex] = node; 87 | heap[index] = parent; 88 | index = parentIndex; 89 | } else { 90 | return; 91 | } 92 | } 93 | } 94 | function siftDown(heap, node, i) { 95 | let index = i; 96 | const length = heap.length; 97 | const halfLength = length >>> 1; 98 | while (index < halfLength) { 99 | const leftIndex = (index + 1) * 2 - 1; 100 | const left = heap[leftIndex]; 101 | const rightIndex = leftIndex + 1; 102 | const right = heap[rightIndex]; 103 | if (compare(left, node) < 0) { 104 | if (rightIndex < length && compare(right, left) < 0) { 105 | heap[index] = right; 106 | heap[rightIndex] = node; 107 | index = rightIndex; 108 | } else { 109 | heap[index] = left; 110 | heap[leftIndex] = node; 111 | index = leftIndex; 112 | } 113 | } else if (rightIndex < length && compare(right, node) < 0) { 114 | heap[index] = right; 115 | heap[rightIndex] = node; 116 | index = rightIndex; 117 | } else { 118 | return; 119 | } 120 | } 121 | } 122 | function compare(a, b) { 123 | const diff = a.sortIndex - b.sortIndex; 124 | return diff !== 0 ? diff : a.id - b.id; 125 | } 126 | -------------------------------------------------------------------------------- /src/react-dom-bindings/src/client/ReactDOMComponent.js: -------------------------------------------------------------------------------- 1 | import { setValueForStyles } from './CSSPropertyOperations'; 2 | import setTextContent from './setTextContent'; 3 | import { setValueForProperty } from './DOMPropertyOperations'; 4 | const STYLE = 'style'; 5 | const CHILDREN = 'children'; 6 | function setInitialDOMProperties(tag, domElement, nextProps) { 7 | for (const propKey in nextProps) { 8 | if (nextProps.hasOwnProperty(propKey)) { 9 | const nextProp = nextProps[propKey]; 10 | if (propKey === STYLE) { 11 | setValueForStyles(domElement, nextProp); 12 | } else if (propKey == CHILDREN) { 13 | if (typeof nextProp === 'string') { 14 | setTextContent(domElement, nextProp); 15 | } else if (typeof nextProp === 'number') { 16 | setTextContent(domElement, `${nextProp}`); 17 | } 18 | } else if (nextProp !== null) { 19 | setValueForProperty(domElement, propKey, nextProp) 20 | } 21 | } 22 | } 23 | } 24 | export function setInitialProperties(domElement, tag, props) { 25 | setInitialDOMProperties(tag, domElement, props); 26 | } 27 | 28 | export function diffProperties(domElement, tag, lastProps, nextProps) { 29 | let updatePayload = null; 30 | let propKey; 31 | let styleName; 32 | let styleUpdates = null; 33 | //处理属性的删除 如果说一个属性在老对象里有,新对象没有的话,那就意味着删除 34 | for (propKey in lastProps) { 35 | //如果新属性对象里有此属性,或者老的没有此属性,或者老的是个null 36 | if (nextProps.hasOwnProperty(propKey) || !lastProps.hasOwnProperty(propKey) || lastProps[propKey] === null) { 37 | continue; 38 | } 39 | if (propKey === STYLE) { 40 | const lastStyle = lastProps[propKey]; 41 | for (styleName in lastStyle) { 42 | if (lastStyle.hasOwnProperty(styleName)) { 43 | if (!styleUpdates) { 44 | styleUpdates = {}; 45 | } 46 | styleUpdates[styleName] = ''; 47 | } 48 | } 49 | } else { 50 | (updatePayload = updatePayload || []).push(propKey, null); 51 | } 52 | } 53 | //遍历新的属性对象 54 | for (propKey in nextProps) { 55 | const nextProp = nextProps[propKey];//新属性中的值 56 | const lastProp = lastProps !== null ? lastProps[propKey] : undefined;//老属性中的值 57 | if (!nextProps.hasOwnProperty(propKey) || nextProp === lastProp || (nextProp === null && lastProp === null)) { 58 | continue; 59 | } 60 | if (propKey === STYLE) { 61 | if (lastProp) { 62 | //计算要删除的行内样式 63 | for (styleName in lastProp) { 64 | //如果此样式对象里在的某个属性老的style里有,新的style里没有 65 | if (lastProp.hasOwnProperty(styleName) && (!nextProp || !nextProp.hasOwnProperty(styleName))) { 66 | if (!styleUpdates) 67 | styleUpdates = {}; 68 | styleUpdates[styleName] = ''; 69 | } 70 | } 71 | //遍历新的样式对象 72 | for (styleName in nextProp) { 73 | //如果说新的属性有,并且新属性的值和老属性不一样 74 | if (nextProp.hasOwnProperty(styleName) && lastProp[styleName] !== nextProp[styleName]) { 75 | if (!styleUpdates) 76 | styleUpdates = {}; 77 | styleUpdates[styleName] = nextProp[styleName]; 78 | } 79 | } 80 | } else { 81 | styleUpdates = nextProp; 82 | } 83 | } else if (propKey === CHILDREN) { 84 | if (typeof nextProp === 'string' || typeof nextProp === 'number') { 85 | (updatePayload = updatePayload || []).push(propKey, nextProp); 86 | } 87 | } else { 88 | (updatePayload = updatePayload || []).push(propKey, nextProp); 89 | } 90 | } 91 | if (styleUpdates) { 92 | (updatePayload = updatePayload || []).push(STYLE, styleUpdates); 93 | } 94 | return updatePayload;//[key1,value1,key2,value2] 95 | } 96 | 97 | export function updateProperties(domElement, updatePayload) { 98 | updateDOMProperties(domElement, updatePayload); 99 | } 100 | function updateDOMProperties(domElement, updatePayload) { 101 | for (let i = 0; i < updatePayload.length; i += 2) { 102 | const propKey = updatePayload[i]; 103 | const propValue = updatePayload[i + 1]; 104 | if (propKey === STYLE) { 105 | setValueForStyles(domElement, propValue); 106 | } else if (propKey === CHILDREN) { 107 | setTextContent(domElement, propValue); 108 | } else { 109 | setValueForProperty(domElement, propKey, propValue); 110 | } 111 | } 112 | } -------------------------------------------------------------------------------- /src/react-dom-bindings/src/events/SyntheticEvent.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Synthetic Event - Cross-Browser Event Wrapper 3 | * 4 | * This module implements React's synthetic event system, which provides a 5 | * consistent interface for handling events across different browsers. 6 | * Synthetic events wrap native browser events and normalize their behavior. 7 | * 8 | * Key features: 9 | * - Cross-browser compatibility 10 | * - Consistent API across different event types 11 | * - Event pooling for performance (in older React versions) 12 | * - preventDefault and stopPropagation support 13 | * - Integration with React's event delegation system 14 | * 15 | * @module SyntheticEvent 16 | */ 17 | 18 | import assign from "shared/assign"; 19 | 20 | /** 21 | * Helper functions for event state tracking 22 | */ 23 | function functionThatReturnsTrue() { 24 | return true; 25 | } 26 | 27 | function functionThatReturnsFalse() { 28 | return false; 29 | } 30 | 31 | /** 32 | * Mouse Event Interface 33 | * 34 | * Defines the properties that should be copied from native mouse events 35 | * to synthetic mouse events. This ensures consistent access to mouse 36 | * position and button information. 37 | */ 38 | const MouseEventInterface = { 39 | clientX: 0, // X coordinate relative to viewport 40 | clientY: 0, // Y coordinate relative to viewport 41 | }; 42 | 43 | /** 44 | * Create Synthetic Event 45 | * 46 | * Factory function that creates synthetic event constructors with specific interfaces. 47 | * This allows different event types (mouse, keyboard, etc.) to have their own 48 | * property sets while sharing common synthetic event behavior. 49 | * 50 | * @param {Object} inter - Interface object defining which properties to copy from native events 51 | * @returns {Function} Synthetic event constructor 52 | */ 53 | function createSyntheticEvent(inter) { 54 | /** 55 | * Synthetic Base Event Constructor 56 | * 57 | * Creates a synthetic event that wraps a native browser event with consistent 58 | * cross-browser behavior and React-specific functionality. 59 | * 60 | * @param {string} reactName - React event prop name (e.g., 'onClick') 61 | * @param {string} reactEventType - Event type (e.g., 'click') 62 | * @param {Fiber} targetInst - Fiber instance of the event target 63 | * @param {Event} nativeEvent - Original native browser event 64 | * @param {Element} nativeEventTarget - DOM element that triggered the event 65 | */ 66 | function SyntheticBaseEvent( 67 | reactName, 68 | reactEventType, 69 | targetInst, 70 | nativeEvent, 71 | nativeEventTarget 72 | ) { 73 | // React-specific properties 74 | this._reactName = reactName; // React prop name (onClick, onChange, etc.) 75 | this.type = reactEventType; // Event type (click, change, etc.) 76 | this._targetInst = targetInst; // Target fiber instance 77 | this.nativeEvent = nativeEvent; // Original native event 78 | this.target = nativeEventTarget; // DOM element that triggered event 79 | 80 | // Copy interface-specific properties from native event 81 | for (const propName in inter) { 82 | if (!inter.hasOwnProperty(propName)) { 83 | continue; 84 | } 85 | this[propName] = nativeEvent[propName]; 86 | } 87 | 88 | // Initialize event state tracking 89 | this.isDefaultPrevented = functionThatReturnsFalse; // Default behavior not prevented 90 | this.isPropagationStopped = functionThatReturnsFalse; // Propagation not stopped 91 | 92 | return this; 93 | } 94 | // Add synthetic event methods to prototype 95 | assign(SyntheticBaseEvent.prototype, { 96 | /** 97 | * Prevent Default 98 | * 99 | * Prevents the default browser behavior for this event. 100 | * Uses cross-browser compatible approach for older browsers. 101 | */ 102 | preventDefault() { 103 | const event = this.nativeEvent; 104 | if (event.preventDefault) { 105 | event.preventDefault(); 106 | } else { 107 | // Fallback for older browsers (IE8 and below) 108 | event.returnValue = false; 109 | } 110 | this.isDefaultPrevented = functionThatReturnsTrue; 111 | }, 112 | 113 | /** 114 | * Stop Propagation 115 | * 116 | * Stops the event from bubbling up or capturing down the DOM tree. 117 | * Uses cross-browser compatible approach for older browsers. 118 | */ 119 | stopPropagation() { 120 | const event = this.nativeEvent; 121 | if (event.stopPropagation) { 122 | event.stopPropagation(); 123 | } else { 124 | // Fallback for older browsers (IE8 and below) 125 | event.cancelBubble = true; 126 | } 127 | this.isPropagationStopped = functionThatReturnsTrue; 128 | }, 129 | }); 130 | return SyntheticBaseEvent; 131 | } 132 | export const SyntheticMouseEvent = createSyntheticEvent(MouseEventInterface); 133 | -------------------------------------------------------------------------------- /src/react-reconciler/src/ReactFiberBeginWork.js: -------------------------------------------------------------------------------- 1 | /** 2 | * React Fiber Begin Work - Render Phase Implementation 3 | * 4 | * This module implements the "begin work" phase of React's reconciliation process. 5 | * During this phase, React processes each fiber node, updates its state and props, 6 | * and creates child fibers based on the new virtual DOM. 7 | * 8 | * Key responsibilities: 9 | * - Process different types of components (host, function, class) 10 | * - Reconcile children (create, update, delete child fibers) 11 | * - Handle state updates and prop changes 12 | * - Optimize rendering with bailout conditions 13 | * 14 | * @module ReactFiberBeginWork 15 | */ 16 | 17 | import { 18 | HostComponent, 19 | HostRoot, 20 | HostText, 21 | IndeterminateComponent, 22 | FunctionComponent, 23 | } from "./ReactWorkTags"; 24 | import { 25 | processUpdateQueue, 26 | cloneUpdateQueue, 27 | } from "./ReactFiberClassUpdateQueue"; 28 | import { mountChildFibers, reconcileChildFibers } from "./ReactChildFiber"; 29 | import { shouldSetTextContent } from "react-dom-bindings/src/client/ReactDOMHostConfig"; 30 | import { renderWithHooks } from "react-reconciler/src/ReactFiberHooks"; 31 | import { NoLane, NoLanes } from "./ReactFiberLane"; 32 | 33 | /** 34 | * Reconcile Children 35 | * 36 | * Creates a new fiber child list based on the new virtual DOM. This function 37 | * implements React's reconciliation algorithm (diffing) to determine what 38 | * changes need to be made to the DOM. 39 | * 40 | * @param {Fiber|null} current - Current (old) parent fiber 41 | * @param {Fiber} workInProgress - Work-in-progress (new) parent fiber 42 | * @param {ReactElement|ReactElement[]|string|number} nextChildren - New child virtual DOM 43 | */ 44 | function reconcileChildren(current, workInProgress, nextChildren) { 45 | if (current === null) { 46 | // If there's no current fiber, this is a mount (initial render) 47 | // All children are new, so we use mountChildFibers which doesn't track side effects 48 | workInProgress.child = mountChildFibers(workInProgress, null, nextChildren); 49 | } else { 50 | // If there's a current fiber, this is an update 51 | // We need to diff the old child fiber list with new virtual DOM 52 | // This enables minimal updates and proper side effect tracking 53 | workInProgress.child = reconcileChildFibers( 54 | workInProgress, 55 | current.child, 56 | nextChildren 57 | ); 58 | } 59 | } 60 | function updateHostRoot(current, workInProgress, renderLanes) { 61 | const nextProps = workInProgress.pendingProps; 62 | cloneUpdateQueue(current, workInProgress); 63 | //需要知道它的子虚拟DOM,知道它的儿子的虚拟DOM信息 64 | processUpdateQueue(workInProgress, nextProps, renderLanes); //workInProgress.memoizedState={ element } 65 | const nextState = workInProgress.memoizedState; 66 | //nextChildren就是新的子虚拟DOM 67 | const nextChildren = nextState.element; //h1 68 | //根据新的虚拟DOM生成子fiber链表 69 | reconcileChildren(current, workInProgress, nextChildren); 70 | return workInProgress.child; //{tag:5,type:'h1'} 71 | } 72 | /** 73 | * 构建原生组件的子fiber链表 74 | * @param {*} current 老fiber 75 | * @param {*} workInProgress 新fiber h1 76 | */ 77 | function updateHostComponent(current, workInProgress) { 78 | const { type } = workInProgress; 79 | const nextProps = workInProgress.pendingProps; 80 | let nextChildren = nextProps.children; 81 | //判断当前虚拟DOM它的儿子是不是一个文本独生子 82 | const isDirectTextChild = shouldSetTextContent(type, nextProps); 83 | if (isDirectTextChild) { 84 | nextChildren = null; 85 | } 86 | reconcileChildren(current, workInProgress, nextChildren); 87 | return workInProgress.child; 88 | } 89 | /** 90 | * 挂载函数组件 91 | * @param {*} current 老fiber 92 | * @param {*} workInProgress 新的fiber 93 | * @param {*} Component 组件类型,也就是函数组件的定义 94 | */ 95 | export function mountIndeterminateComponent( 96 | current, 97 | workInProgress, 98 | Component 99 | ) { 100 | const props = workInProgress.pendingProps; 101 | //const value = Component(props); 102 | const value = renderWithHooks(current, workInProgress, Component, props); 103 | workInProgress.tag = FunctionComponent; 104 | reconcileChildren(current, workInProgress, value); 105 | return workInProgress.child; 106 | } 107 | export function updateFunctionComponent( 108 | current, 109 | workInProgress, 110 | Component, 111 | nextProps, 112 | renderLanes 113 | ) { 114 | const nextChildren = renderWithHooks( 115 | current, 116 | workInProgress, 117 | Component, 118 | nextProps, 119 | renderLanes 120 | ); 121 | reconcileChildren(current, workInProgress, nextChildren); 122 | return workInProgress.child; 123 | } 124 | /** 125 | * 目标是根据新虚拟DOM构建新的fiber子链表 child .sibling 126 | * @param {*} current 老fiber 127 | * @param {*} workInProgress 新的fiber h1 128 | * @returns 129 | */ 130 | export function beginWork(current, workInProgress, renderLanes) { 131 | //在构建fiber树之后清空lanes 132 | workInProgress.lanes = 0; 133 | switch (workInProgress.tag) { 134 | // 因为在React里组件其实有两种,一种是函数组件,一种是类组件,但是它们都是都是函数 135 | case IndeterminateComponent: 136 | return mountIndeterminateComponent( 137 | current, 138 | workInProgress, 139 | workInProgress.type, 140 | renderLanes 141 | ); 142 | case FunctionComponent: { 143 | const Component = workInProgress.type; 144 | const nextProps = workInProgress.pendingProps; 145 | return updateFunctionComponent( 146 | current, 147 | workInProgress, 148 | Component, 149 | nextProps, 150 | renderLanes 151 | ); 152 | } 153 | case HostRoot: 154 | return updateHostRoot(current, workInProgress, renderLanes); 155 | case HostComponent: 156 | return updateHostComponent(current, workInProgress, renderLanes); 157 | case HostText: 158 | return null; 159 | default: 160 | return null; 161 | } 162 | } 163 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "jsx": "react", 4 | /* Visit https://aka.ms/tsconfig.json to read more about this file */ 5 | 6 | /* Basic Options */ 7 | // "incremental": true, /* Enable incremental compilation */ 8 | "target": "es5" /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', or 'ESNEXT'. */, 9 | "module": "CommonJS" /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */, 10 | // "lib": [], /* Specify library files to be included in the compilation. */ 11 | // "allowJs": true, /* Allow javascript files to be compiled. */ 12 | // "checkJs": true, /* Report errors in .js files. */ 13 | // "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */ 14 | // "declaration": true, /* Generates corresponding '.d.ts' file. */ 15 | // "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */ 16 | "sourceMap": true, /* Generates corresponding '.map' file. */ 17 | // "outFile": "./", /* Concatenate and emit output to single file. */ 18 | "outDir": "./build/", /* Redirect output structure to the directory. */ 19 | // "rootDir": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */ 20 | // "composite": true, /* Enable project compilation */ 21 | // "tsBuildInfoFile": "./", /* Specify file to store incremental compilation information */ 22 | // "removeComments": true, /* Do not emit comments to output. */ 23 | // "noEmit": true, /* Do not emit outputs. */ 24 | // "importHelpers": true, /* Import emit helpers from 'tslib'. */ 25 | // "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */ 26 | // "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */ 27 | 28 | /* Strict Type-Checking Options */ 29 | "strict": true /* Enable all strict type-checking options. */, 30 | // "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */ 31 | // "strictNullChecks": true, /* Enable strict null checks. */ 32 | // "strictFunctionTypes": true, /* Enable strict checking of function types. */ 33 | // "strictBindCallApply": true, /* Enable strict 'bind', 'call', and 'apply' methods on functions. */ 34 | // "strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */ 35 | // "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */ 36 | // "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */ 37 | 38 | /* Additional Checks */ 39 | // "noUnusedLocals": true, /* Report errors on unused locals. */ 40 | // "noUnusedParameters": true, /* Report errors on unused parameters. */ 41 | // "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */ 42 | // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */ 43 | 44 | /* Module Resolution Options */ 45 | // "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */ 46 | // "baseUrl": "./", /* Base directory to resolve non-absolute module names. */ 47 | // "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */ 48 | // "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */ 49 | // "typeRoots": [], /* List of folders to include type definitions from. */ 50 | // "types": [], /* Type declaration files to be included in compilation. */ 51 | // "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */ 52 | "esModuleInterop": true /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */, 53 | // "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */ 54 | // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ 55 | 56 | /* Source Map Options */ 57 | // "sourceRoot": "", /* Specify the location where debugger should locate TypeScript files instead of source locations. */ 58 | // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ 59 | // "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */ 60 | // "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */ 61 | 62 | /* Experimental Options */ 63 | // "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */ 64 | // "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */ 65 | 66 | /* Advanced Options */ 67 | "skipLibCheck": true /* Skip type checking of declaration files. */, 68 | "forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */ 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /src/react-reconciler/src/ReactFiberCompleteWork.js: -------------------------------------------------------------------------------- 1 | /** 2 | * React Fiber Complete Work - Complete Phase Implementation 3 | * 4 | * This module implements the "complete work" phase of React's reconciliation process. 5 | * During this phase, React creates DOM nodes, processes props, and builds the 6 | * complete DOM tree structure. This phase works bottom-up through the fiber tree. 7 | * 8 | * Key responsibilities: 9 | * - Create DOM nodes for host components 10 | * - Process and apply props to DOM elements 11 | * - Build parent-child DOM relationships 12 | * - Handle refs and side effects 13 | * - Optimize with bailout conditions 14 | * 15 | * @module ReactFiberCompleteWork 16 | */ 17 | 18 | import { 19 | createTextInstance, 20 | createInstance, 21 | appendInitialChild, 22 | finalizeInitialChildren, 23 | prepareUpdate, 24 | } from "react-dom-bindings/src/client/ReactDOMHostConfig"; 25 | import { NoFlags, Update, Ref } from "./ReactFiberFlags"; 26 | import { 27 | HostComponent, 28 | HostRoot, 29 | HostText, 30 | FunctionComponent, 31 | } from "./ReactWorkTags"; 32 | import { NoLanes, mergeLanes } from "./ReactFiberLane"; 33 | 34 | /** 35 | * Mark Ref 36 | * 37 | * Marks a fiber as having a ref that needs to be attached during commit phase. 38 | * 39 | * @param {Fiber} workInProgress - Fiber with ref to mark 40 | */ 41 | function markRef(workInProgress) { 42 | workInProgress.flags |= Ref; 43 | } 44 | /** 45 | * Append All Children 46 | * 47 | * Appends all child DOM nodes of the completed fiber to its parent DOM node. 48 | * This function traverses the fiber tree and builds the actual DOM hierarchy 49 | * by connecting child DOM elements to their parent. 50 | * 51 | * The traversal skips over component fibers (function/class components) and 52 | * only processes host components (DOM elements) and text nodes. 53 | * 54 | * @param {Element} parent - Parent DOM node to append children to 55 | * @param {Fiber} workInProgress - Completed fiber whose children should be appended 56 | */ 57 | function appendAllChildren(parent, workInProgress) { 58 | let node = workInProgress.child; 59 | 60 | while (node) { 61 | // If child is a host component (DOM element) or text node, append it 62 | if (node.tag === HostComponent || node.tag === HostText) { 63 | appendInitialChild(parent, node.stateNode); 64 | } else if (node.child !== null) { 65 | // If child is a component (function/class), traverse to its children 66 | node = node.child; 67 | continue; 68 | } 69 | 70 | // If we've processed all children of workInProgress, we're done 71 | if (node === workInProgress) { 72 | return; 73 | } 74 | 75 | // If current node has no sibling, go back up the tree 76 | while (node.sibling === null) { 77 | if (node.return === null || node.return === workInProgress) { 78 | return; 79 | } 80 | // Move back to parent node 81 | node = node.return; 82 | } 83 | node = node.sibling; 84 | } 85 | } 86 | function markUpdate(workInProgress) { 87 | workInProgress.flags |= Update; //给当前的fiber添加更新的副作用 88 | } 89 | /** 90 | * 在fiber(button)的完成阶段准备更新DOM 91 | * @param {*} current button老fiber 92 | * @param {*} workInProgress button的新fiber 93 | * @param {*} type 类型 94 | * @param {*} newProps 新属性 95 | */ 96 | function updateHostComponent(current, workInProgress, type, newProps) { 97 | const oldProps = current.memoizedProps; //老的属性 98 | const instance = workInProgress.stateNode; //老的DOM节点 99 | //比较新老属性,收集属性的差异 100 | const updatePayload = prepareUpdate(instance, type, oldProps, newProps); 101 | //让原生组件的新fiber更新队列等于[] 102 | workInProgress.updateQueue = updatePayload; 103 | if (updatePayload) { 104 | markUpdate(workInProgress); 105 | } 106 | } 107 | /** 108 | * 完成一个fiber节点 109 | * @param {*} current 老fiber 110 | * @param {*} workInProgress 新的构建的fiber 111 | */ 112 | export function completeWork(current, workInProgress) { 113 | const newProps = workInProgress.pendingProps; 114 | switch (workInProgress.tag) { 115 | case HostRoot: 116 | bubbleProperties(workInProgress); 117 | break; 118 | //如果完成的是原生节点的话 119 | case HostComponent: 120 | ///现在只是在处理创建或者说挂载新节点的逻辑,后面此处分进行区分是初次挂载还是更新 121 | //创建真实的DOM节点 122 | const { type } = workInProgress; 123 | //如果老fiber存在,并且老fiber上真实DOM节点,要走节点更新的逻辑 124 | if (current !== null && workInProgress.stateNode !== null) { 125 | updateHostComponent(current, workInProgress, type, newProps); 126 | if ((current.ref !== workInProgress.ref) !== null) { 127 | markRef(workInProgress); 128 | } 129 | } else { 130 | const instance = createInstance(type, newProps, workInProgress); 131 | //把自己所有的儿子都添加到自己的身上 132 | appendAllChildren(instance, workInProgress); 133 | workInProgress.stateNode = instance; 134 | finalizeInitialChildren(instance, type, newProps); 135 | if (workInProgress.ref !== null) { 136 | markRef(workInProgress); 137 | } 138 | } 139 | bubbleProperties(workInProgress); 140 | break; 141 | case FunctionComponent: 142 | bubbleProperties(workInProgress); 143 | break; 144 | case HostText: 145 | //如果完成的fiber是文本节点,那就创建真实的文本节点 146 | const newText = newProps; 147 | //创建真实的DOM节点并传入stateNode 148 | workInProgress.stateNode = createTextInstance(newText); 149 | //向上冒泡属性 150 | bubbleProperties(workInProgress); 151 | break; 152 | } 153 | } 154 | 155 | function bubbleProperties(completedWork) { 156 | let newChildLanes = NoLanes; 157 | let subtreeFlags = NoFlags; 158 | //遍历当前fiber的所有子节点,把所有的子节的副作用,以及子节点的子节点的副作用全部合并 159 | let child = completedWork.child; 160 | while (child !== null) { 161 | newChildLanes = mergeLanes( 162 | newChildLanes, 163 | mergeLanes(child.lanes, child.childLanes) 164 | ); 165 | subtreeFlags |= child.subtreeFlags; 166 | subtreeFlags |= child.flags; 167 | child = child.sibling; 168 | } 169 | completedWork.childLanes = newChildLanes; 170 | completedWork.subtreeFlags = subtreeFlags; 171 | } 172 | -------------------------------------------------------------------------------- /markdown/minHeap.md: -------------------------------------------------------------------------------- 1 | 5 | ### Min Heap 6 | 7 | 1. A min heap is a sorted complete binary tree 8 | 9 | 2. The data value of any non-terminal node is not greater than the value of its left and right child nodes 10 | 11 | 3. The root node value is the smallest among all heap node values 12 | 13 | Index relationships 14 | 15 | + Left child index = (parent index * 2) + 1 16 | 17 | + Right child index = left child index + 1 18 | 19 | + Parent index = (child index - 1) / 2 20 | 21 | 22 | 23 | ### Why use min heap 24 | 25 | 1. This is because in the min heap structure, the minimum value is at the first position, and React can quickly extract the minimum value. 26 | 27 | 2. Why does React extract the minimum value instead of the maximum value? We can think of it this way: React breaks update tasks into multiple small tasks, each small task's data structure is an object with expirationTime, where expirationTime represents the expiration time of this task. The smaller the expirationTime, the closer the expiration time, and the higher the priority of the task. Extracting the minimum value is equivalent to extracting the highest priority task. 28 | 29 | 30 | ### Min heap scheduling 31 | 32 | 33 | 1. peek() View the top of the heap 34 | 35 | 2. pop() After popping the top of the heap, need to call siftDown function to adjust the heap downward 36 | 37 | 3. push() After adding a new node, need to call siftUp function to adjust the heap upward 38 | 39 | 4. siftDown() Adjust heap structure downward to ensure min heap 40 | 41 | 5. siftUp() Need to adjust heap structure upward to ensure min heap 42 | 43 | ```javaScript 44 | //scheduler/src/SchedulerMinHeap.js 45 | /** 46 | * Add a node 47 | * @param {*} heap 48 | * @param {*} node 49 | */ 50 | export function push(heap, node) { 51 | const index = heap.length; 52 | heap.push(node); 53 | siftUp(heap, node, index); 54 | } 55 | /** 56 | * View the top node of the heap 57 | * @param {*} heap 58 | * @returns 59 | */ 60 | export function peek(heap) { 61 | return heap.length === 0 ? null : heap[0]; 62 | } 63 | /** 64 | * Pop the top node of the heap 65 | * @param {*} heap 66 | * @returns 67 | */ 68 | export function pop(heap) { 69 | if (heap.length === 0) { 70 | return null; 71 | } 72 | // Take out the first element, which is the top element of the heap 73 | const first = heap[0]; 74 | // Take out the last element, which is the tail element of the heap 75 | const last = heap.pop(); 76 | if (last !== first) { 77 | // Swap the top and tail of the heap, then adjust downward from the top 78 | heap[0] = last; 79 | siftDown(heap, last, 0); 80 | } 81 | return first; 82 | } 83 | /** 84 | * Adjust a node upward to put it in the correct position 85 | * @param {*} heap min heap 86 | * @param {*} node node 87 | * @param {*} i index 88 | * @returns 89 | */ 90 | function siftUp(heap, node, i) { 91 | let index = i; 92 | while (index > 0) { 93 | // Get parent index (child-1)/2 is equivalent to right shift, this way of writing has the advantage of direct rounding 94 | const parentIndex = index - 1 >>> 1; 95 | // Get parent node 96 | const parent = heap[parentIndex]; 97 | // Parent node is larger than child node 98 | if (compare(parent, node) > 0) { 99 | // Put child's value to parent index 100 | heap[parentIndex] = node; 101 | // Put parent's value to child index 102 | heap[index] = parent; 103 | // Let index = parent index 104 | index = parentIndex; 105 | } else { 106 | // Child node is larger than parent node 107 | return; 108 | } 109 | } 110 | } 111 | /** 112 | * Adjust a node downward to put it in the correct position 113 | * @param {*} heap min heap 114 | * @param {*} node node 115 | * @param {*} i index 116 | * @returns 117 | */ 118 | function siftDown(heap, node, i) { 119 | let index = i; 120 | const length = heap.length; 121 | // Similar to binary search, but this is sorting 122 | const halfLength = length >>> 1; 123 | while (index < halfLength) { 124 | // Left index 125 | const leftIndex = (index + 1) * 2 - 1; 126 | // Left child node 127 | const left = heap[leftIndex]; 128 | // Right index 129 | const rightIndex = leftIndex + 1; 130 | // Right node 131 | const right = heap[rightIndex]; 132 | // Compare and move index 133 | if (compare(left, node) < 0) { 134 | // Right node is smaller than left node, swap parent and right node 135 | if (rightIndex < length && compare(right, left) < 0) { 136 | // Current index is right node 137 | heap[index] = right; 138 | // Right index set as parent node 139 | heap[rightIndex] = node; 140 | // Index equals right index 141 | index = rightIndex; 142 | } else { 143 | // Current index set as left node 144 | heap[index] = left; 145 | // Left index set as parent node 146 | heap[leftIndex] = node; 147 | // Index equals left index 148 | index = leftIndex; 149 | } 150 | // Right node is smaller than parent node 151 | } else if (rightIndex < length && compare(right, node) < 0) { 152 | // Current index is right node 153 | heap[index] = right; 154 | // Right index is parent node 155 | heap[rightIndex] = node; 156 | // Index set as right index 157 | index = rightIndex; 158 | } else { 159 | return; 160 | } 161 | } 162 | } 163 | function compare(a, b) { 164 | const diff = a.sortIndex - b.sortIndex; 165 | return diff !== 0 ? diff : a.id - b.id; 166 | } 167 | ``` 168 | 169 | ```javaScript 170 | const { push, pop, peek } = require('./SchedulerMinHeap'); 171 | let heap = []; 172 | let id = 1; 173 | push(heap, { sortIndex: 1,id:id++ }); 174 | push(heap, { sortIndex: 2,id:id++ }); 175 | push(heap, { sortIndex: 3,id:id++ }); 176 | console.log(peek(heap)); 177 | push(heap, { sortIndex: 4 }); 178 | push(heap, { sortIndex: 5 }); 179 | push(heap, { sortIndex: 6 }); 180 | push(heap, { sortIndex: 7 }); 181 | console.log(peek(heap)); 182 | pop(heap); 183 | console.log(peek(heap)); 184 | ``` -------------------------------------------------------------------------------- /src/react-reconciler/src/ReactFiberClassUpdateQueue.js: -------------------------------------------------------------------------------- 1 | import { enqueueConcurrentClassUpdate } from "./ReactFiberConcurrentUpdates"; 2 | import assign from "shared/assign"; 3 | import { NoLanes, mergeLanes, isSubsetOfLanes } from "./ReactFiberLane"; 4 | 5 | export const UpdateState = 0; 6 | 7 | export function initialUpdateQueue(fiber) { 8 | // Create a new update queue 9 | // pending is actually a circular linked list 10 | const queue = { 11 | baseState: fiber.memoizedState, // The current fiber state before this update, updates will be calculated based on it 12 | firstBaseUpdate: null, // The head of the previously skipped update linked list saved on this fiber before this update 13 | lastBaseUpdate: null, // The tail of the previously skipped update linked list saved on this fiber before this update 14 | shared: { 15 | pending: null, 16 | }, 17 | }; 18 | fiber.updateQueue = queue; 19 | } 20 | 21 | export function createUpdate(lane) { 22 | const update = { tag: UpdateState, lane, next: null }; 23 | return update; 24 | } 25 | export function enqueueUpdate(fiber, update, lane) { 26 | // Get the update queue 27 | const updateQueue = fiber.updateQueue; 28 | // Get the shared queue 29 | const sharedQueue = updateQueue.shared; 30 | return enqueueConcurrentClassUpdate(fiber, sharedQueue, update, lane); 31 | } 32 | /** 33 | * Calculate the latest state based on the old state and updates in the update queue 34 | * @param {*} workInProgress The fiber to be calculated 35 | */ 36 | export function processUpdateQueue(workInProgress, nextProps, renderLanes) { 37 | const queue = workInProgress.updateQueue; 38 | // Old linked list head 39 | let firstBaseUpdate = queue.firstBaseUpdate; 40 | // Old linked list tail 41 | let lastBaseUpdate = queue.lastBaseUpdate; 42 | // New linked list tail 43 | const pendingQueue = queue.shared.pending; 44 | // Merge new and old linked lists into a single linked list 45 | if (pendingQueue !== null) { 46 | queue.shared.pending = null; 47 | // New linked list tail 48 | const lastPendingUpdate = pendingQueue; 49 | // New linked list tail 50 | const firstPendingUpdate = lastPendingUpdate.next; 51 | // Cut off the old linked list to make it a single linked list, circular linked list becomes single linked list 52 | lastPendingUpdate.next = null; 53 | // If there is no old linked list 54 | if (lastBaseUpdate === null) { 55 | // Point to the new linked list head 56 | firstBaseUpdate = firstPendingUpdate; 57 | } else { 58 | lastBaseUpdate.next = firstPendingUpdate; 59 | } 60 | lastBaseUpdate = lastPendingUpdate; 61 | } 62 | // If the linked list is not empty firstBaseUpdate=>lastBaseUpdate 63 | if (firstBaseUpdate !== null) { 64 | // State before the last skipped update 65 | let newState = queue.baseState; 66 | // Lanes of updates that have not been executed yet 67 | let newLanes = NoLanes; 68 | let newBaseState = null; 69 | let newFirstBaseUpdate = null; 70 | let newLastBaseUpdate = null; 71 | let update = firstBaseUpdate; //updateA 72 | do { 73 | // Get the lane of this update 74 | const updateLane = update.lane; 75 | // If updateLane is not a subset of renderLanes, it means this render does not need to process this update, so this update needs to be skipped 76 | if (!isSubsetOfLanes(renderLanes, updateLane)) { 77 | // Clone this update 78 | const clone = { 79 | id: update.id, 80 | lane: updateLane, 81 | payload: update.payload, 82 | }; 83 | // If the new skipped base linked list is empty, it means the current update is the first skipped update 84 | if (newLastBaseUpdate === null) { 85 | // Let both the head and tail of the new skipped linked list point to this first skipped update 86 | newFirstBaseUpdate = newLastBaseUpdate = clone; 87 | // Calculate and save the new baseState as the state when this update is skipped 88 | newBaseState = newState; // "" 89 | } else { 90 | newLastBaseUpdate = newLastBaseUpdate.next = clone; 91 | } 92 | // If there are skipped updates, merge the lanes of the skipped updates into newLanes, 93 | // Finally, newLanes will be assigned to fiber.lanes 94 | newLanes = mergeLanes(newLanes, updateLane); 95 | } else { 96 | // It means there are already skipped updates 97 | if (newLastBaseUpdate !== null) { 98 | const clone = { 99 | id: update.id, 100 | lane: 0, 101 | payload: update.payload, 102 | }; 103 | newLastBaseUpdate = newLastBaseUpdate.next = clone; 104 | } 105 | newState = getStateFromUpdate(update, newState); 106 | } 107 | update = update.next; 108 | } while (update); 109 | // If there are no skipped updates 110 | if (!newLastBaseUpdate) { 111 | newBaseState = newState; 112 | } 113 | queue.baseState = newBaseState; 114 | queue.firstBaseUpdate = newFirstBaseUpdate; 115 | queue.lastBaseUpdate = newLastBaseUpdate; 116 | workInProgress.lanes = newLanes; 117 | // After this render is complete, it will check if there are any non-zero lanes on this fiber, and if so, it will render again 118 | workInProgress.memoizedState = newState; 119 | } 120 | } 121 | /** 122 | * state=0 update=>1 update=2 123 | * Calculate new state based on old state and update 124 | * @param {*} update The update object, there are actually many types 125 | * @param {*} prevState 126 | */ 127 | function getStateFromUpdate(update, prevState, nextProps) { 128 | switch (update.tag) { 129 | case UpdateState: 130 | const { payload } = update; 131 | let partialState; 132 | if (typeof payload === "function") { 133 | partialState = payload.call(null, prevState, nextProps); 134 | } else { 135 | partialState = payload; 136 | } 137 | return assign({}, prevState, partialState); 138 | } 139 | } 140 | 141 | export function cloneUpdateQueue(current, workInProgress) { 142 | const workInProgressQueue = workInProgress.updateQueue; 143 | const currentQueue = current.updateQueue; 144 | // If the new queue and the old queue are not the same object 145 | if (currentQueue === workInProgressQueue) { 146 | const clone = { 147 | baseState: currentQueue.baseState, 148 | firstBaseUpdate: currentQueue.firstBaseUpdate, 149 | firstBaseUpdate: currentQueue.firstBaseUpdate, 150 | lastBaseUpdate: currentQueue.lastBaseUpdate, 151 | shared: currentQueue.shared, 152 | }; 153 | workInProgress.updateQueue = clone; 154 | } 155 | } 156 | -------------------------------------------------------------------------------- /src/scheduler/src/forks/Scheduler.js: -------------------------------------------------------------------------------- 1 | /** 2 | * React Scheduler - Task Scheduling and Time Slicing 3 | * 4 | * This module implements React's scheduler which manages task execution with 5 | * priority-based scheduling and time slicing. It ensures that high-priority 6 | * tasks can interrupt low-priority ones and that the main thread remains 7 | * responsive by yielding control back to the browser. 8 | * 9 | * Key features: 10 | * - Priority-based task scheduling 11 | * - Time slicing with configurable frame intervals 12 | * - Task queue management using min heap 13 | * - Cooperative scheduling with shouldYield 14 | * 15 | * @module Scheduler 16 | */ 17 | 18 | import { push, peek, pop } from "./SchedulerMinHeap"; 19 | import { 20 | ImmediatePriority, 21 | UserBlockingPriority, 22 | NormalPriority, 23 | LowPriority, 24 | IdlePriority, 25 | } from "./SchedulerPriorities"; 26 | 27 | /** 28 | * Get Current Time 29 | * 30 | * Returns the current time in milliseconds using high-resolution timer. 31 | * 32 | * @returns {number} Current time in milliseconds 33 | */ 34 | function getCurrentTime() { 35 | return performance.now(); 36 | } 37 | 38 | // Maximum 31-bit signed integer value 39 | var maxSigned31BitInt = 1073741823; 40 | 41 | // Priority timeout constants (in milliseconds) 42 | var IMMEDIATE_PRIORITY_TIMEOUT = -1; // Expires immediately 43 | var USER_BLOCKING_PRIORITY_TIMEOUT = 250; // Expires in 250ms 44 | var NORMAL_PRIORITY_TIMEOUT = 5000; // Expires in 5 seconds 45 | var LOW_PRIORITY_TIMEOUT = 10000; // Expires in 10 seconds 46 | var IDLE_PRIORITY_TIMEOUT = maxSigned31BitInt; // Never expires 47 | 48 | // Scheduler state 49 | let taskIdCounter = 1; // Task ID counter for unique identification 50 | const taskQueue = []; // Min heap of scheduled tasks 51 | let scheduleHostCallback = null; // Host callback scheduler function 52 | let startTime = -1; // Work start time for yielding calculations 53 | let currentTask = null; // Currently executing task 54 | 55 | // Time slicing configuration 56 | // React requests 5ms per frame for task execution 57 | // If tasks don't complete within 5ms, React yields control back to browser 58 | const frameInterval = 5; 59 | 60 | const channel = new MessageChannel(); 61 | var port2 = channel.port2; 62 | var port1 = channel.port1; 63 | port1.onmessage = performWorkUntilDeadline; 64 | 65 | /** 66 | * 按优先级执行任务 67 | * @param {*} priorityLevel 68 | * @param {*} callback 69 | */ 70 | function scheduleCallback(priorityLevel, callback) { 71 | // 获取当前的时候 72 | const currentTime = getCurrentTime(); 73 | // 此任务的开时间 74 | const startTime = currentTime; 75 | //超时时间 76 | let timeout; 77 | //根据优先级计算过期的时间 78 | switch (priorityLevel) { 79 | case ImmediatePriority: 80 | timeout = IMMEDIATE_PRIORITY_TIMEOUT; // -1 81 | break; 82 | case UserBlockingPriority: 83 | timeout = USER_BLOCKING_PRIORITY_TIMEOUT; // 250ms 84 | break; 85 | case IdlePriority: 86 | timeout = IDLE_PRIORITY_TIMEOUT; //1073741823 87 | break; 88 | case LowPriority: 89 | timeout = LOW_PRIORITY_TIMEOUT; //10000 90 | break; 91 | case NormalPriority: 92 | default: 93 | timeout = NORMAL_PRIORITY_TIMEOUT; //5000 94 | break; 95 | } 96 | //计算此任务的过期时间 97 | const expirationTime = startTime + timeout; 98 | const newTask = { 99 | id: taskIdCounter++, 100 | callback, //回调函数或者说任务函数 101 | priorityLevel, //优先级别 102 | startTime, //任务的开始时间 103 | expirationTime, //任务的过期时间 104 | sortIndex: expirationTime, //排序依赖 105 | }; 106 | //向任务最小堆里添加任务,排序的依据是过期时间 107 | push(taskQueue, newTask); 108 | //flushWork执行工作,刷新工作,执行任务,司机接人 109 | requestHostCallback(workLoop); 110 | return newTask; 111 | } 112 | function shouldYieldToHost() { 113 | //用当前时间减去开始的时间就是过去的时间 114 | const timeElapsed = getCurrentTime() - startTime; 115 | //如果流逝或者说经过的时间小于5毫秒,那就不需要放弃执行 116 | if (timeElapsed < frameInterval) { 117 | return false; 118 | } //否则就是表示5毫秒用完了,需要放弃执行 119 | return true; 120 | } 121 | function workLoop(startTime) { 122 | let currentTime = startTime; 123 | //取出优先级最高的任务 局长 124 | currentTask = peek(taskQueue); 125 | while (currentTask !== null) { 126 | //如果此任务的过期时间小于当前时间,也就是说没有过期,并且需要放弃执行 时间片到期 127 | if (currentTask.expirationTime > currentTime && shouldYieldToHost()) { 128 | //跳出工作循环 129 | break; 130 | } 131 | //取出当前的任务中的回调函数 performConcurrentWorkOnRoot 132 | const callback = currentTask.callback; 133 | if (typeof callback === "function") { 134 | currentTask.callback = null; 135 | //执行工作,如果返回新的函数,则表示当前的工作没有完成 136 | const didUserCallbackTimeout = currentTask.expirationTime <= currentTime; 137 | const continuationCallback = callback(didUserCallbackTimeout); 138 | if (typeof continuationCallback === "function") { 139 | currentTask.callback = continuationCallback; 140 | return true; //还有任务要执行 141 | } 142 | //如果此任务已经完成,则不需要再继续执行了,可以把此任务弹出 143 | if (currentTask === peek(taskQueue)) { 144 | pop(taskQueue); 145 | } 146 | } else { 147 | pop(taskQueue); 148 | } 149 | //如果当前的任务执行完了,或者当前任务不合法,取出下一个任务执行 150 | currentTask = peek(taskQueue); 151 | } 152 | //如果循环结束还有未完成的任务,那就表示hasMoreWork=true 153 | if (currentTask !== null) { 154 | return true; 155 | } 156 | //没有任何要完成的任务了 157 | return false; 158 | } 159 | function requestHostCallback(workLoop) { 160 | //先缓存回调函数 161 | scheduleHostCallback = workLoop; 162 | //执行工作直到截止时间 163 | schedulePerformWorkUntilDeadline(); 164 | } 165 | function schedulePerformWorkUntilDeadline() { 166 | port2.postMessage(null); 167 | } 168 | function performWorkUntilDeadline() { 169 | if (scheduleHostCallback) { 170 | // 先获取开始执行任务的时间 171 | //表示时间片的开始 172 | startTime = getCurrentTime(); 173 | // 是否有更多的工作要做 174 | let hasMoreWork = true; 175 | try { 176 | //执行 flushWork ,并判断有没有返回值 177 | hasMoreWork = scheduleHostCallback(startTime); 178 | } finally { 179 | //执行完以后如果为true,说明还有更多工作要做 180 | if (hasMoreWork) { 181 | //继续执行 182 | schedulePerformWorkUntilDeadline(); 183 | } else { 184 | scheduleHostCallback = null; 185 | } 186 | } 187 | } 188 | } 189 | function unstable_cancelCallback(task) { 190 | task.callback = null; 191 | } 192 | export { 193 | scheduleCallback as unstable_scheduleCallback, 194 | shouldYieldToHost as unstable_shouldYield, 195 | ImmediatePriority as unstable_ImmediatePriority, 196 | UserBlockingPriority as unstable_UserBlockingPriority, 197 | NormalPriority as unstable_NormalPriority, 198 | LowPriority as unstable_LowPriority, 199 | IdlePriority as unstable_IdlePriority, 200 | unstable_cancelCallback, 201 | getCurrentTime as now, 202 | }; 203 | 204 | /* 205 | 开始执行任务队列中的任务 206 | 207 | function flushWork(startTime) { 208 | return workLoop(startTime); 209 | } 210 | */ 211 | -------------------------------------------------------------------------------- /src/react-reconciler/src/ReactFiber.js: -------------------------------------------------------------------------------- 1 | /** 2 | * React Fiber - Core Fiber Node Implementation 3 | * 4 | * This module contains the Fiber node structure and related utilities. 5 | * Fiber is React's reconciliation algorithm that enables interruptible rendering, 6 | * priority-based scheduling, and efficient updates. 7 | * 8 | * @module ReactFiber 9 | */ 10 | 11 | import { 12 | HostComponent, 13 | HostRoot, 14 | IndeterminateComponent, 15 | HostText, 16 | } from "./ReactWorkTags"; 17 | import { NoFlags } from "./ReactFiberFlags"; 18 | import { NoLanes } from "./ReactFiberLane"; 19 | 20 | /** 21 | * Fiber Node Constructor 22 | * 23 | * Creates a new Fiber node which represents a unit of work in React's reconciliation. 24 | * Each React element corresponds to a Fiber node in the Fiber tree. 25 | * 26 | * @param {number} tag - Fiber type (FunctionComponent: 0, ClassComponent: 1, HostComponent: 5, HostRoot: 3) 27 | * @param {any} pendingProps - New props waiting to be processed 28 | * @param {string|number|null} key - Unique identifier for reconciliation 29 | */ 30 | export function FiberNode(tag, pendingProps, key) { 31 | // Instance properties 32 | this.tag = tag; // Fiber type identifier 33 | this.key = key; // Unique key for reconciliation 34 | this.type = null; // Component type (function, class, or DOM tag name) 35 | this.stateNode = null; // Reference to DOM node or component instance 36 | 37 | // Fiber tree structure - forms a linked list tree 38 | this.return = null; // Parent fiber (return pointer) 39 | this.child = null; // First child fiber 40 | this.sibling = null; // Next sibling fiber 41 | 42 | // Props and state management 43 | this.pendingProps = pendingProps; // New props waiting to be applied 44 | this.memoizedProps = null; // Props from the last completed render 45 | 46 | // State varies by fiber type: 47 | // - Class components: component instance state 48 | // - Host root: elements to render 49 | // - Function components: hooks state 50 | this.memoizedState = null; 51 | 52 | // Update queue for managing state updates 53 | this.updateQueue = null; 54 | 55 | // Side effects - indicate what operations need to be performed 56 | this.flags = NoFlags; // Side effects for this fiber 57 | this.subtreeFlags = NoFlags; // Side effects for child fibers 58 | 59 | // Double buffering - enables efficient updates 60 | this.alternate = null; // Alternate fiber for double buffering 61 | 62 | // Additional properties 63 | this.index = 0; // Index of this fiber among siblings 64 | this.deletions = null; // Child fibers to be deleted 65 | this.lanes = NoLanes; // Priority lanes for this fiber 66 | this.childLanes = NoLanes; // Priority lanes for child fibers 67 | this.ref = null; // Ref object or callback 68 | } 69 | // We use a double buffering pooling technique because we know that we'll only ever need at most two versions of a tree. 70 | // We pool the "other" unused node that we're free to reuse. 71 | 72 | // This is lazily created to avoid allocating 73 | // extra objects for things that are never updated. It also allow us to 74 | // reclaim the extra memory if needed. 75 | export function createFiber(tag, pendingProps, key) { 76 | return new FiberNode(tag, pendingProps, key); 77 | } 78 | export function createHostRootFiber() { 79 | return createFiber(HostRoot, null, null); 80 | } 81 | /** 82 | * Create Work-in-Progress Fiber 83 | * 84 | * Creates a work-in-progress fiber based on the current fiber and new props. 85 | * This implements React's double buffering technique where we maintain two fiber trees: 86 | * - Current tree: represents the current state 87 | * - Work-in-progress tree: represents the next state being built 88 | * 89 | * Reuse has two meanings: 90 | * 1. Reuse the old fiber object structure 91 | * 2. Reuse the actual DOM nodes when possible 92 | * 93 | * @param {Fiber} current - The current fiber (old fiber) 94 | * @param {any} pendingProps - New props to be applied 95 | * @returns {Fiber} Work-in-progress fiber 96 | */ 97 | export function createWorkInProgress(current, pendingProps) { 98 | let workInProgress = current.alternate; 99 | 100 | if (workInProgress === null) { 101 | // No alternate exists, create a new fiber 102 | workInProgress = createFiber(current.tag, pendingProps, current.key); 103 | workInProgress.type = current.type; 104 | workInProgress.stateNode = current.stateNode; 105 | 106 | // Establish bidirectional connection for double buffering 107 | workInProgress.alternate = current; 108 | current.alternate = workInProgress; 109 | } else { 110 | // Reuse existing alternate fiber 111 | workInProgress.pendingProps = pendingProps; 112 | workInProgress.type = current.type; 113 | 114 | // Reset side effects 115 | workInProgress.flags = NoFlags; 116 | workInProgress.subtreeFlags = NoFlags; 117 | workInProgress.deletions = null; 118 | } 119 | 120 | // Copy properties from current fiber 121 | workInProgress.child = current.child; 122 | workInProgress.memoizedProps = current.memoizedProps; 123 | workInProgress.memoizedState = current.memoizedState; 124 | workInProgress.updateQueue = current.updateQueue; 125 | workInProgress.sibling = current.sibling; 126 | workInProgress.index = current.index; 127 | workInProgress.ref = current.ref; 128 | workInProgress.flags = current.flags; 129 | workInProgress.lanes = current.lanes; 130 | workInProgress.childLanes = current.childLanes; 131 | 132 | return workInProgress; 133 | } 134 | /** 135 | * Create Fiber from React Element 136 | * 137 | * Converts a React element (virtual DOM node) into a Fiber node. 138 | * This is used during the reconciliation process to build the Fiber tree. 139 | * 140 | * @param {ReactElement} element - React element to convert 141 | * @returns {Fiber} New fiber node 142 | */ 143 | export function createFiberFromElement(element) { 144 | const { type, key, props: pendingProps } = element; 145 | return createFiberFromTypeAndProps(type, key, pendingProps); 146 | } 147 | 148 | /** 149 | * Create Fiber from Type and Props 150 | * 151 | * Creates a fiber node based on the component type and props. 152 | * Determines the appropriate fiber tag based on the component type. 153 | * 154 | * @param {string|function|class} type - Component type 155 | * @param {string|number|null} key - Unique key 156 | * @param {object} pendingProps - Component props 157 | * @returns {Fiber} New fiber node 158 | */ 159 | function createFiberFromTypeAndProps(type, key, pendingProps) { 160 | let tag = IndeterminateComponent; // Default for function components 161 | 162 | // If type is a string (span, div, etc.), this is a host component 163 | if (typeof type === "string") { 164 | tag = HostComponent; 165 | } 166 | 167 | const fiber = createFiber(tag, pendingProps, key); 168 | fiber.type = type; 169 | return fiber; 170 | } 171 | 172 | /** 173 | * Create Fiber from Text Content 174 | * 175 | * Creates a fiber node for text content. Text nodes are leaf nodes 176 | * in the Fiber tree and represent actual text content in the DOM. 177 | * 178 | * @param {string} content - Text content 179 | * @returns {Fiber} Text fiber node 180 | */ 181 | export function createFiberFromText(content) { 182 | return createFiber(HostText, content, null); 183 | } 184 | -------------------------------------------------------------------------------- /src/react-reconciler/src/ReactFiberLane.js: -------------------------------------------------------------------------------- 1 | /** 2 | * React Fiber Lane - Priority-Based Scheduling System 3 | * 4 | * This module implements React's lane-based priority system for concurrent rendering. 5 | * Lanes are represented as binary flags, allowing efficient bitwise operations for 6 | * priority management, merging, and comparison. 7 | * 8 | * Key concepts: 9 | * - Lower numeric values = higher priority 10 | * - Lanes can be combined using bitwise OR 11 | * - Each lane represents a different type of work or priority level 12 | * - Enables fine-grained control over update scheduling 13 | * 14 | * Lane types (from highest to lowest priority): 15 | * - SyncLane: Synchronous updates (highest priority) 16 | * - InputContinuous: User input events (high priority) 17 | * - Default: Normal updates (medium priority) 18 | * - Idle: Background updates (lowest priority) 19 | * 20 | * @module ReactFiberLane 21 | */ 22 | 23 | import { allowConcurrentByDefault } from "shared/ReactFeatureFlags"; 24 | 25 | // Total number of available lanes (31-bit system) 26 | export const TotalLanes = 31; 27 | 28 | // Lane constants - binary representation for efficient bitwise operations 29 | export const NoLanes = 0b0000000000000000000000000000000; // No work scheduled 30 | export const NoLane = 0b0000000000000000000000000000000; // Alias for NoLanes 31 | 32 | // Synchronous lane - highest priority, cannot be interrupted 33 | export const SyncLane = 0b0000000000000000000000000000001; 34 | 35 | // Input continuous lanes - for user interactions 36 | export const InputContinuousHydrationLane = 0b0000000000000000000000000000010; 37 | export const InputContinuousLane = 0b0000000000000000000000000000100; 38 | 39 | // Default lanes - for normal updates 40 | export const DefaultHydrationLane = 0b0000000000000000000000000001000; 41 | export const DefaultLane = 0b0000000000000000000000000010000; 42 | 43 | // Special purpose lanes 44 | export const SelectiveHydrationLane = 0b0001000000000000000000000000000; 45 | export const IdleHydrationLane = 0b0010000000000000000000000000000; 46 | export const IdleLane = 0b0100000000000000000000000000000; 47 | export const OffscreenLane = 0b1000000000000000000000000000000; 48 | 49 | // Mask for non-idle lanes (excludes idle and offscreen work) 50 | const NonIdleLanes = 0b0001111111111111111111111111111; 51 | 52 | // Timestamp constants 53 | export const NoTimestamp = -1; // Indicates no timestamp set 54 | 55 | /** 56 | * Mark Root Updated 57 | * 58 | * Marks a root as having pending updates in the specified lane. 59 | * This adds the update lane to the root's pending lanes using bitwise OR. 60 | * 61 | * @param {FiberRoot} root - Fiber root to mark as updated 62 | * @param {Lane} updateLane - Lane containing the update 63 | */ 64 | export function markRootUpdated(root, updateLane) { 65 | // Add the update lane to pending lanes (bitwise OR combines lanes) 66 | root.pendingLanes |= updateLane; 67 | } 68 | 69 | /** 70 | * Get Next Lanes 71 | * 72 | * Determines which lanes should be processed next based on priority. 73 | * This function implements React's scheduling logic, considering: 74 | * - Pending work lanes 75 | * - Currently rendering work 76 | * - Priority levels 77 | * 78 | * @param {FiberRoot} root - Fiber root to get lanes for 79 | * @param {Lanes} wipLanes - Currently work-in-progress lanes 80 | * @returns {Lanes} Next lanes to process 81 | */ 82 | export function getNextLanes(root, wipLanes) { 83 | // Get all lanes with pending updates 84 | const pendingLanes = root.pendingLanes; 85 | if (pendingLanes === NoLanes) { 86 | return NoLanes; 87 | } 88 | 89 | // Get the highest priority lanes from pending work 90 | const nextLanes = getHighestPriorityLanes(pendingLanes); 91 | 92 | if (wipLanes !== NoLane && wipLanes !== nextLanes) { 93 | // If new lanes have lower priority than current work, continue current work 94 | // (Higher numeric value = lower priority) 95 | if (nextLanes > wipLanes) { 96 | return wipLanes; 97 | } 98 | } 99 | 100 | return nextLanes; 101 | } 102 | export function getHighestPriorityLanes(lanes) { 103 | return getHighestPriorityLane(lanes); 104 | } 105 | //找到最右边的1 只能返回一个车道 106 | export function getHighestPriorityLane(lanes) { 107 | return lanes & -lanes; 108 | } 109 | export function includesNonIdleWork(lanes) { 110 | return (lanes & NonIdleLanes) !== NoLanes; 111 | } 112 | /** 113 | * 源码此处的逻辑有大的改变动 114 | * 以前 115 | * pendingLanes= 001100 116 | * 找到最右边的1 000100 117 | * nextLanes 000111 118 | * 119 | * 现在的源码已经改了 120 | * pendingLanes= 001100 121 | * 找到最右边的1 000100 122 | * update 000010 123 | * 那是不是意味着以前是不检测车道上有没有任务的,就是先拿优先级再检测? 124 | */ 125 | 126 | export function isSubsetOfLanes(set, subset) { 127 | return (set & subset) === subset; 128 | } 129 | export function mergeLanes(a, b) { 130 | return a | b; 131 | } 132 | 133 | export function includesBlockingLane(root, lanes) { 134 | //如果允许默认并行渲染 135 | if (allowConcurrentByDefault) { 136 | return false; 137 | } 138 | const SyncDefaultLanes = InputContinuousLane | DefaultLane; 139 | return (lanes & SyncDefaultLanes) !== NoLane; 140 | } 141 | /** 142 | * 取是左侧的1的索引 143 | * 00011000 144 | * 7-3=4 145 | */ 146 | function pickArbitraryLaneIndex(lanes) { 147 | //clz32返回最左侧的1的左边0的个数 148 | // 000100010 149 | return 31 - Math.clz32(lanes); 150 | } 151 | 152 | export function markStarvedLanesAsExpired(root, currentTime) { 153 | //获取当前有更新赛 道 154 | const pendingLanes = root.pendingLanes; 155 | //记录每个赛道上的过期时间 156 | const expirationTimes = root.expirationTimes; 157 | let lanes = pendingLanes; 158 | while (lanes > 0) { 159 | //获取最左侧的1的索引 160 | const index = pickArbitraryLaneIndex(lanes); 161 | const lane = 1 << index; 162 | const expirationTime = expirationTimes[index]; 163 | //如果此赛道上没有过期时间,说明没有为此车道设置过期时间 164 | if (expirationTime === NoTimestamp) { 165 | expirationTimes[index] = computeExpirationTime(lane, currentTime); 166 | //如果此车道的过期时间已经小于等于当前时间了 167 | } else if (expirationTime <= currentTime) { 168 | //把此车道添加到过期车道里 169 | root.expiredLanes |= lane; 170 | console.log( 171 | "expirationTime", 172 | expirationTime, 173 | "currentTime", 174 | currentTime, 175 | root.expiredLanes 176 | ); 177 | } 178 | lanes &= ~lane; 179 | } 180 | } 181 | function computeExpirationTime(lane, currentTime) { 182 | switch (lane) { 183 | case SyncLane: 184 | case InputContinuousLane: 185 | return currentTime + 250; 186 | case DefaultLane: 187 | return currentTime + 5000; 188 | case IdleLane: 189 | return NoTimestamp; 190 | default: 191 | return NoTimestamp; 192 | } 193 | } 194 | 195 | export function createLaneMap(initial) { 196 | const laneMap = []; 197 | for (let i = 0; i < TotalLanes; i++) { 198 | laneMap.push(initial); 199 | } 200 | return laneMap; 201 | } 202 | export function includesExpiredLane(root, lanes) { 203 | return (lanes & root.expiredLanes) !== NoLanes; 204 | } 205 | export function markRootFinished(root, remainingLanes) { 206 | //pendingLanes根上所有的将要被渲染的车道 1和2 207 | //remainingLanes 2 208 | //noLongerPendingLanes指的是已经更新过的lane 209 | const noLongerPendingLanes = root.pendingLanes & ~remainingLanes; 210 | root.pendingLanes = remainingLanes; 211 | const expirationTimes = root.expirationTimes; 212 | let lanes = noLongerPendingLanes; 213 | while (lanes > 0) { 214 | //获取最左侧的1的索引 215 | const index = pickArbitraryLaneIndex(lanes); 216 | const lane = 1 << index; 217 | //清除已经计算过的车道的过期时间 218 | expirationTimes[index] = NoTimestamp; 219 | lanes &= ~lane; 220 | } 221 | } 222 | -------------------------------------------------------------------------------- /src/react-dom-bindings/src/events/DOMPluginEventSystem.js: -------------------------------------------------------------------------------- 1 | /** 2 | * DOM Plugin Event System - React's Event Delegation Implementation 3 | * 4 | * This module implements React's synthetic event system with event delegation. 5 | * Instead of attaching event listeners to individual DOM elements, React uses 6 | * a single event delegation system at the root container level. 7 | * 8 | * Key features: 9 | * - Event delegation at root container level 10 | * - Cross-browser event normalization 11 | * - Synthetic event creation and dispatch 12 | * - Support for both capture and bubble phases 13 | * - Priority-based event handling 14 | * 15 | * Benefits: 16 | * - Better performance (fewer event listeners) 17 | * - Consistent behavior across browsers 18 | * - Dynamic event handling for added/removed elements 19 | * - Memory efficiency 20 | * 21 | * @module DOMPluginEventSystem 22 | */ 23 | 24 | import { allNativeEvents } from "./EventRegistry"; 25 | import * as SimpleEventPlugin from "./plugins/SimpleEventPlugin"; 26 | import { IS_CAPTURE_PHASE } from "./EventSystemFlags"; 27 | import { createEventListenerWrapperWithPriority } from "./ReactDOMEventListener"; 28 | import { 29 | addEventCaptureListener, 30 | addEventBubbleListener, 31 | } from "./EventListener"; 32 | import getEventTarget from "./getEventTarget"; 33 | import { HostComponent } from "react-reconciler/src/ReactWorkTags"; 34 | import getListener from "./getListener"; 35 | 36 | // Register all simple events (click, change, etc.) 37 | SimpleEventPlugin.registerEvents(); 38 | 39 | // Unique marker to track if container already has listeners 40 | const listeningMarker = `_reactListening` + Math.random().toString(36).slice(2); 41 | /** 42 | * Listen to All Supported Events 43 | * 44 | * Sets up event delegation for all supported events on the root container. 45 | * This function is called once per root container and registers listeners 46 | * for both capture and bubble phases of all native events. 47 | * 48 | * The event delegation approach means that instead of attaching listeners 49 | * to individual elements, we attach them to the root and handle all events 50 | * from there, determining the actual target through event bubbling/capturing. 51 | * 52 | * @param {Element} rootContainerElement - Root DOM element (e.g., div#root) 53 | */ 54 | export function listenToAllSupportedEvents(rootContainerElement) { 55 | // Only set up listeners once per container 56 | if (!rootContainerElement[listeningMarker]) { 57 | rootContainerElement[listeningMarker] = true; 58 | 59 | // Register listeners for all native events (click, change, focus, etc.) 60 | allNativeEvents.forEach((domEventName) => { 61 | // Register for capture phase (events flow down from root to target) 62 | listenToNativeEvent(domEventName, true, rootContainerElement); 63 | // Register for bubble phase (events flow up from target to root) 64 | listenToNativeEvent(domEventName, false, rootContainerElement); 65 | }); 66 | } 67 | } 68 | /** 69 | * Listen to Native Event 70 | * 71 | * Registers a native event listener on the target DOM element for a specific 72 | * event type and phase (capture or bubble). This creates the actual DOM event 73 | * listener that will handle all events of this type. 74 | * 75 | * @param {string} domEventName - Native event name (e.g., 'click', 'change') 76 | * @param {boolean} isCapturePhaseListener - Whether this is for capture phase 77 | * @param {Element} target - Target DOM element (root container) 78 | */ 79 | export function listenToNativeEvent( 80 | domEventName, 81 | isCapturePhaseListener, 82 | target 83 | ) { 84 | // Set event system flags: 0 for bubble phase, IS_CAPTURE_PHASE for capture 85 | let eventSystemFlags = 0; 86 | if (isCapturePhaseListener) { 87 | eventSystemFlags |= IS_CAPTURE_PHASE; 88 | } 89 | addTrappedEventListener( 90 | target, 91 | domEventName, 92 | eventSystemFlags, 93 | isCapturePhaseListener 94 | ); 95 | } 96 | 97 | function addTrappedEventListener( 98 | targetContainer, 99 | domEventName, 100 | eventSystemFlags, 101 | isCapturePhaseListener 102 | ) { 103 | const listener = createEventListenerWrapperWithPriority( 104 | targetContainer, 105 | domEventName, 106 | eventSystemFlags 107 | ); 108 | if (isCapturePhaseListener) { 109 | addEventCaptureListener(targetContainer, domEventName, listener); 110 | } else { 111 | addEventBubbleListener(targetContainer, domEventName, listener); 112 | } 113 | } 114 | 115 | export function dispatchEventForPluginEventSystem( 116 | domEventName, 117 | eventSystemFlags, 118 | nativeEvent, 119 | targetInst, 120 | targetContainer 121 | ) { 122 | dispatchEventForPlugins( 123 | domEventName, 124 | eventSystemFlags, 125 | nativeEvent, 126 | targetInst, 127 | targetContainer 128 | ); 129 | } 130 | 131 | function dispatchEventForPlugins( 132 | domEventName, 133 | eventSystemFlags, 134 | nativeEvent, 135 | targetInst, 136 | targetContainer 137 | ) { 138 | const nativeEventTarget = getEventTarget(nativeEvent); 139 | //派发事件的数组 140 | const dispatchQueue = []; 141 | extractEvents( 142 | dispatchQueue, 143 | domEventName, 144 | targetInst, 145 | nativeEvent, 146 | nativeEventTarget, 147 | eventSystemFlags, 148 | targetContainer 149 | ); 150 | processDispatchQueue(dispatchQueue, eventSystemFlags); 151 | } 152 | function processDispatchQueue(dispatchQueue, eventSystemFlags) { 153 | //判断是否在捕获阶段 154 | const inCapturePhase = (eventSystemFlags & IS_CAPTURE_PHASE) !== 0; 155 | for (let i = 0; i < dispatchQueue.length; i++) { 156 | const { event, listeners } = dispatchQueue[i]; 157 | processDispatchQueueItemsInOrder(event, listeners, inCapturePhase); 158 | } 159 | } 160 | function executeDispatch(event, listener, currentTarget) { 161 | //合成事件实例currentTarget是在不断的变化的 162 | // event nativeEventTarget 它的是原始的事件源,是永远不变的 163 | // event currentTarget 当前的事件源,它是会随着事件回调的执行不断变化的 164 | event.currentTarget = currentTarget; 165 | listener(event); 166 | } 167 | function processDispatchQueueItemsInOrder( 168 | event, 169 | dispatchListeners, 170 | inCapturePhase 171 | ) { 172 | if (inCapturePhase) { 173 | //dispatchListeners[子,父] 174 | for (let i = dispatchListeners.length - 1; i >= 0; i--) { 175 | const { listener, currentTarget } = dispatchListeners[i]; 176 | if (event.isPropagationStopped()) { 177 | return; 178 | } 179 | executeDispatch(event, listener, currentTarget); 180 | } 181 | } else { 182 | for (let i = 0; i < dispatchListeners.length; i++) { 183 | const { listener, currentTarget } = dispatchListeners[i]; 184 | if (event.isPropagationStopped()) { 185 | return; 186 | } 187 | executeDispatch(event, listener, currentTarget); 188 | } 189 | } 190 | } 191 | function extractEvents( 192 | dispatchQueue, 193 | domEventName, 194 | targetInst, 195 | nativeEvent, 196 | nativeEventTarget, 197 | eventSystemFlags, 198 | targetContainer 199 | ) { 200 | SimpleEventPlugin.extractEvents( 201 | dispatchQueue, 202 | domEventName, 203 | targetInst, 204 | nativeEvent, 205 | nativeEventTarget, 206 | eventSystemFlags, 207 | targetContainer 208 | ); 209 | } 210 | 211 | export function accumulateSinglePhaseListeners( 212 | targetFiber, 213 | reactName, 214 | nativeEventType, 215 | isCapturePhase 216 | ) { 217 | const captureName = reactName + "Capture"; 218 | const reactEventName = isCapturePhase ? captureName : reactName; 219 | const listeners = []; 220 | let instance = targetFiber; 221 | while (instance !== null) { 222 | const { stateNode, tag } = instance; //stateNode 当前的执行回调的DOM节点 223 | if (tag === HostComponent && stateNode !== null) { 224 | const listener = getListener(instance, reactEventName); 225 | if (listener) { 226 | listeners.push(createDispatchListener(instance, listener, stateNode)); 227 | } 228 | } 229 | instance = instance.return; 230 | } 231 | return listeners; 232 | } 233 | function createDispatchListener(instance, listener, currentTarget) { 234 | return { instance, listener, currentTarget }; 235 | } 236 | -------------------------------------------------------------------------------- /src/react-reconciler/src/ReactFiberCommitWork.js: -------------------------------------------------------------------------------- 1 | /** 2 | * React Fiber Commit Work - Commit Phase Implementation 3 | * 4 | * This module implements the commit phase of React's reconciliation process. 5 | * The commit phase is synchronous and applies all the side effects that were 6 | * collected during the render phase to the actual DOM. 7 | * 8 | * The commit phase has three sub-phases: 9 | * 1. Before Mutation: getSnapshotBeforeUpdate, async scheduling 10 | * 2. Mutation: DOM insertions, updates, deletions 11 | * 3. Layout: componentDidMount, componentDidUpdate, useLayoutEffect 12 | * 13 | * Key responsibilities: 14 | * - Apply DOM mutations (insert, update, delete nodes) 15 | * - Execute lifecycle methods and effects 16 | * - Handle refs attachment/detachment 17 | * - Manage focus and selection 18 | * 19 | * @module ReactFiberCommitWork 20 | */ 21 | 22 | import { 23 | appendChild, 24 | insertBefore, 25 | commitUpdate, 26 | removeChild, 27 | } from "react-dom-bindings/src/client/ReactDOMHostConfig"; 28 | import { 29 | Placement, 30 | MutationMask, 31 | Update, 32 | Passive, 33 | LayoutMask, 34 | Ref, 35 | } from "./ReactFiberFlags"; 36 | import { 37 | FunctionComponent, 38 | HostComponent, 39 | HostRoot, 40 | HostText, 41 | } from "./ReactWorkTags"; 42 | import { 43 | HasEffect as HookHasEffect, 44 | Passive as HookPassive, 45 | Layout as HookLayout, 46 | } from "./ReactHookEffectTags"; 47 | 48 | // Global state for tracking the current host parent during commit 49 | let hostParent = null; 50 | /** 51 | * 提交删除副作用 52 | * @param {*} root 根节点 53 | * @param {*} returnFiber 父fiber 54 | * @param {*} deletedFiber 删除的fiber 55 | */ 56 | function commitDeletionEffects(root, returnFiber, deletedFiber) { 57 | let parent = returnFiber; 58 | //一直向上找,找到真实的DOM节点为此 59 | findParent: while (parent !== null) { 60 | switch (parent.tag) { 61 | case HostComponent: { 62 | hostParent = parent.stateNode; 63 | break findParent; 64 | } 65 | case HostRoot: { 66 | hostParent = parent.stateNode.containerInfo; 67 | break findParent; 68 | } 69 | } 70 | parent = parent.return; 71 | } 72 | commitDeletionEffectsOnFiber(root, returnFiber, deletedFiber); 73 | hostParent = null; 74 | } 75 | function commitDeletionEffectsOnFiber( 76 | finishedRoot, 77 | nearestMountedAncestor, 78 | deletedFiber 79 | ) { 80 | switch (deletedFiber.tag) { 81 | case HostComponent: 82 | case HostText: { 83 | //当要删除一个节点的时候,要先删除它的子节点 84 | recursivelyTraverseDeletionEffects( 85 | finishedRoot, 86 | nearestMountedAncestor, 87 | deletedFiber 88 | ); 89 | //再把自己删除 90 | if (hostParent !== null) { 91 | removeChild(hostParent, deletedFiber.stateNode); 92 | } 93 | break; 94 | } 95 | default: 96 | break; 97 | } 98 | } 99 | function recursivelyTraverseDeletionEffects( 100 | finishedRoot, 101 | nearestMountedAncestor, 102 | parent 103 | ) { 104 | let child = parent.child; 105 | while (child !== null) { 106 | commitDeletionEffectsOnFiber(finishedRoot, nearestMountedAncestor, child); 107 | child = child.sibling; 108 | } 109 | } 110 | /** 111 | * 递归遍历处理变更的作用 112 | * @param {*} root 根节点 113 | * @param {*} parentFiber 父fiber 114 | */ 115 | function recursivelyTraverseMutationEffects(root, parentFiber) { 116 | //先把父fiber上该删除的节点都删除 117 | const deletions = parentFiber.deletions; 118 | if (deletions !== null) { 119 | for (let i = 0; i < deletions.length; i++) { 120 | const childToDelete = deletions[i]; 121 | commitDeletionEffects(root, parentFiber, childToDelete); 122 | } 123 | } 124 | //再去处理剩下的子节点 125 | if (parentFiber.subtreeFlags & MutationMask) { 126 | let { child } = parentFiber; 127 | while (child !== null) { 128 | commitMutationEffectsOnFiber(child, root); 129 | child = child.sibling; 130 | } 131 | } 132 | } 133 | function commitReconciliationEffects(finishedWork) { 134 | const { flags } = finishedWork; 135 | //如果此fiber要执行插入操作的话 136 | if (flags & Placement) { 137 | //进行插入操作,也就是把此fiber对应的真实DOM节点添加到父真实DOM节点上 138 | commitPlacement(finishedWork); 139 | //把flags里的Placement删除 140 | finishedWork.flags & ~Placement; 141 | } 142 | } 143 | function isHostParent(fiber) { 144 | return fiber.tag === HostComponent || fiber.tag == HostRoot; //div#root 145 | } 146 | function getHostParentFiber(fiber) { 147 | let parent = fiber.return; 148 | while (parent !== null) { 149 | if (isHostParent(parent)) { 150 | return parent; 151 | } 152 | parent = parent.return; 153 | } 154 | parent; 155 | } 156 | 157 | /** 158 | * 把子节点对应的真实DOM插入到父节点DOM中 159 | * @param {*} node 将要插入的fiber节点 160 | * @param {*} parent 父真实DOM节点 161 | */ 162 | function insertOrAppendPlacementNode(node, before, parent) { 163 | const { tag } = node; 164 | //判断此fiber对应的节点是不是真实DOM节点 165 | const isHost = tag === HostComponent || tag === HostText; 166 | //如果是的话直接插入 167 | if (isHost) { 168 | const { stateNode } = node; 169 | if (before) { 170 | insertBefore(parent, stateNode, before); 171 | } else { 172 | appendChild(parent, stateNode); 173 | } 174 | } else { 175 | //如果node不是真实的DOM节点,获取它的大儿子 176 | const { child } = node; 177 | if (child !== null) { 178 | //把大儿子添加到父亲DOM节点里面去 179 | insertOrAppendPlacementNode(child, before, parent); 180 | let { sibling } = child; 181 | while (sibling !== null) { 182 | insertOrAppendPlacementNode(sibling, before, parent); 183 | sibling = sibling.sibling; 184 | } 185 | } 186 | } 187 | } 188 | /** 189 | * 找到要插入的锚点 190 | * 找到可以插在它的前面的那个fiber对应的真实DOM 191 | * @param {*} fiber 192 | */ 193 | function getHostSibling(fiber) { 194 | let node = fiber; 195 | siblings: while (true) { 196 | while (node.sibling === null) { 197 | if (node.return === null || isHostParent(node.return)) { 198 | return null; 199 | } 200 | node = node.return; 201 | } 202 | node = node.sibling; 203 | //如果弟弟不是原生节点也不是文本节点 204 | while (node.tag !== HostComponent && node.tag !== HostText) { 205 | //如果此节点是一个将要插入的新的节点,找它的弟弟 206 | if (node.flags & Placement) { 207 | continue siblings; 208 | } else { 209 | node = node.child; 210 | } 211 | } 212 | if (!(node.flags & Placement)) { 213 | return node.stateNode; 214 | } 215 | } 216 | } 217 | /** 218 | * 把此fiber的真实DOM插入到父DOM里 219 | * @param {*} finishedWork 220 | */ 221 | function commitPlacement(finishedWork) { 222 | const parentFiber = getHostParentFiber(finishedWork); 223 | switch (parentFiber.tag) { 224 | case HostRoot: { 225 | const parent = parentFiber.stateNode.containerInfo; 226 | const before = getHostSibling(finishedWork); //获取最近的弟弟真实DOM节点 227 | insertOrAppendPlacementNode(finishedWork, before, parent); 228 | break; 229 | } 230 | case HostComponent: { 231 | const parent = parentFiber.stateNode; 232 | const before = getHostSibling(finishedWork); 233 | insertOrAppendPlacementNode(finishedWork, before, parent); 234 | break; 235 | } 236 | default: 237 | break; 238 | } 239 | } 240 | /** 241 | * 遍历fiber树,执行fiber上的副作用 242 | * @param {*} finishedWork fiber节点 243 | * @param {*} root 根节点 244 | */ 245 | export function commitMutationEffectsOnFiber(finishedWork, root) { 246 | const current = finishedWork.alternate; 247 | const flags = finishedWork.flags; 248 | switch (finishedWork.tag) { 249 | case FunctionComponent: { 250 | //先遍历它们的子节点,处理它们的子节点上的副作用 251 | recursivelyTraverseMutationEffects(root, finishedWork); 252 | //再处理自己身上的副作用 253 | commitReconciliationEffects(finishedWork); 254 | if (flags & Update) { 255 | commitHookEffectListUnmount(HookHasEffect | HookLayout, finishedWork); 256 | } 257 | break; 258 | } 259 | case HostRoot: 260 | case HostText: { 261 | //先遍历它们的子节点,处理它们的子节点上的副作用 262 | recursivelyTraverseMutationEffects(root, finishedWork); 263 | //再处理自己身上的副作用 264 | commitReconciliationEffects(finishedWork); 265 | break; 266 | } 267 | case HostComponent: { 268 | //先遍历它们的子节点,处理它们的子节点上的副作用 269 | recursivelyTraverseMutationEffects(root, finishedWork); 270 | //再处理自己身上的副作用 271 | commitReconciliationEffects(finishedWork); 272 | if (flags & Ref) { 273 | commitAttachRef(finishedWork); 274 | } 275 | //处理DOM更新 276 | if (flags & Update) { 277 | //获取真实DOM 278 | const instance = finishedWork.stateNode; 279 | //更新真实DOM 280 | if (instance !== null) { 281 | const newProps = finishedWork.memoizedProps; 282 | const oldProps = current !== null ? current.memoizedProps : newProps; 283 | const type = finishedWork.type; 284 | const updatePayload = finishedWork.updateQueue; 285 | finishedWork.updateQueue = null; 286 | if (updatePayload) { 287 | commitUpdate( 288 | instance, 289 | updatePayload, 290 | type, 291 | oldProps, 292 | newProps, 293 | finishedWork 294 | ); 295 | } 296 | } 297 | } 298 | break; 299 | } 300 | default: 301 | break; 302 | } 303 | } 304 | function commitAttachRef(finishedWork) { 305 | const ref = finishedWork.ref; 306 | if (ref !== null) { 307 | const instance = finishedWork.stateNode; 308 | if (typeof ref === "function") { 309 | ref(instance); 310 | } else { 311 | ref.current = instance; 312 | } 313 | } 314 | } 315 | 316 | export function commitPassiveUnmountEffects(finishedWork) { 317 | commitPassiveUnmountOnFiber(finishedWork); 318 | } 319 | function commitPassiveUnmountOnFiber(finishedWork) { 320 | const flags = finishedWork.flags; 321 | switch (finishedWork.tag) { 322 | case HostRoot: { 323 | recursivelyTraversePassiveUnmountEffects(finishedWork); 324 | break; 325 | } 326 | case FunctionComponent: { 327 | recursivelyTraversePassiveUnmountEffects(finishedWork); 328 | if (flags & Passive) { 329 | //1024 330 | commitHookPassiveUnmountEffects( 331 | finishedWork, 332 | HookHasEffect | HookPassive 333 | ); 334 | } 335 | break; 336 | } 337 | } 338 | } 339 | function recursivelyTraversePassiveUnmountEffects(parentFiber) { 340 | if (parentFiber.subtreeFlags & Passive) { 341 | let child = parentFiber.child; 342 | while (child !== null) { 343 | commitPassiveUnmountOnFiber(child); 344 | child = child.sibling; 345 | } 346 | } 347 | } 348 | function commitHookPassiveUnmountEffects(finishedWork, hookFlags) { 349 | commitHookEffectListUnmount(hookFlags, finishedWork); 350 | } 351 | function commitHookEffectListUnmount(flags, finishedWork) { 352 | const updateQueue = finishedWork.updateQueue; 353 | const lastEffect = updateQueue !== null ? updateQueue.lastEffect : null; 354 | if (lastEffect !== null) { 355 | //获取 第一个effect 356 | const firstEffect = lastEffect.next; 357 | let effect = firstEffect; 358 | do { 359 | //如果此 effect类型和传入的相同,都是 9 HookHasEffect | PassiveEffect 360 | if ((effect.tag & flags) === flags) { 361 | const destroy = effect.destroy; 362 | if (destroy !== undefined) { 363 | destroy(); 364 | } 365 | } 366 | effect = effect.next; 367 | } while (effect !== firstEffect); 368 | } 369 | } 370 | export function commitPassiveMountEffects(root, finishedWork) { 371 | commitPassiveMountOnFiber(root, finishedWork); 372 | } 373 | function commitPassiveMountOnFiber(finishedRoot, finishedWork) { 374 | const flags = finishedWork.flags; 375 | switch (finishedWork.tag) { 376 | case HostRoot: { 377 | recursivelyTraversePassiveMountEffects(finishedRoot, finishedWork); 378 | break; 379 | } 380 | case FunctionComponent: { 381 | recursivelyTraversePassiveMountEffects(finishedRoot, finishedWork); 382 | if (flags & Passive) { 383 | //1024 384 | commitHookPassiveMountEffects( 385 | finishedWork, 386 | HookHasEffect | HookPassive 387 | ); 388 | } 389 | break; 390 | } 391 | } 392 | } 393 | function recursivelyTraversePassiveMountEffects(root, parentFiber) { 394 | if (parentFiber.subtreeFlags & Passive) { 395 | let child = parentFiber.child; 396 | while (child !== null) { 397 | commitPassiveMountOnFiber(root, child); 398 | child = child.sibling; 399 | } 400 | } 401 | } 402 | function commitHookPassiveMountEffects(finishedWork, hookFlags) { 403 | commitHookEffectListMount(hookFlags, finishedWork); 404 | } 405 | function commitHookEffectListMount(flags, finishedWork) { 406 | const updateQueue = finishedWork.updateQueue; 407 | const lastEffect = updateQueue !== null ? updateQueue.lastEffect : null; 408 | if (lastEffect !== null) { 409 | //获取 第一个effect 410 | const firstEffect = lastEffect.next; 411 | let effect = firstEffect; 412 | do { 413 | //如果此 effect类型和传入的相同,都是 9 HookHasEffect | PassiveEffect 414 | if ((effect.tag & flags) === flags) { 415 | const create = effect.create; 416 | effect.destroy = create(); 417 | } 418 | effect = effect.next; 419 | } while (effect !== firstEffect); 420 | } 421 | } 422 | 423 | export function commitLayoutEffects(finishedWork, root) { 424 | //老的根fiber 425 | const current = finishedWork.alternate; 426 | commitLayoutEffectOnFiber(root, current, finishedWork); 427 | } 428 | function commitLayoutEffectOnFiber(finishedRoot, current, finishedWork) { 429 | const flags = finishedWork.flags; 430 | switch (finishedWork.tag) { 431 | case HostRoot: { 432 | recursivelyTraverseLayoutEffects(finishedRoot, finishedWork); 433 | break; 434 | } 435 | case FunctionComponent: { 436 | recursivelyTraverseLayoutEffects(finishedRoot, finishedWork); 437 | if (flags & LayoutMask) { 438 | // LayoutMask=Update=4 439 | commitHookLayoutEffects(finishedWork, HookHasEffect | HookLayout); 440 | } 441 | break; 442 | } 443 | } 444 | } 445 | function commitHookLayoutEffects(finishedWork, hookFlags) { 446 | commitHookEffectListMount(hookFlags, finishedWork); 447 | } 448 | function recursivelyTraverseLayoutEffects(root, parentFiber) { 449 | if (parentFiber.subtreeFlags & LayoutMask) { 450 | let child = parentFiber.child; 451 | while (child !== null) { 452 | const current = child.alternate; 453 | commitLayoutEffectOnFiber(root, current, child); 454 | child = child.sibling; 455 | } 456 | } 457 | } 458 | -------------------------------------------------------------------------------- /src/react-reconciler/src/ReactChildFiber.js: -------------------------------------------------------------------------------- 1 | /** 2 | * React Child Fiber - Reconciliation Algorithm Implementation 3 | * 4 | * This module implements React's reconciliation algorithm (also known as the "diffing" algorithm). 5 | * It's responsible for comparing the current fiber tree with the new virtual DOM tree and 6 | * determining what changes need to be made to efficiently update the DOM. 7 | * 8 | * Key concepts: 9 | * - Reconciliation: Process of comparing old and new trees 10 | * - Side effects: Tracking what DOM operations need to be performed 11 | * - Keys: Used for efficient list reconciliation 12 | * - Placement/Deletion flags: Mark fibers for DOM operations 13 | * 14 | * The reconciler handles: 15 | * - Single element reconciliation 16 | * - Array/list reconciliation with keys 17 | * - Text node reconciliation 18 | * - Deletion of removed elements 19 | * 20 | * @module ReactChildFiber 21 | */ 22 | 23 | import { REACT_ELEMENT_TYPE } from "shared/ReactSymbols"; 24 | import { 25 | createFiberFromElement, 26 | createFiberFromText, 27 | createWorkInProgress, 28 | } from "./ReactFiber"; 29 | import { Placement, ChildDeletion } from "./ReactFiberFlags"; 30 | import isArray from "shared/isArray"; 31 | import { HostText } from "./ReactWorkTags"; 32 | 33 | /** 34 | * Create Child Reconciler 35 | * 36 | * Factory function that creates a child reconciler with configurable side effect tracking. 37 | * During mount phase, side effects aren't tracked since everything is new. 38 | * During update phase, side effects are tracked to determine what DOM operations are needed. 39 | * 40 | * @param {boolean} shouldTrackSideEffects - Whether to track side effects (placement, deletion) 41 | * @returns {Function} Reconciler function for processing children 42 | */ 43 | function createChildReconciler(shouldTrackSideEffects) { 44 | /** 45 | * Use Fiber 46 | * 47 | * Reuses an existing fiber by creating a work-in-progress copy with new props. 48 | * This is an optimization to avoid creating new fiber objects when possible. 49 | * 50 | * @param {Fiber} fiber - Existing fiber to reuse 51 | * @param {any} pendingProps - New props for the fiber 52 | * @returns {Fiber} Work-in-progress fiber 53 | */ 54 | function useFiber(fiber, pendingProps) { 55 | const clone = createWorkInProgress(fiber, pendingProps); 56 | clone.index = 0; 57 | clone.sibling = null; 58 | return clone; 59 | } 60 | 61 | /** 62 | * Delete Child 63 | * 64 | * Marks a child fiber for deletion during the commit phase. 65 | * Adds the fiber to the parent's deletions array and sets the ChildDeletion flag. 66 | * 67 | * @param {Fiber} returnFiber - Parent fiber 68 | * @param {Fiber} childToDelete - Child fiber to mark for deletion 69 | */ 70 | function deleteChild(returnFiber, childToDelete) { 71 | if (!shouldTrackSideEffects) return; 72 | 73 | const deletions = returnFiber.deletions; 74 | if (deletions === null) { 75 | returnFiber.deletions = [childToDelete]; 76 | returnFiber.flags |= ChildDeletion; 77 | } else { 78 | returnFiber.deletions.push(childToDelete); 79 | } 80 | } 81 | 82 | /** 83 | * Delete Remaining Children 84 | * 85 | * Marks all remaining children starting from currentFirstChild for deletion. 86 | * This is used when the new children list is shorter than the old one. 87 | * 88 | * @param {Fiber} returnFiber - Parent fiber 89 | * @param {Fiber} currentFirstChild - First child to start deleting from 90 | * @returns {null} Always returns null 91 | */ 92 | function deleteRemainingChildren(returnFiber, currentFirstChild) { 93 | if (!shouldTrackSideEffects) return null; 94 | 95 | let childToDelete = currentFirstChild; 96 | while (childToDelete !== null) { 97 | deleteChild(returnFiber, childToDelete); 98 | childToDelete = childToDelete.sibling; 99 | } 100 | return null; 101 | } 102 | /** 103 | * Reconcile Single Element 104 | * 105 | * Reconciles a single React element with the existing fiber tree. This function 106 | * implements the core diffing logic for single elements, comparing keys and types 107 | * to determine if a fiber can be reused or if a new one needs to be created. 108 | * 109 | * Algorithm: 110 | * 1. Compare keys - if different, delete old fiber and create new one 111 | * 2. Compare types - if same key but different type, delete old and create new 112 | * 3. If key and type match, reuse existing fiber with new props 113 | * 114 | * @param {Fiber} returnFiber - Parent fiber (e.g., div#root fiber) 115 | * @param {Fiber} currentFirstChild - Existing first child fiber 116 | * @param {ReactElement} element - New virtual DOM element to reconcile 117 | * @returns {Fiber} New or reused first child fiber 118 | */ 119 | function reconcileSingleElement(returnFiber, currentFirstChild, element) { 120 | // Get the key from new virtual DOM element (used for reconciliation) 121 | const key = element.key; 122 | let child = currentFirstChild; 123 | while (child !== null) { 124 | //判断此老fiber对应的key和新的虚拟DOM对象的key是否一样 null===null 125 | if (child.key === key) { 126 | //判断老fiber对应的类型和新虚拟DOM元素对应的类型是否相同 127 | if (child.type === element.type) { 128 | // p div 129 | deleteRemainingChildren(returnFiber, child.sibling); 130 | //如果key一样,类型也一样,则认为此节点可以复用 131 | const existing = useFiber(child, element.props); 132 | existing.ref = element.ref; 133 | existing.return = returnFiber; 134 | return existing; 135 | } else { 136 | //如果找到一key一样老fiber,但是类型不一样,不能此老fiber,把剩下的全部删除 137 | deleteRemainingChildren(returnFiber, child); 138 | } 139 | } else { 140 | deleteChild(returnFiber, child); 141 | } 142 | child = child.sibling; 143 | } 144 | 145 | //因为我们现实的初次挂载,老节点currentFirstChild肯定是没有的,所以可以直接根据虚拟DOM创建新的Fiber节点 146 | const created = createFiberFromElement(element); 147 | created.ref = element.ref; 148 | created.return = returnFiber; 149 | return created; 150 | } 151 | /** 152 | * 设置副作用 153 | * @param {*} newFiber 154 | * @returns 155 | */ 156 | function placeSingleChild(newFiber) { 157 | //说明要添加副作用 158 | if (shouldTrackSideEffects && newFiber.alternate === null) { 159 | //要在最后的提交阶段插入此节点 React渲染分成渲染(创建Fiber树)和提交(更新真实DOM)二个阶段 160 | newFiber.flags |= Placement; 161 | } 162 | return newFiber; 163 | } 164 | function createChild(returnFiber, newChild) { 165 | if ( 166 | (typeof newChild === "string" && newChild !== "") || 167 | typeof newChild === "number" 168 | ) { 169 | const created = createFiberFromText(`${newChild}`); 170 | created.return = returnFiber; 171 | return created; 172 | } 173 | if (typeof newChild === "object" && newChild !== null) { 174 | switch (newChild.$$typeof) { 175 | case REACT_ELEMENT_TYPE: { 176 | const created = createFiberFromElement(newChild); 177 | created.ref = newChild.ref; 178 | created.return = returnFiber; 179 | return created; 180 | } 181 | default: 182 | break; 183 | } 184 | } 185 | return null; 186 | } 187 | function placeChild(newFiber, lastPlacedIndex, newIdx) { 188 | //指定新的fiber在新的挂载索引 189 | newFiber.index = newIdx; 190 | //如果不需要跟踪副作用 191 | if (!shouldTrackSideEffects) { 192 | return lastPlacedIndex; 193 | } 194 | //获取它的老fiber 195 | const current = newFiber.alternate; 196 | //如果有,说明这是一个更新的节点,有老的真实DOM。 197 | if (current !== null) { 198 | const oldIndex = current.index; 199 | //如果找到的老fiber的索引比lastPlacedIndex要小,则老fiber对应的DOM节点需要移动 200 | if (oldIndex < lastPlacedIndex) { 201 | newFiber.flags |= Placement; 202 | return lastPlacedIndex; 203 | } else { 204 | return oldIndex; 205 | } 206 | } else { 207 | //如果没有,说明这是一个新的节点,需要插入 208 | newFiber.flags |= Placement; 209 | return lastPlacedIndex; 210 | } 211 | } 212 | function updateElement(returnFiber, current, element) { 213 | const elementType = element.type; 214 | if (current !== null) { 215 | //判断是否类型一样,则表示key和type都一样,可以复用老的fiber和真实DOM 216 | if (current.type === elementType) { 217 | const existing = useFiber(current, element.props); 218 | existing.ref = element.ref; 219 | existing.return = returnFiber; 220 | return existing; 221 | } 222 | } 223 | const created = createFiberFromElement(element); 224 | created.ref = element.ref; 225 | created.return = returnFiber; 226 | return created; 227 | } 228 | function updateSlot(returnFiber, oldFiber, newChild) { 229 | const key = oldFiber !== null ? oldFiber.key : null; 230 | if (newChild !== null && typeof newChild === "object") { 231 | switch (newChild.$$typeof) { 232 | case REACT_ELEMENT_TYPE: { 233 | //如果key一样,进入更新元素的逻辑 234 | if (newChild.key === key) { 235 | return updateElement(returnFiber, oldFiber, newChild); 236 | } 237 | } 238 | default: 239 | return null; 240 | } 241 | } 242 | return null; 243 | } 244 | function mapRemainingChildren(returnFiber, currentFirstChild) { 245 | const existingChildren = new Map(); 246 | let existingChild = currentFirstChild; 247 | while (existingChild != null) { 248 | //如果有key用key,如果没有key使用索引 249 | if (existingChild.key !== null) { 250 | existingChildren.set(existingChild.key, existingChild); 251 | } else { 252 | existingChildren.set(existingChild.index, existingChild); 253 | } 254 | existingChild = existingChild.sibling; 255 | } 256 | return existingChildren; 257 | } 258 | function updateTextNode(returnFiber, current, textContent) { 259 | if (current === null || current.tag !== HostText) { 260 | const created = createFiberFromText(textContent); 261 | created.return = returnFiber; 262 | return created; 263 | } else { 264 | const existing = useFiber(current, textContent); 265 | existing.return = returnFiber; 266 | return existing; 267 | } 268 | } 269 | function updateFromMap(existingChildren, returnFiber, newIdx, newChild) { 270 | if ( 271 | (typeof newChild === "string" && newChild !== "") || 272 | typeof newChild === "number" 273 | ) { 274 | const matchedFiber = existingChildren.get(newIdx) || null; 275 | return updateTextNode(returnFiber, matchedFiber, "" + newChild); 276 | } 277 | if (typeof newChild === "object" && newChild !== null) { 278 | switch (newChild.$$typeof) { 279 | case REACT_ELEMENT_TYPE: { 280 | const matchedFiber = 281 | existingChildren.get( 282 | newChild.key === null ? newIdx : newChild.key 283 | ) || null; 284 | return updateElement(returnFiber, matchedFiber, newChild); 285 | } 286 | } 287 | } 288 | } 289 | function reconcileChildrenArray(returnFiber, currentFirstChild, newChildren) { 290 | let resultingFirstChild = null; //返回的第一个新儿子 291 | let previousNewFiber = null; //上一个的一个新的儿fiber 292 | let newIdx = 0; //用来遍历新的虚拟DOM的索引 293 | let oldFiber = currentFirstChild; //第一个老fiber 294 | let nextOldFiber = null; //下一个第fiber 295 | let lastPlacedIndex = 0; //上一个不需要移动的老节点的索引 296 | // 开始第一轮循环 如果老fiber有值,新的虚拟DOM也有值 297 | for (; oldFiber !== null && newIdx < newChildren.length; newIdx++) { 298 | //先暂下一个老fiber 299 | nextOldFiber = oldFiber.sibling; 300 | //试图更新或者试图复用老的fiber 301 | const newFiber = updateSlot(returnFiber, oldFiber, newChildren[newIdx]); 302 | if (newFiber === null) { 303 | break; 304 | } 305 | if (shouldTrackSideEffects) { 306 | //如果有老fiber,但是新的fiber并没有成功复用老fiber和老的真实DOM,那就删除老fiber,在提交阶段会删除真实DOM 307 | if (oldFiber && newFiber.alternate === null) { 308 | deleteChild(returnFiber, oldFiber); 309 | } 310 | } 311 | //指定新fiber的位置 312 | lastPlacedIndex = placeChild(newFiber, lastPlacedIndex, newIdx); 313 | if (previousNewFiber === null) { 314 | resultingFirstChild = newFiber; //li(A).sibling=p(B).sibling=>li(C) 315 | } else { 316 | previousNewFiber.sibling = newFiber; 317 | } 318 | previousNewFiber = newFiber; 319 | oldFiber = nextOldFiber; 320 | } 321 | //新的虚拟DOM已经循环完毕,3=>2 322 | if (newIdx === newChildren.length) { 323 | //删除剩下的老fiber 324 | deleteRemainingChildren(returnFiber, oldFiber); 325 | return resultingFirstChild; 326 | } 327 | if (oldFiber === null) { 328 | //如果老的 fiber已经没有了, 新的虚拟DOM还有,进入插入新节点的逻辑 329 | for (; newIdx < newChildren.length; newIdx++) { 330 | const newFiber = createChild(returnFiber, newChildren[newIdx]); 331 | if (newFiber === null) continue; 332 | lastPlacedIndex = placeChild(newFiber, lastPlacedIndex, newIdx); 333 | //如果previousNewFiber为null,说明这是第一个fiber 334 | if (previousNewFiber === null) { 335 | resultingFirstChild = newFiber; //这个newFiber就是大儿子 336 | } else { 337 | //否则说明不是大儿子,就把这个newFiber添加上一个子节点后面 338 | previousNewFiber.sibling = newFiber; 339 | } 340 | //让newFiber成为最后一个或者说上一个子fiber 341 | previousNewFiber = newFiber; 342 | } 343 | } 344 | // 开始处理移动的情况 345 | const existingChildren = mapRemainingChildren(returnFiber, oldFiber); 346 | //开始遍历剩下的虚拟DOM子节点 347 | for (; newIdx < newChildren.length; newIdx++) { 348 | const newFiber = updateFromMap( 349 | existingChildren, 350 | returnFiber, 351 | newIdx, 352 | newChildren[newIdx] 353 | ); 354 | if (newFiber !== null) { 355 | if (shouldTrackSideEffects) { 356 | //如果要跟踪副作用,并且有老fiber 357 | if (newFiber.alternate !== null) { 358 | existingChildren.delete( 359 | newFiber.key === null ? newIdx : newFiber.key 360 | ); 361 | } 362 | } 363 | //指定新的fiber存放位置 ,并且给lastPlacedIndex赋值 364 | lastPlacedIndex = placeChild(newFiber, lastPlacedIndex, newIdx); 365 | if (previousNewFiber === null) { 366 | resultingFirstChild = newFiber; //这个newFiber就是大儿子 367 | } else { 368 | //否则说明不是大儿子,就把这个newFiber添加上一个子节点后面 369 | previousNewFiber.sibling = newFiber; 370 | } 371 | //让newFiber成为最后一个或者说上一个子fiber 372 | previousNewFiber = newFiber; 373 | } 374 | } 375 | if (shouldTrackSideEffects) { 376 | //等全部处理完后,删除map中所有剩下的老fiber 377 | existingChildren.forEach((child) => deleteChild(returnFiber, child)); 378 | } 379 | return resultingFirstChild; 380 | } 381 | /** 382 | * 比较子Fibers DOM-DIFF 就是用老的子fiber链表和新的虚拟DOM进行比较的过程 383 | * @param {*} returnFiber 新的父Fiber 384 | * @param {*} currentFirstChild 老fiber第一个子fiber current一般来说指的是老 385 | * @param {*} newChild 新的子虚拟DOM h1虚拟DOM 386 | */ 387 | function reconcileChildFibers(returnFiber, currentFirstChild, newChild) { 388 | //现在需要处理更新的逻辑了,处理dom diff 389 | //现在暂时只考虑新的节点只有一个的情况 390 | if (typeof newChild === "object" && newChild !== null) { 391 | switch (newChild.$$typeof) { 392 | case REACT_ELEMENT_TYPE: 393 | return placeSingleChild( 394 | reconcileSingleElement(returnFiber, currentFirstChild, newChild) 395 | ); 396 | default: 397 | break; 398 | } 399 | } 400 | //newChild [hello文本节点,span虚拟DOM元素] 401 | if (isArray(newChild)) { 402 | return reconcileChildrenArray(returnFiber, currentFirstChild, newChild); 403 | } 404 | return null; 405 | } 406 | return reconcileChildFibers; 407 | } 408 | //有老父fiber更新的时候用这个 409 | export const reconcileChildFibers = createChildReconciler(true); 410 | //如果没有老父fiber,初次挂载的时候用这个 411 | export const mountChildFibers = createChildReconciler(false); 412 | -------------------------------------------------------------------------------- /src/react-reconciler/src/ReactFiberWorkLoop.js: -------------------------------------------------------------------------------- 1 | /** 2 | * React Fiber Work Loop - Core Rendering Engine 3 | * 4 | * This module contains the main work loop that drives React's rendering process. 5 | * It implements the concurrent rendering algorithm with time slicing, priority 6 | * scheduling, and interruptible rendering. 7 | * 8 | * The work loop is responsible for: 9 | * - Scheduling and executing render work 10 | * - Managing work-in-progress fiber tree 11 | * - Handling interruptions and yielding 12 | * - Coordinating commit phase operations 13 | * 14 | * @module ReactFiberWorkLoop 15 | */ 16 | 17 | import { 18 | scheduleCallback as Scheduler_scheduleCallback, 19 | shouldYield, 20 | ImmediatePriority as ImmediateSchedulerPriority, 21 | UserBlockingPriority as UserBlockingSchedulerPriority, 22 | NormalPriority as NormalSchedulerPriority, 23 | IdlePriority as IdleSchedulerPriority, 24 | cancelCallback as Scheduler_cancelCallback, 25 | now, 26 | } from "./scheduler"; 27 | import { createWorkInProgress } from "./ReactFiber"; 28 | import { beginWork } from "./ReactFiberBeginWork"; 29 | import { completeWork } from "./ReactFiberCompleteWork"; 30 | import { NoFlags, MutationMask, Passive } from "./ReactFiberFlags"; 31 | import { 32 | commitMutationEffectsOnFiber, // Execute DOM operations 33 | commitPassiveUnmountEffects, // Execute cleanup functions 34 | commitPassiveMountEffects, // Execute effect functions 35 | commitLayoutEffects, 36 | } from "./ReactFiberCommitWork"; 37 | import { finishQueueingConcurrentUpdates } from "./ReactFiberConcurrentUpdates"; 38 | import { 39 | NoLanes, 40 | markRootUpdated, 41 | getNextLanes, 42 | getHighestPriorityLane, 43 | SyncLane, 44 | includesBlockingLane, 45 | NoLane, 46 | markStarvedLanesAsExpired, 47 | includesExpiredLane, 48 | markRootFinished, 49 | NoTimestamp, 50 | mergeLanes, 51 | } from "./ReactFiberLane"; 52 | import { 53 | getCurrentUpdatePriority, 54 | lanesToEventPriority, 55 | DiscreteEventPriority, 56 | ContinuousEventPriority, 57 | DefaultEventPriority, 58 | IdleEventPriority, 59 | setCurrentUpdatePriority, 60 | } from "./ReactEventPriorities"; 61 | import { getCurrentEventPriority } from "react-dom-bindings/src/client/ReactDOMHostConfig"; 62 | import { 63 | scheduleSyncCallback, 64 | flushSyncCallbacks, 65 | } from "./ReactFiberSyncTaskQueue"; 66 | 67 | // Global work loop state 68 | let workInProgress = null; // Current fiber being worked on 69 | let workInProgressRoot = null; // Root fiber currently being built 70 | let rootDoesHavePassiveEffect = false; // Whether root has passive effects (useEffect) 71 | let rootWithPendingPassiveEffects = null; // Root with pending passive effects 72 | let workInProgressRootRenderLanes = NoLanes; // Lanes being rendered 73 | 74 | // Work loop exit status constants 75 | const RootInProgress = 0; // Fiber tree construction in progress 76 | const RootCompleted = 5; // Fiber tree construction completed 77 | 78 | // Current render state 79 | let workInProgressRootExitStatus = RootInProgress; // Current fiber tree status 80 | let currentEventTime = NoTimestamp; // Current event timestamp 81 | 82 | /** 83 | * Schedule Update on Fiber 84 | * 85 | * Schedules an update on a fiber root. This is the entry point for all updates 86 | * in React, whether they come from setState, props changes, or other sources. 87 | * 88 | * @param {FiberRoot} root - The fiber root to update 89 | * @param {Fiber} fiber - The fiber that triggered the update 90 | * @param {Lane} lane - Priority lane for this update 91 | * @param {number} eventTime - Time when the event occurred 92 | */ 93 | export function scheduleUpdateOnFiber(root, fiber, lane, eventTime) { 94 | // Mark the root as having updates in this lane 95 | markRootUpdated(root, lane); 96 | 97 | // Ensure the root is scheduled for updates 98 | ensureRootIsScheduled(root, eventTime); 99 | } 100 | 101 | /** 102 | * Ensure Root is Scheduled 103 | * 104 | * Ensures that a root is scheduled for updates. This function implements 105 | * React's scheduling logic, including priority management and batching. 106 | * 107 | * @param {FiberRoot} root - The fiber root to schedule 108 | * @param {number} currentTime - Current time for scheduling calculations 109 | */ 110 | function ensureRootIsScheduled(root, currentTime) { 111 | // Get existing callback node if any 112 | const existingCallbackNode = root.callbackNode; 113 | 114 | // Mark starved lanes as expired to prevent starvation 115 | markStarvedLanesAsExpired(root, currentTime); 116 | 117 | // Get the next lanes to work on (highest priority) 118 | const nextLanes = getNextLanes(root, workInProgressRootRenderLanes); 119 | 120 | // If no work to do, exit early 121 | if (nextLanes === NoLanes) { 122 | return; 123 | } 124 | 125 | // Get the priority of the new work 126 | let newCallbackPriority = getHighestPriorityLane(nextLanes); 127 | 128 | // Get the priority of existing work 129 | const existingCallbackPriority = root.callbackPriority; 130 | 131 | // If priorities match, we can batch the updates 132 | if (existingCallbackPriority === newCallbackPriority) { 133 | return; 134 | } 135 | 136 | // Cancel existing callback if priorities differ 137 | if (existingCallbackNode !== null) { 138 | console.log("cancelCallback"); 139 | Scheduler_cancelCallback(existingCallbackNode); 140 | } 141 | 142 | // Schedule new callback 143 | let newCallbackNode = null; 144 | //如果新的优先级是同步的话 145 | if (newCallbackPriority === SyncLane) { 146 | //先把performSyncWorkOnRoot添回到同步队列中 147 | scheduleSyncCallback(performSyncWorkOnRoot.bind(null, root)); 148 | //再把flushSyncCallbacks放入微任务 149 | queueMicrotask(flushSyncCallbacks); 150 | //如果是同步执行的话 151 | newCallbackNode = null; 152 | } else { 153 | //如果不是同步,就需要调度一个新的任务 154 | let schedulerPriorityLevel; 155 | switch (lanesToEventPriority(nextLanes)) { 156 | case DiscreteEventPriority: 157 | schedulerPriorityLevel = ImmediateSchedulerPriority; 158 | break; 159 | case ContinuousEventPriority: 160 | schedulerPriorityLevel = UserBlockingSchedulerPriority; 161 | break; 162 | case DefaultEventPriority: 163 | schedulerPriorityLevel = NormalSchedulerPriority; 164 | break; 165 | case IdleEventPriority: 166 | schedulerPriorityLevel = IdleSchedulerPriority; 167 | break; 168 | default: 169 | schedulerPriorityLevel = NormalSchedulerPriority; 170 | break; 171 | } 172 | newCallbackNode = Scheduler_scheduleCallback( 173 | schedulerPriorityLevel, 174 | performConcurrentWorkOnRoot.bind(null, root) 175 | ); 176 | } 177 | //the task executed on the root is newCallbackNode 178 | root.callbackNode = newCallbackNode; 179 | root.callbackPriority = newCallbackPriority; 180 | /* if (workInProgressRoot) return; 181 | workInProgressRoot = root; 182 | //告诉 浏览器要执行performConcurrentWorkOnRoot 在此触发更新 183 | scheduleCallback(NormalSchedulerPriority, performConcurrentWorkOnRoot.bind(null, root)); */ 184 | } 185 | /** 186 | * perform synchronous work on the root 187 | */ 188 | function performSyncWorkOnRoot(root) { 189 | //get the highest priority lane 190 | const lanes = getNextLanes(root); 191 | //render the new fiber tree 192 | renderRootSync(root, lanes); 193 | //get the new rendered fiber root node 194 | const finishedWork = root.current.alternate; 195 | root.finishedWork = finishedWork; 196 | commitRoot(root); 197 | return null; 198 | } 199 | /** 200 | * build the fiber tree according to the fiber, to create the real DOM node, you also need to insert the real DOM node into the container 201 | * @param {*} root 202 | */ 203 | function performConcurrentWorkOnRoot(root, didTimeout) { 204 | //get the task on the current root node 205 | const originalCallbackNode = root.callbackNode; 206 | //get the highest priority lane 207 | const lanes = getNextLanes(root, NoLanes); //16 208 | if (lanes === NoLanes) { 209 | return null; 210 | } 211 | //if it does not contain the blocking lane, and it is not timed out, it can be rendered in parallel, that is, the time slice is enabled 212 | //所以说默认更新车道是同步的,不能启用时间分片 213 | //whether it does not contain the blocking lane 214 | const nonIncludesBlockingLane = !includesBlockingLane(root, lanes); 215 | //whether it does not contain the expired lane 216 | const nonIncludesExpiredLane = !includesExpiredLane(root, lanes); 217 | //the time slice is not expired 218 | const nonTimeout = !didTimeout; 219 | //three variables are true, the time slice can be enabled, that is, the concurrent rendering can be performed, that is, the execution can be interrupted 220 | const shouldTimeSlice = 221 | nonIncludesBlockingLane && nonIncludesExpiredLane && nonTimeout; 222 | // console.log('shouldTimeSlice', shouldTimeSlice); 223 | //execute rendering, get the exit status 224 | const exitStatus = shouldTimeSlice 225 | ? renderRootConcurrent(root, lanes) 226 | : renderRootSync(root, lanes); 227 | //if it is not rendering, then it means that the rendering must be finished 228 | if (exitStatus !== RootInProgress) { 229 | const finishedWork = root.current.alternate; 230 | root.finishedWork = finishedWork; 231 | commitRoot(root); 232 | } 233 | //it means that the task is not completed 234 | if (root.callbackNode === originalCallbackNode) { 235 | //return this function, next time to continue 236 | return performConcurrentWorkOnRoot.bind(null, root); 237 | } 238 | return null; 239 | } 240 | function renderRootConcurrent(root, lanes) { 241 | //because this method will be repeatedly entered during the construction of the fiber tree, it will be entered multiple times 242 | //only when the first time comes in will the new fiber tree be created, or the new fiber 243 | if (workInProgressRoot !== root || workInProgressRootRenderLanes !== lanes) { 244 | prepareFreshStack(root, lanes); 245 | } 246 | //execute the fiber tree construction or rendering within the current time slice(5ms) 247 | workLoopConcurrent(); 248 | //if workInProgress is not null, it means that the fiber tree construction is not completed 249 | if (workInProgress !== null) { 250 | return RootInProgress; 251 | } 252 | //if workInProgress is null, it means that the rendering work is completely finished 253 | return workInProgressRootExitStatus; 254 | } 255 | function flushPassiveEffect() { 256 | if (rootWithPendingPassiveEffects !== null) { 257 | const root = rootWithPendingPassiveEffects; 258 | //execute the unmount effect, destroy 259 | commitPassiveUnmountEffects(root.current); 260 | //execute the mount effect, create 261 | commitPassiveMountEffects(root, root.current); 262 | } 263 | } 264 | function commitRoot(root) { 265 | const previousUpdatePriority = getCurrentUpdatePriority(); 266 | try { 267 | //set the current update priority to 1 268 | setCurrentUpdatePriority(DiscreteEventPriority); 269 | commitRootImpl(root); 270 | } finally { 271 | setCurrentUpdatePriority(previousUpdatePriority); 272 | } 273 | } 274 | function commitRootImpl(root) { 275 | //get the new built fiber tree root fiber tag=3 276 | const { finishedWork } = root; 277 | workInProgressRoot = null; 278 | workInProgressRootRenderLanes = NoLanes; 279 | root.callbackNode = null; 280 | root.callbackPriority = NoLane; 281 | //merge the remaining lanes on the current new root 282 | const remainingLanes = mergeLanes( 283 | finishedWork.lanes, 284 | finishedWork.childLanes 285 | ); 286 | markRootFinished(root, remainingLanes); 287 | if ( 288 | (finishedWork.subtreeFlags & Passive) !== NoFlags || 289 | (finishedWork.flags & Passive) !== NoFlags 290 | ) { 291 | if (!rootDoesHavePassiveEffect) { 292 | rootDoesHavePassiveEffect = true; 293 | Scheduler_scheduleCallback(NormalSchedulerPriority, flushPassiveEffect); 294 | } 295 | } 296 | //whether the subtree has effects 297 | const subtreeHasEffects = 298 | (finishedWork.subtreeFlags & MutationMask) !== NoFlags; 299 | const rootHasEffect = (finishedWork.flags & MutationMask) !== NoFlags; 300 | //if the effect or the subtree has effects, the DOM operation should be committed 301 | if (subtreeHasEffects || rootHasEffect) { 302 | //after the DOM is executed, the DOM operation should be committed 303 | commitMutationEffectsOnFiber(finishedWork, root); 304 | //execute the layout effect 305 | commitLayoutEffects(finishedWork, root); 306 | if (rootDoesHavePassiveEffect) { 307 | rootDoesHavePassiveEffect = false; 308 | rootWithPendingPassiveEffects = root; 309 | } 310 | } 311 | //after the DOM is changed, the root's current can be pointed to the new fiber tree 312 | root.current = finishedWork; 313 | //after the submission, because the root may have skipped updates, so the scheduling needs to be re-scheduled 314 | ensureRootIsScheduled(root, now()); 315 | } 316 | function prepareFreshStack(root, renderLanes) { 317 | workInProgress = createWorkInProgress(root.current, null); 318 | workInProgressRootRenderLanes = renderLanes; 319 | workInProgressRoot = root; 320 | finishQueueingConcurrentUpdates(); 321 | } 322 | function renderRootSync(root, renderLanes) { 323 | //if the new root is different from the old root, or the new render priority is different from the old render priority 324 | if ( 325 | root !== workInProgressRoot || 326 | workInProgressRootRenderLanes !== renderLanes 327 | ) { 328 | // create a substitute 329 | prepareFreshStack(root, renderLanes); 330 | } 331 | workLoopSync(); 332 | return RootCompleted; 333 | } 334 | function workLoopConcurrent() { 335 | //if there is a next fiber to build and the time slice is not expired 336 | while (workInProgress !== null && !shouldYield()) { 337 | //console.log('shouldYield()', shouldYield(), workInProgress); 338 | sleep(5); 339 | performUnitOfWork(workInProgress); 340 | } 341 | } 342 | function workLoopSync() { 343 | while (workInProgress !== null) { 344 | performUnitOfWork(workInProgress); 345 | } 346 | } 347 | /** 348 | * execute a work unit 349 | * @param {*} unitOfWork 350 | */ 351 | function performUnitOfWork(unitOfWork) { 352 | //get the old fiber corresponding to the new fiber 353 | const current = unitOfWork.alternate; 354 | //after the current fiber's child fiber chain list is built, complete the work 355 | const next = beginWork(current, unitOfWork, workInProgressRootRenderLanes); 356 | unitOfWork.memoizedProps = unitOfWork.pendingProps; 357 | if (next === null) { 358 | //if there is no child node, it means that the current fiber has been completed 359 | completeUnitOfWork(unitOfWork); 360 | } else { 361 | //if there is a child node, let the child node become the next work unit 362 | workInProgress = next; 363 | } 364 | } 365 | 366 | function completeUnitOfWork(unitOfWork) { 367 | let completedWork = unitOfWork; 368 | do { 369 | const current = completedWork.alternate; 370 | const returnFiber = completedWork.return; 371 | //execute the completion work of this fiber, if it is a native component, it is to create the real DOM node 372 | completeWork(current, completedWork); 373 | //if there is a sibling, build the fiber child chain list corresponding to the sibling 374 | const siblingFiber = completedWork.sibling; 375 | if (siblingFiber !== null) { 376 | workInProgress = siblingFiber; 377 | return; 378 | } 379 | //if there is no sibling, it means that the current completed is the last node of the parent fiber 380 | //也就是说一个父fiber,所有的子fiber全部完成了 381 | completedWork = returnFiber; 382 | workInProgress = completedWork; 383 | } while (completedWork !== null); 384 | //if you get here, it means that the entire fiber tree has been built, and the build status is set to empty 385 | if (workInProgressRootExitStatus === RootInProgress) { 386 | workInProgressRootExitStatus = RootCompleted; 387 | } 388 | } 389 | 390 | export function requestUpdateLane() { 391 | const updateLane = getCurrentUpdatePriority(); 392 | if (updateLane !== NoLanes) { 393 | return updateLane; 394 | } 395 | const eventLane = getCurrentEventPriority(); 396 | return eventLane; 397 | } 398 | function sleep(duration) { 399 | const timeStamp = new Date().getTime(); 400 | const endTime = timeStamp + duration; 401 | while (true) { 402 | if (new Date().getTime() > endTime) { 403 | return; 404 | } 405 | } 406 | } 407 | //request the current time 408 | export function requestEventTime() { 409 | currentEventTime = now(); 410 | return currentEventTime; //performance.now() 411 | } 412 | -------------------------------------------------------------------------------- /src/react-reconciler/src/ReactFiberHooks.js: -------------------------------------------------------------------------------- 1 | /** 2 | * React Fiber Hooks - Internal Hooks Implementation 3 | * 4 | * This module contains the internal implementation of React hooks. It manages 5 | * hook state, effects, and the hook dispatcher system that switches between 6 | * mount and update phases. 7 | * 8 | * Key concepts: 9 | * - Hook objects store state and form a linked list per component 10 | * - Different dispatchers for mount vs update phases 11 | * - Effect hooks manage side effects with dependency tracking 12 | * - State hooks manage component state with update queues 13 | * 14 | * @module ReactFiberHooks 15 | */ 16 | 17 | import ReactSharedInternals from "shared/ReactSharedInternals"; 18 | import { 19 | scheduleUpdateOnFiber, 20 | requestUpdateLane, 21 | requestEventTime, 22 | } from "./ReactFiberWorkLoop"; 23 | import { enqueueConcurrentHookUpdate } from "./ReactFiberConcurrentUpdates"; 24 | import { 25 | Passive as PassiveEffect, 26 | Update as UpdateEffect, 27 | } from "./ReactFiberFlags"; 28 | import { 29 | HasEffect as HookHasEffect, 30 | Passive as HookPassive, 31 | Layout as HookLayout, 32 | } from "./ReactHookEffectTags"; 33 | import { NoLane, NoLanes, isSubsetOfLanes, mergeLanes } from "./ReactFiberLane"; 34 | 35 | // Global hook state 36 | const { ReactCurrentDispatcher } = ReactSharedInternals; 37 | let currentlyRenderingFiber = null; // Fiber currently being rendered 38 | let workInProgressHook = null; // Current hook being processed 39 | let currentHook = null; // Hook from previous render 40 | let renderLanes = NoLanes; // Current render priority lanes 41 | 42 | const HooksDispatcherOnMount = { 43 | useReducer: mountReducer, 44 | useState: mountState, 45 | useEffect: mountEffect, 46 | useLayoutEffect: mountLayoutEffect, 47 | useRef: mountRef, 48 | }; 49 | const HooksDispatcherOnUpdate = { 50 | useReducer: updateReducer, 51 | useState: updateState, 52 | useEffect: updateEffect, 53 | useLayoutEffect: updateLayoutEffect, 54 | useRef: updateRef, 55 | }; 56 | function mountRef(initialValue) { 57 | const hook = mountWorkInProgressHook(); 58 | const ref = { 59 | current: initialValue, 60 | }; 61 | hook.memoizedState = ref; 62 | return ref; 63 | } 64 | function updateRef() { 65 | const hook = updateWorkInProgressHook(); 66 | return hook.memoizedState; 67 | } 68 | function mountLayoutEffect(create, deps) { 69 | return mountEffectImpl(UpdateEffect, HookLayout, create, deps); 70 | } 71 | function updateLayoutEffect(create, deps) { 72 | return updateEffectImpl(UpdateEffect, HookLayout, create, deps); 73 | } 74 | function updateEffect(create, deps) { 75 | return updateEffectImpl(PassiveEffect, HookPassive, create, deps); 76 | } 77 | function updateEffectImpl(fiberFlags, hookFlags, create, deps) { 78 | const hook = updateWorkInProgressHook(); 79 | const nextDeps = deps === undefined ? null : deps; 80 | let destroy; 81 | // the previous old hook 82 | if (currentHook !== null) { 83 | // get the old effect object on this useEffect hook create deps destroy 84 | const prevEffect = currentHook.memoizedState; 85 | destroy = prevEffect.destroy; 86 | if (nextDeps !== null) { 87 | const prevDeps = prevEffect.deps; 88 | // compare the new array with the old array, if they are the same 89 | if (areHookInputsEqual(nextDeps, prevDeps)) { 90 | // regardless of whether it needs to be re-executed, the new effect needs to be added to the fiber.updateQueue 91 | hook.memoizedState = pushEffect(hookFlags, create, destroy, nextDeps); 92 | return; 93 | } 94 | } 95 | } 96 | // if it needs to be executed, the fiber's flags need to be modified 97 | currentlyRenderingFiber.flags |= fiberFlags; 98 | // if it needs to be executed, add the HookHasEffect flag 99 | // recently a student asked Passive还需HookHasEffect, because not every Passive will be executed 100 | hook.memoizedState = pushEffect( 101 | HookHasEffect | hookFlags, 102 | create, 103 | destroy, 104 | nextDeps 105 | ); 106 | } 107 | function areHookInputsEqual(nextDeps, prevDeps) { 108 | if (prevDeps === null) return null; 109 | for (let i = 0; i < prevDeps.length && i < nextDeps.length; i++) { 110 | if (Object.is(nextDeps[i], prevDeps[i])) { 111 | continue; 112 | } 113 | return false; 114 | } 115 | return true; 116 | } 117 | function mountEffect(create, deps) { 118 | return mountEffectImpl(PassiveEffect, HookPassive, create, deps); 119 | } 120 | /* function mountLayoutEffect(create, deps) { 121 | return mountEffectImpl(PassiveEffect, HookPassive, create, deps); 122 | } */ 123 | function mountEffectImpl(fiberFlags, hookFlags, create, deps) { 124 | const hook = mountWorkInProgressHook(); 125 | const nextDeps = deps === undefined ? null : deps; 126 | //给当前的函数组件fiber添加flags 127 | currentlyRenderingFiber.flags |= fiberFlags; 128 | hook.memoizedState = pushEffect( 129 | HookHasEffect | hookFlags, 130 | create, 131 | undefined, 132 | nextDeps 133 | ); 134 | } 135 | /** 136 | * add effect list 137 | * @param {*} tag effect tag 138 | * @param {*} create create method 139 | * @param {*} destroy destroy method 140 | * @param {*} deps dependencies array 141 | */ 142 | function pushEffect(tag, create, destroy, deps) { 143 | const effect = { 144 | tag, 145 | create, 146 | destroy, 147 | deps, 148 | next: null, 149 | }; 150 | let componentUpdateQueue = currentlyRenderingFiber.updateQueue; 151 | if (componentUpdateQueue === null) { 152 | componentUpdateQueue = createFunctionComponentUpdateQueue(); 153 | currentlyRenderingFiber.updateQueue = componentUpdateQueue; 154 | componentUpdateQueue.lastEffect = effect.next = effect; 155 | } else { 156 | const lastEffect = componentUpdateQueue.lastEffect; 157 | if (lastEffect === null) { 158 | componentUpdateQueue.lastEffect = effect.next = effect; 159 | } else { 160 | const firstEffect = lastEffect.next; 161 | lastEffect.next = effect; 162 | effect.next = firstEffect; 163 | componentUpdateQueue.lastEffect = effect; 164 | } 165 | } 166 | return effect; 167 | } 168 | function createFunctionComponentUpdateQueue() { 169 | return { 170 | lastEffect: null, 171 | }; 172 | } 173 | // useState is actually a useReducer with a reducer built in 174 | function baseStateReducer(state, action) { 175 | return typeof action === "function" ? action(state) : action; 176 | } 177 | function updateState(initialState) { 178 | return updateReducer(baseStateReducer, initialState); 179 | } 180 | /** 181 | * hook attributes 182 | * hook.memoizedState the current hook's真正显示出来的状态 183 | * hook.baseState the old state before the first skipped update 184 | * hook.queue.lastRenderedState the previous calculated state 185 | */ 186 | function mountState(initialState) { 187 | const hook = mountWorkInProgressHook(); 188 | hook.memoizedState = hook.baseState = initialState; 189 | const queue = { 190 | pending: null, 191 | dispatch: null, 192 | lastRenderedReducer: baseStateReducer, // the previous reducer 193 | lastRenderedState: initialState, // the previous state 194 | }; 195 | hook.queue = queue; 196 | const dispatch = (queue.dispatch = dispatchSetState.bind( 197 | null, 198 | currentlyRenderingFiber, 199 | queue 200 | )); 201 | return [hook.memoizedState, dispatch]; 202 | } 203 | function dispatchSetState(fiber, queue, action) { 204 | // get the current update lane lane 1 205 | const lane = requestUpdateLane(); 206 | const update = { 207 | lane, // the current update priority is 1 208 | action, 209 | hasEagerState: false, // whether there is an eager update 210 | eagerState: null, // eager update state 211 | next: null, 212 | }; 213 | const alternate = fiber.alternate; 214 | 215 | // when you dispatch an action, I immediately use the previous state and the previous reducer to calculate the new state 216 | // as long as the first update can perform this optimization 217 | if ( 218 | fiber.lanes === NoLanes && 219 | (alternate === null || alternate.lanes == NoLanes) 220 | ) { 221 | // get the old state and reducer on the queue 222 | const { lastRenderedReducer, lastRenderedState } = queue; 223 | // use the previous state and reducer to calculate the new state 224 | const eagerState = lastRenderedReducer(lastRenderedState, action); 225 | update.hasEagerState = true; 226 | update.eagerState = eagerState; 227 | if (Object.is(eagerState, lastRenderedState)) { 228 | return; 229 | } 230 | } 231 | // below is the actual enqueue update, and schedule update logic 232 | const root = enqueueConcurrentHookUpdate(fiber, queue, update, lane); 233 | const eventTime = requestEventTime(); 234 | scheduleUpdateOnFiber(root, fiber, lane, eventTime); 235 | } 236 | /** 237 | * build new hooks 238 | */ 239 | function updateWorkInProgressHook() { 240 | // get the old hook of the new hook to be built 241 | if (currentHook === null) { 242 | const current = currentlyRenderingFiber.alternate; 243 | currentHook = current.memoizedState; 244 | } else { 245 | currentHook = currentHook.next; 246 | } 247 | // create new hook based on the old hook 248 | const newHook = { 249 | memoizedState: currentHook.memoizedState, 250 | queue: currentHook.queue, 251 | next: null, 252 | baseState: currentHook.baseState, 253 | baseQueue: currentHook.baseQueue, 254 | }; 255 | if (workInProgressHook === null) { 256 | currentlyRenderingFiber.memoizedState = workInProgressHook = newHook; 257 | } else { 258 | workInProgressHook = workInProgressHook.next = newHook; 259 | } 260 | return workInProgressHook; 261 | } 262 | function updateReducer(reducer) { 263 | const hook = updateWorkInProgressHook(); 264 | const queue = hook.queue; 265 | queue.lastRenderedReducer = reducer; 266 | const current = currentHook; 267 | let baseQueue = current.baseQueue; 268 | const pendingQueue = queue.pending; 269 | // merge the new and old update lists 270 | if (pendingQueue !== null) { 271 | if (baseQueue !== null) { 272 | const baseFirst = baseQueue.next; 273 | const pendingFirst = pendingQueue.next; 274 | baseQueue.next = pendingFirst; 275 | pendingQueue.next = baseFirst; 276 | } 277 | current.baseQueue = baseQueue = pendingQueue; 278 | queue.pending = null; 279 | } 280 | if (baseQueue !== null) { 281 | printQueue(baseQueue); 282 | const first = baseQueue.next; 283 | let newState = current.baseState; 284 | let newBaseState = null; 285 | let newBaseQueueFirst = null; 286 | let newBaseQueueLast = null; 287 | let update = first; 288 | do { 289 | const updateLane = update.lane; 290 | const shouldSkipUpdate = !isSubsetOfLanes(renderLanes, updateLane); 291 | if (shouldSkipUpdate) { 292 | const clone = { 293 | lane: updateLane, 294 | action: update.action, 295 | hasEagerState: update.hasEagerState, 296 | eagerState: update.eagerState, 297 | next: null, 298 | }; 299 | if (newBaseQueueLast === null) { 300 | newBaseQueueFirst = newBaseQueueLast = clone; 301 | newBaseState = newState; 302 | } else { 303 | newBaseQueueLast = newBaseQueueLast.next = clone; 304 | } 305 | currentlyRenderingFiber.lanes = mergeLanes( 306 | currentlyRenderingFiber.lanes, 307 | updateLane 308 | ); 309 | } else { 310 | if (newBaseQueueLast !== null) { 311 | const clone = { 312 | lane: NoLane, 313 | action: update.action, 314 | hasEagerState: update.hasEagerState, 315 | eagerState: update.eagerState, 316 | next: null, 317 | }; 318 | newBaseQueueLast = newBaseQueueLast.next = clone; 319 | } 320 | if (update.hasEagerState) { 321 | newState = update.eagerState; 322 | } else { 323 | const action = update.action; 324 | newState = reducer(newState, action); 325 | } 326 | } 327 | update = update.next; 328 | } while (update !== null && update !== first); 329 | if (newBaseQueueLast === null) { 330 | newBaseState = newState; 331 | } else { 332 | newBaseQueueLast.next = newBaseQueueFirst; 333 | } 334 | hook.memoizedState = newState; 335 | hook.baseState = newBaseState; 336 | hook.baseQueue = newBaseQueueLast; 337 | queue.lastRenderedState = newState; 338 | } 339 | if (baseQueue === null) { 340 | queue.lanes = NoLanes; 341 | } 342 | const dispatch = queue.dispatch; 343 | return [hook.memoizedState, dispatch]; 344 | } 345 | function printQueue(queue) { 346 | const first = queue.next; 347 | let desc = ""; 348 | let update = first; 349 | do { 350 | desc += "=>" + update.action.id; 351 | update = update.next; 352 | } while (update !== null && update !== first); 353 | desc += "=>null"; 354 | console.log(desc); 355 | } 356 | function mountReducer(reducer, initialArg) { 357 | const hook = mountWorkInProgressHook(); 358 | hook.memoizedState = hook.baseState = initialArg; 359 | const queue = { 360 | pending: null, 361 | dispatch: null, 362 | lastRenderedReducer: reducer, 363 | lastRenderedState: initialArg, 364 | }; 365 | hook.queue = queue; 366 | const dispatch = (queue.dispatch = dispatchReducerAction.bind( 367 | null, 368 | currentlyRenderingFiber, 369 | queue 370 | )); 371 | return [hook.memoizedState, dispatch]; 372 | } 373 | /** 374 | * execute the method of dispatching an action, it needs to update the state, and make the interface re-update 375 | * @param {*} fiber the fiber corresponding to the function 376 | * @param {*} queue the update queue corresponding to the hook 377 | * @param {*} action the action to be dispatched 378 | */ 379 | function dispatchReducerAction(fiber, queue, action) { 380 | // in each hook, there will be an update queue, the update queue is a circular list of update objects update1.next=update2.next=update1 381 | const lane = requestUpdateLane(); 382 | const update = { 383 | lane, // the current update priority 384 | action, //{ type: 'add', payload: 1 } the action to be dispatched 385 | hasEagerState: false, // whether there is an eager update 386 | eagerState: null, // eager update state 387 | next: null, // point to the next update object 388 | }; 389 | // add the current latest update to the update queue, and return the current root fiber 390 | const root = enqueueConcurrentHookUpdate(fiber, queue, update, lane); 391 | const eventTime = requestEventTime(); 392 | scheduleUpdateOnFiber(root, fiber, lane, eventTime); 393 | } 394 | /** 395 | * mount the hook being built 396 | * */ 397 | function mountWorkInProgressHook() { 398 | const hook = { 399 | memoizedState: null, //hook's state 0 400 | queue: null, // the update queue corresponding to the hook queue.pending=update's circular list 401 | next: null, // point to the next hook, a function can have multiple hooks, they will form a singly linked list 402 | baseState: null, // the state before the first skipped update 403 | baseQueue: null, // the list of skipped updates 404 | }; 405 | if (workInProgressHook === null) { 406 | // the state of the fiber corresponding to the current function is equal to the first hook object 407 | currentlyRenderingFiber.memoizedState = workInProgressHook = hook; 408 | } else { 409 | workInProgressHook = workInProgressHook.next = hook; 410 | } 411 | return workInProgressHook; 412 | } 413 | /** 414 | * render the function component 415 | * @param {*} current old fiber 416 | * @param {*} workInProgress new fiber 417 | * @param {*} Component component definition 418 | * @param {*} props component properties 419 | * @returns virtual DOM or React element 420 | */ 421 | export function renderWithHooks( 422 | current, 423 | workInProgress, 424 | Component, 425 | props, 426 | nextRenderLanes 427 | ) { 428 | // the current render lane 429 | renderLanes = nextRenderLanes; 430 | currentlyRenderingFiber = workInProgress; 431 | // the update queue of the function component stores the effect 432 | workInProgress.updateQueue = null; 433 | // the linked list of hooks stored in the function component state 434 | workInProgress.memoizedState = null; 435 | // if there is an old fiber and an old hook linked list 436 | if (current !== null && current.memoizedState !== null) { 437 | ReactCurrentDispatcher.current = HooksDispatcherOnUpdate; 438 | } else { 439 | ReactCurrentDispatcher.current = HooksDispatcherOnMount; 440 | } 441 | // need to assign the value of ReactCurrentDispatcher.current before executing the function component 442 | const children = Component(props); 443 | currentlyRenderingFiber = null; 444 | workInProgressHook = null; 445 | currentHook = null; 446 | renderLanes = NoLanes; 447 | return children; 448 | } 449 | --------------------------------------------------------------------------------