学习:用ES5实现React18.2源码

241 阅读12分钟

1.基础知识

1.1 React 是什么?

  • React是一个用于构建用户界面的 JavaScript 库。
  • 可以通过组件化的方式,构建快速响应的大型Web应用程序。

2.创建项目

2.1 创建目录

mkdir react182
cd react182
npm init -y

2.2 安装

npm install vite @vitejs/plugin-react --save

2.3 vite.config.js

import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";
import path from "path";

export default defineConfig({
  define: {
    __DEV__: true,
    __PROFILE__: true,
    __UMD__: true,
    __EXPERIMENTAL__: true,
  },
  resolve: {
    alias: {
      react: path.posix.resolve("src/react"),
      "react-dom": path.posix.resolve("src/react-dom"),
      "react-dom-bindings": path.posix.resolve("src/react-dom-bindings"),
      "react-reconciler": path.posix.resolve("src/react-reconciler"),
      scheduler: path.posix.resolve("src/scheduler"),
      shared: path.posix.resolve("src/shared"),
    },
  },
  plugins: [react()],
  optimizeDeps: {
    force: true,
  },
});

2.4 jsonconfig.json

{
  "compilerOptions": {
    "baseUrl": "./",
    "paths": {
      "react/*": ["src/react/*"],
      "react-dom/*": ["src/react-dom/*"],
      "react-dom-bindings/*": ["src/react-dom-bindings/*"],
      "react-reconciler/*": ["src/react-reconciler/*"],
      "scheduler/*": ["scheduler/*"],
      "shared/*": ["src/shared/*"]
    }
  },
  "exclude": ["node_modules", "dist"]
}

2.5 main.jsx

console.log('main----')

2.6 index.html

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>React18</title>
  </head>

  <body>
    <div id="root"></div>
    <script type="module" src="/src/main.jsx"></script>
  </body>
</html>

2.7 package.json

{
  "scripts": {
    "dev": "vite --force"
  }
}

3.实现虚拟DOM

image.png

3.1 main.jsx

let element=(`
    <div>
        hello<span style={{color: 'red'}}>world</span>
    </div>
`)
comsole.log('element=>', element)

3.2 jsx-dev-runtime.js

export { jsxDEV } from './src/jsx/ReactJSXElement';

3.3 ReactJSXElement.js

import hasOwnProperty from 'shared/hasOwnProperty';
import { REACT_ELEMENT_TYPE } from 'shared/ReactSymbols';

const RESVERED_PROPS = {
   ref: true,    
   key: true,
   __self: true,
   __source: true,
}

function hasVaildRef(config) {
   return config.ref !== undefined
}

function ReactElement(type, key, ref, props){
    const element = {
        $$typeof: REACT_ELEMENT_TYPE,
        type,
        kef,
        ref,
        props
    }
    return element;
}

export function jsxDEV(type, configm , maybeKey){
   let propName,
       ref = null,
       key = null;
   const props = {};
    
   if(hasVaildRef(config)) {
       ref = config.ref
   }
   if(typeof maybeKey !== 'undefined') {
       key = maybeKey
   }
   for(propName in config) {
       if(
           hasOwnProperty.call(config, propNmae) &&
           !RESERVED_PROPS.hasOwnProperty(propName)
       ) {
           props[propName] = config[propName]
       }
   }
   return ReactElement(type, key, ref, props);
}

3.4 ReactSymbols

export const REACT_ELEMENT_TYPE = Symbol.for('react-element');

3.5 hasOwnProperty

const { hasOwnProperty } = Object.prototype;
export default hasOwnProperty;

4.创建ReactDOMRoot

image.png

4.1 main.jsx

+ import { createRoot } from 'react-dom/client';

let element = (`
    <div>
        hello<span style={{color: 'red'}}>world</span>
    </div>
`)
conole.log('element=>', element);
+ const root = createRoot(document.getElementById('root'));
+ console.log('root=>', root);

4.2 client.js

export { createRoot } from './src/client/ReactDOMRoot';

4.3 ReactDOMRoot.js

import { createContainer } from 'react-reconciler/ReactFiberReconciler';

function ReactDOMRoot(internalRoot) {
    this._internalRoot = internalRoot
}
export function createRoot(container) {
    const root = createContainer(container);
    return new ReactDOMRoot(root);
}

4.4 ReactFiberReconciler.js

import { createFiberRoot } from './ReactFiberRoot';

export function createRoot(containerInfo) {
    return createFiberRoot(containerInfo)
}

4.5 ReactFiberRoot.js

function FiberRootNode(containerInfo) {
    this.contaienrInfo = containerInfo;
}
export function createFiberRoot(containerInfo){
    const root = new FiberRootNode(containerInfo);
    return root;
}

5.创建RootFiber

image.png

5.1 ReactFiberRoot.js

+ import { createHostRootFiber } from './ReactFiber';

function FiberRootNode(containerInfo) {
    this.contaienrInfo = containerInfo;
}
export function createFiberRoot(containerInfo){
    const root = new FiberRootNode(containerInfo);
+   const uninitializedFiber = createHostRootFiber();
+   root.current = uninitializedFiber;
+   uninitializedFiber.stateNode = root;
    return root;
}

5.2 ReactFiber.js

import { HostRoot } from './ReactWorkTags';
import { NoFlags } from './ReactFiberFlags';

export function FiberNode(tag, pendingProps, key){
    this.tag = tag;
    this.key = key;
    this.type = null;
    
    this.pendingProps = pendingProps;
    this.memoizeProps = null;
    this.memoizeState = null;
    
    this.child = null;
    this.return = null;
    this.sibling = null;
    
    this.stateNode = null;
    this.alternate = null;
    this.updateQueue = null;
    
    this.flags = NoFlags;
    this.substringFlags = NoFlags;
}
export function createFiber(tag, pendingProps, key) {
    return new FiberNode(tag, pendingProps, key)
}
export function createHostRootFiber() {
    createFiber(HostRoot, null, null)
}

5.3 ReactWorkTags.js

export const HostRoot = 3;
export const HostComponet = 5;
export const HostText = 6;

5.4 ReactFiberFlags.js

export NoFlags = 0b0000000000000000000000000;

6.initializeUpdateQueue

image.png

6.1 ReactFiberRoot.js

import { createHostRootFiber } from './ReactFiber';
+ import { initializeUpdateQueue } from './ReactFiberClassUpdate';

function FiberRootNode(containerInfo) {
    this.contaienrInfo = containerInfo;
}
export function createFiberRoot(containerInfo){
    const root = new FiberRootNode(containerInfo);
    const uninitializedFiber = createHostRootFiber();
    root.current = uninitializedFiber;
    uninitializedFiber.stateNode = root;
+  initializeUpdateQueue(uninitializedFiber);
    return root;
}

6.2 ReactFiberClassUpdate.js

export function initializeUpdateQueue(fiber) {
    const queue = {
        shared: {
            pending: null
        }
    }
    fiber.updateQueue = queue;
}

7.enqueueUpdate

image.png

7.1 main.jsx

import { createRoot } from 'react-dom/client';

let element = (`
    <div>
        hello<span style={{color: 'red'}}>world</span>
    </div>
`)
conole.log('element=>', element);
const root = createRoot(document.getElementById('root'));
console.log('root=>', root);
+ root.render(element);

7.2 ReactDOMRoot.js

import { 
    createContainer,
    updateContainer
} from 'react-reconciler/ReactFiberReconciler';

function ReactDOMRoot(internalRoot) {
    this._internalRoot = internalRoot
}

+ ReactDOMRoot.prototype.render = function render(children){
+     const root = this._internalRoot;
+     updateContainer(children, root);
+ }

export function createRoot(container) {
    const root = createContainer(container);
    return new ReactDOMRoot(root);
}

7.3 ReactFiberReconciler.js

import { createFiberRoot } from './ReactFiberRoot';
+ import { createUpdate, enqueueUpdate } from './ReactFiberClassUpdate';

export function updateContainer(element, container) {
    const current = container.current;
    const update = createUpdate(current);
    update.payload = { element };
    const root = enqueueUpdate(current, update);
    conole.log('updateContainer--root=>', root);
}
export function createContainer(containerInfo) {
    createFiberRoot(containerInfo)
}

7.4 ReactFiberClassUpdate.js

+ const UpdateState = 0;

export function initializeUpdateQueue(fiber) {
    const queue = {
        shared: {
            pending: null
        }
    }
    fiber.updateQueue = queue;
}

+ export function createUpdate() {
+    const update = { tag: UpdateState };
+    return update;
+ }

+ export function enqueueUpdate(fiber, update) {
+    let updateQueue = fiber.updateQueue;
+    let pending = updateQueue.shared.pending;
    
+    if(pending === null) {
+        update.next = update
+    } else {
+         update.next = pending.next;
+         pending.next = update;
+    }
+    updateQueue.shred.pending = update;

+    return markUpdateLaneFromFiberToRoot(fiber);
+ }

7.5 ReactFiberConcurrentUpdates.js

import { HostRoot } from './ReactWorkTags';

export function markUpdateLaneFromFiberToRoot(sourceFiber) {
    let node = sourceFiber;
    let parent = sourceFiber.return;
    while(parent !== null) {
        node = parent;
        parent = parent.return;
    }
    
    if(node.tag === HostRoot) {
        const root = node.stateNode;
        return root;
    }
    
    return null;
}

8.performConcurrentWorkOnRoot

8.1 ReactFiberReconciler.js

import { createFiberRoot } from './ReactFiberRoot';
import { createUpdate, enqueueUpdate } from './ReactFiberClassUpdate';
+ import { scheduleUpdateOnFiber } from './ReactFiberWorkLoop';

export function updateContainer(element, container) {
    const current = container.current;
    const update = createUpdate(current);
    update.payload = { element };
    const root = enqueueUpdate(current, update);
    conole.log('updateContainer--root=>', root);
   
+   scheduleUpdateOnFiber(root);
}
export function createContainer(containerInfo) {
    createFiberRoot(containerInfo)
}

8.2 ReactFiberWorkLoop.js

import { scheduleCallBack } from 'scheduler/index.js';

export function scheduleUpdateOnFiber(root) {
    ensureRootIsScheduled(root)
}
function ensureRootIsScheduled(root) {
    scheduleCallBack(performConcurrentWorkOnRoot.bind(null, root))
}
function performConcurrentWorlOnRoot(root) {
    console.log(root);
}

8.3 scheduler/index.js

export * from ./src/forks/scheduler;

8.4 scheduler.js

export function scheduleCallback(callback) {
    requestIdleCallBack(callback)
}

9.prepareFreshStack

image.png

9.1 ReactFiberWorkLoop.js

import { scheduleCallBack } from 'scheduler/index';
import { createWorkInProgress } from './ReactFiber';

+ let workInProgress = null;
export function scheduleUpdateOnFiber(root) {
    ensureRootIsScheduled(root)
}
function ensureRootIsScheduled(root) {
    scheduleCallBack(performConcurrentWorkOnRoot.bind(null, root))
}
function performConcurrentWorkOnRoot(root) {
    console.log(root);
+   renderRootSync(root);
}

+ function renderRootSync(root) {
+       prepareFreshStack(root);
+ }
+ function prepareFreshStack(root) {
+   workInProgress = createWorkInProgress(root.current, null);
+   console.log('prepareFreshStack-workInProgress', workInProgress);
+ }

9.2 ReactFiber.js

import { HostRoot } from './ReactWorkTags';
import { NoFlags } from './ReactFiberFlags';

export function FiberNode(tag, pendingProps, key){
    this.tag = tag;
    this.key = key;
    this.type = null;
    
    this.pendingProps = pendingProps;
    this.memoizeProps = null;
    this.memoizeState = null;
    
    this.child = null;
    this.return = null;
    this.sibling = null;
    
    this.stateNode = null;
    this.alternate = null;
    this.updateQueue = null;
    
    this.flags = NoFlags;
    this.substringFlags = NoFlags;
}
export function createFiber(tag, pendingProps, key) {
    return new FiberNode(tag, pendingProps, key)
}
export function createHostRootFiber() {
    createFiber(HostRoot, null, null)
}

+ export function workInProgress(current, pendingProps) {
+    let workInProgress = current.alternate;
+    if(workInProgress === null) {
+        workInProgress = createFiber(HostRoot, pendingProps, current.key);
+        workInProgress.type = current.type;
+        workInProgress.stateNode = current.stateNode;
+        workInProgress.alternate = current;
+        current.alternate = workInProgress;
+    }else {
+        workInProgress.type = current.type;
+        workInProgress.pendingProps = current.pendingProps;
+        workInProgress.falgs = NoFlags;
+        workInProgress.substreeFlags = NoFlags;
+    }
+    workInProgress.memoizedProps = current.memoizeProps;
+    workInProgress.memoizedState = current.memoizeState;
+    workInProgress.child = current.child;
+    worlInProgress.sibling = current.cibling;
+    workInProgress.updateQueue = current.updateQueue;
+    workInProgress.index = current.index;
+ }

10.beginWork

10.1 ReactFiberWorkLoop.js

import { scheduleCallBack } from 'scheduler/index';
import { createWorkInProgress } from './ReactFiber';
+import { beginWork } from './ReactFiberBegingWork';

let workInProgress = null;
export function scheduleUpdateOnFiber(root) {
    ensureRootIsScheduled(root)
}
function ensureRootIsScheduled(root) {
    scheduleCallBack(performConcurrentWorkOnRoot.bind(null, root))
}
/**
 * 根据虚拟DOM构建fiber树,要创建真实的DOM节点,还需要把真实的DOM节点插入容器
 * @param {*} root
*/
function performConcurrentWorkOnRoot(root) {
    console.log(root);
    //第一次渲染以同步的方式渲染根节点,初次渲染的时候,都是同步
    //因为第一次渲染时,要保证页面尽快渲染出来,呈现给用户,不能卡顿
    renderRootSync(root);
}

function renderRootSync(root) {
    //开始构建fiber树
    prepareFreshStack(root);
+   workLoopSync();
}
function prepareFreshStack(root) {
    workInProgress = createWorkInProgress(root.current, null);
    console.log('prepareFreshStack-workInProgress', workInProgress);
}

+ function workLoopSync() {
+    if(workInProgress !== null) {
+        performUnitOfWork(workInProgress)
+    }
+ }
/**
 * 执行一个工作单元
 * @params {*} unitOfWork
 */
+ function performUnitOfWork(unitOfWork) {
     //获取新的fiber对应的老fiber
+    let current = unitOfWork.alternate;
     //完成当前fiber的子fiber链表构建后
+    const next = begingWork(current, unitOfWork);
+    unitOfWork.memoizedProps = unitOfWork,pengdingProps;
+    if(next === null) {
         //如果没有子节点表示当前的fiber已经完成了
+        //completeUnitOfWork(unitOfWork)
+        workInProgress = null;
+    }else {
        //如果有子节点,就让子节点成为下一个工作单元
+        workInProgress = next
+    }
+ }

10.2 ReactFiberBeginWork.js

import { HostRoot, HostComponent, HostText } from "./ReactWorkTags';
import { processUpdate } from './ReactFiberClassUpdate';
import { mountChildFibers, reconcileChildFibers } from './ReactChildFiber';

/**
 * 根据新的虚拟DOM生成新的Fiber链表
 * @param {*} current 老的父Fiber
 * @param {*} workInProgress 新的Fiber
 * @param {*} next 新的子虚拟DOM
 */
function reconcileChildren(current, workInProgress, nextChildren) {
    if(current === null) {
        workInProgress.child = mountChildFibers(workInProgress, null, nextChildren)
    }else {
        workInProgress.child = reconcileChildFibers(workInProgress, current.child, nextChildren)
    }
}
function updateHostRoot(current, workInProgress) {
    //需知道它的子虚拟DOM,知道它的儿子的虚拟DOM信息
    processUpdate(workInProgress); //workInprogress.memoizedState={ element }
    let nextState = workInProgress.memoizedState;
    let nextChildren = nextState.element;
    //根据新的虚拟DOM生成子fiber链表
    reconcileChildren(current, workInProgress, nextChild);
    return workInProgress.child;
}
/**
 * 构建原生组件的子fiber链表
 * @param {*} current 老fiber
 * @param {*} workInProgress 新fiber h1
 */
function updateHostComponent(current, workInProgress) {
    const {type} = workInProgess;
    const nextProps = workInProgress.pendingProps;
    let nextChildren = nextProps.children;
    //判断当前虚拟DOM它的儿子是不是一个文本独生子
    const isDirectTextChild = shouldSetTextContent(type, nextProps);
    if(isDirectTextChild) {
        nextChildren = null;
    }
    reconcileChildren(current, workInProgress, nextChildren);
    return workInProgress.child;
}
/**
 * 目标是根据新虚拟DOM构建新的fiber子链表 child .sibling
 * @param {*} current 老的fiber
 * @param {*} workInProgress 新的fiber
 * @return
 */
export function beginWork(current, workInProgress) {
    switch (workInProgress.tag) {
        case HostRoot:
            return updateHostRoot(current, workInProgress);
        case HostCompontent:
            return updateHostComponent(current, HostComponent);
        case HostText:
            return null;
        default:
            return null;
    }
}

10.3 ReactFiberClassUpdate.js

import { markUpdateLaneFromFiberToRoot } from "./ReactFiberConcurrentUpdates";
+ import { assign } from 'shared/assign';

const UpdateState = 0;

export function initializeUpdateQueue(fiber) {
    const queue = {
        shared: {
            pending: null
        }
    }
    fiber.updateQueue = queue;
}

export function createUpdate() {
   const update = { tag: UpdateState };
   return update;
}

export function enqueueUpdate(fiber, update) {
   let updateQueue = fiber.updateQueue;
   let pending = updateQueue.shared.pending;
    
   if(pending === null) {
       update.next = update
   } else {
        update.next = pending.next;
        pending.next = update;
   }
   updateQueue.shred.pending = update;

   return markUpdateLaneFromFiberToRoot(fiber);
}

+ function getStateFromUpdate(update, prevState) {
    switch (update.tag) {
        case UpdateState:
            const {payload} = update;
            const partialState = payload;
            return assign({}, prevState, partialState);
       default:
           return prevState;
    }
}

+ export function processUpdate(workInProgress) {
+      let queue = workInProgress.updateQueue;
+      let sharedQueue = queue.shared;
+      let pendingQueue = shareQueue.pending;
      
+      if(pendingQueue !== null) {
+          queue.shared.pending === null;
+          let lastPendingUpdate = pendingQueue;
+          let firstPendingUpdate = lastPendingUpdate.next;
+          lastPendingUpdate.next = null;
+          let newState = workInProgress.memoizedState;
+          let update = firstPendingUpdate;
          
+          while(update) {
+              newState = getStateFromUpdate(update, newState);
+              update = update.next;
+          }
+          workInProgress.memoizedState = newState;
+      }
+ }

10.4 shared/assign.js

const { assign } = Object;
export default assign;

10.5 ReactChildFiber.js

import { REACT_ELEMENT_TYPE } from "shared/ReactSymbols";
import isArray from "shared/isArray";
import { createFiberFromElement, FiberNode, createFiberFromText } from "./ReactFiber";
import { Placement } from "./ReactFiberFlags";
import { HostText } from "./ReactWorkTags";

function createChildReconciler(shouldTrackSideEffects){
    function reconcileSingleElement(returnFiber, currentFirstChild, element) {
         //因为是初次挂载,老的节点currentFirstFiber是没有的,所以直接根据虚拟DOM创建新Fiber节点
        const created = createFiberFromElement(element);
        created.return = returnFiber;
        return created;
    }
    function placeSingeChild(newFiber){
        if(shouleTrackSideEffects) newFiber.flags |= Placement;
        return newFiber;
    }
    function reconcileSingleTextNode(returnFiber, currentFirstChild, content) {
        const created = new FiberNode(HostRoot, {content}, null);
        created.return = returnFiber;
        return created;
    }
    /**
     * 设置副作用
     * @param {*} newFiber
     * return
     */
    function placeChild(newFiber, newIndex){
        newFiber.index = newIndex;
        if(shouldTrackSideEffects) {
            newFiber.flags |= Placement;
        }
    }
    function reconcileArrayChild(returnFiber, currentFirstChild, newChildren) {
        let resultingFirstChild = null;
        let prevoiusNewFiber = null;
        let newIdx = 0;
        for(;newIdx < newChildren.length; newIdx++) {
            const newFiber = createChild(returnFiber, newChildren[newIdx]);
            if(newFiber === null) {
                countine;
            }
            placeChild(newFiber, newIdx);
            //如果previousNewFiber为null,说明这是第一个fiber
            if(previousNewFiber === null) {
                resultingFirstChild = newFiber;
            }else {//否则说明不是大儿子,就把这个newFiber添加上一个子节点后面
                previousNewFiber.sibling = newFiber;
            }
            //让newFiber成为最后一个或者说上一个子fiber
            previousNewFiber = newFiber;
        }
        return resultingFirstChild;
    }
    /**
     * 比较子Fibers DOM-DIFF 就是用老的子fiber链表和新的虚拟DOM进行比较的过程
     * @param {*} returnFiber 新的父Fiber
     * @param {*} currentFirstFiber 老fiber的第一个子fiber current一般来说指的是老
     * @param {*} newChild 新的子虚拟DOM h1虚拟DOM
     */
    function reconcileChildFibers(returnFiber, currentFirstFiber, newChild){
        //新的节点只有一个的情况
        if(typeof newChild === 'object' && newChild !== null) {
            switch (newChild.$$typeof) {
                case REACT_ELEMENT_TYPE:
                    return placeSingleChild(reconcileSingleElement(returnFiber, currentFirstChild, newChild));
                default:
                    break;
            }
            //newChild [hello文本节点,span虚拟DOM元素]
            if(isArray(newChild)) {
                return reconcileArrayChild(returnFiber, currentFirstChild, newChild)
            }
        }
        if(typeof newChild === 'string') {
            return placeSingeChild(reconcileSingleTextNode(returnFiber, currentFirstChild, newChild));
        }
        return null;
    }
    
    return reconcileChildFibers;
}

export const reconcileChildFibers = createChildReconciler(true);
export const mountChildFibers = crateChildReconciler(false);

10.6 ReactDOMHostConfig.js

export function shouldSetContentText(type, props) {
    return typeof props.children === 'string' || typeof props.children === 'number';
}

10.7 isArray.js

const { isArray } = Array;
export default isArray;

10.8 ReactFiber.js

import {
  HostRoot,
+ IndeterminateComponent,
+ HostComponent,
+ HostText,
} from "./ReactWorkTags";
import { NoFlags } from "./ReactFiberFlags";
export function FiberNode(tag, pendingProps, key) {
  this.tag = tag;
  this.key = key;
  this.type = null;
  this.stateNode = null;

  this.return = null;
  this.child = null;
  this.sibling = null;

  this.pendingProps = pendingProps;
  this.memoizedProps = null;
  this.updateQueue = null;
  this.memoizedState = null;

  this.flags = NoFlags;
  this.subtreeFlags = NoFlags;
  this.alternate = null;
  // this.index = 0;
}
function createFiber(tag, pendingProps, key) {
  return new FiberNode(tag, pendingProps, key);
}
export function createHostRootFiber() {
  return createFiber(HostRoot, null, null);
}
// We use a double buffering pooling technique because we know that we'll
// only ever need at most two versions of a tree. We pool the "other" unused
// node that we're free to reuse. This is lazily created to avoid allocating
// extra objects for things that are never updated. It also allow us to
// reclaim the extra memory if needed.
//我们使用双缓冲池技术,因为我们知道一棵树最多只需要两个版本
//我们将“其他”未使用的我们可以自由重用的节点
//这是延迟创建的,以避免分配从未更新的内容的额外对象。它还允许我们如果需要,回收额外的内存
export function createWorkInProgress(current, pendingProps) {
  let workInProgress = current.alternate;
  if (workInProgress === null) {
    workInProgress = createFiber(current.tag, pendingProps, current.key);
    workInProgress.type = current.type;
    workInProgress.stateNode = current.stateNode;
    workInProgress.alternate = current;
    current.alternate = workInProgress;
  } else {
    workInProgress.pendingProps = pendingProps;
    workInProgress.type = current.type;
    workInProgress.flags = NoFlags;
    workInProgress.subtreeFlags = NoFlags;
  }
  workInProgress.child = current.child;
  workInProgress.memoizedProps = current.memoizedProps;
  workInProgress.memoizedState = current.memoizedState;
  workInProgress.updateQueue = current.updateQueue;
  workInProgress.sibling = current.sibling;
  workInProgress.index = current.index;
  return workInProgress;
}
+export function createFiberFromTypeAndProps(type, key, pendingProps) {
+  let fiberTag = IndeterminateComponent; //函数的话
  //如果类型type是一字符串 span div,说明此Fiber类型是一个原生组件
+  if (typeof type === "string") {
+    fiberTag = HostComponent;
+  }
+  const fiber = createFiber(fiberTag, pendingProps, key);
+  fiber.type = type;
+  return fiber;
+}
/**
 * 根据虚拟DOM创建Fiber节点
 * @param {*} element
 */
+export function createFiberFromElement(element) {
+  const { type } = element;
+  const { key } = element;
+  const pendingProps = element.props;
// +  const { type, key, pendingProps } = element;
+  const fiber = createFiberFromTypeAndProps(type, key, pendingProps);
+  return fiber;
+}

+export function createFiberFromText(content) {
+  const fiber = createFiber(HostText, content, null);
+  return fiber;
+}

10.9 ReactFiberFlags.js

//0b表示一个二进制的前缀
export const NoFlags = 0b00000000000000000000000000; //无操作0
+export const Placement = 0b00000000000000000000000010; //插入2
+export const update = 0b00000000000000000000000100; //更新4
+export const MutationMask = Placement;

10.10 ReactWorkTags.js

export const HostRoot = 3; //容器根节点
//后面我们会讲到组件,组件分为类数组件和函数组件,因为它们都是函数,刚开始的时候
+export const IndeterminateComponent = 2;
+export const HostComponent = 5; //原生节点 span div h1
+export const HostText = 6; //纯文件节点

11.completeUnitOfWork

11.1 ReactFiberWorkLoop.js

import { scheduleCallback } from "scheduler";
import { createWorkInProgress } from "./ReactFiber";
import { beginWork } from "./ReactFiberBeginWork";
+import { completeWork } from "./ReactFiberCompleteWork";
let workInProgress = null;
export function scheduleUpdateOnFiber(root) {
  ensureRootIsScheduled(root);
}
function ensureRootIsScheduled(root) {
  scheduleCallback(performConcurrentWorkOnRoot.bind(null, root));
}
function performConcurrentWorkOnRoot(root) {
  renderRootSync(root);
}
function prepareFreshStack(root) {
  workInProgress = createWorkInProgress(root.current, null);
}
function renderRootSync(root) {
  prepareFreshStack(root);
  workLoopSync();
}

function workLoopSync() {
  while (workInProgress !== null) {
    performUnitOfWork(workInProgress);
  }
}
function performUnitOfWork(unitOfWork) {
  const current = unitOfWork.alternate;
  const next = beginWork(current, unitOfWork);
  unitOfWork.memoizedProps = unitOfWork.pendingProps;
  if (next === null) {
    //如果没有子节点表示当前的fiber已经完成
+   completeUnitOfWork(unitOfWork);
  } else {
    //如果有子节点,就让子节点成为下一个工作单元
    workInProgress = next;
  }
}

+ function completeUnitOfWork(unitOfWork){
+    let completedWork = unitOfWork;
+    do {
+        const current = completedWork.alternate;
+        const returnFiber = completedWork.return;
+        //执行此fiber 的完成工作,如果是原生组件的话就是创建真实的DOM节点
+        completeWork(current, completedWork);
+        //如果有弟弟,就构建弟弟对应的fiber子链表
+        const siblingFiber = completedWork.sibling;
+        if(siblingFiber !== null) {
+            workInProgress = siblingFiber;
+            return;
+        }
+        //如果没有弟弟,说明这当前完成的就是父fiber的最后一个节点
+        //也就是说一个父fiber,所有的子fiber全部完成了
+        completedWork = returnFiber;
+        workInProgress = completedWork;       
+    } while(completedWork !== null)
+ }

11.2 ReactFiberCompleteWork.js

import {
  appendInitialChild,
  createInstance,
  createTextInstance,
  finalizeInitialChildren,
} from "react-dom-bindings/src/client/ReactDOMHostConfig";
import { HostComponent, HostRoot, HostText } from "./ReactWorkTags";
import { NoFlags } from "./ReactFiberFlags";

function bubbleProperties(completedWork) {
  let subtreeFlags = NoFlags;
  //遍历当前fiber的所有子节点,把所有的子节点的副作用,以及子节点的子节点的副作用全部合并
  let child = completedWork.child;
  while (child !== null) {
    subtreeFlags |= child.subtreeFlags;
    subtreeFlags |= child.flags;
    child = child.sibling;
  }
  completedWork.subtreeFlags |= subtreeFlags;
}

/**
 * 把当前的完成的fiber所有的子节点对应的真实DOM都挂载到自己父parent真实DOM节点上
 * @param {*} parent 当前完成的fiber真实的DOM节点
 * @param {*} workInProgress 完成的fiber
 */
function appendAllChildren(parent, workInProgress) {
  // 我们只有创建的顶级fiber,但需要递归其子节点来查找所有终端节点
  let node = workInProgress.child;
  while (node !== null) {
    // 如果是原生节点,直接添加到父节点上
    if (node.tag === HostComponent || node.tag === HostText) {
      appendInitialChild(parent, node.stateNode);
      // 再看看第一个节节点是不是原生节点
    } else if (node.child !== null) {
      // node.child.return = node
      node = node.child;
      continue;
    }
    if (node === workInProgress) {
      return;
    }
    // 如果没有弟弟就找父亲的弟弟
    while (node.sibling === null) {
      // 如果找到了根节点或者回到了原节点结束
      if (node.return === null || node.return === workInProgress) {
        return;
      }
      node = node.return;
    }
    // node.sibling.return = node.return
    // 下一个弟弟节点
    node = node.sibling;
  }
}

/**
 * 完成一个fiber节点
 * @param {*} current 老fiber
 * @param {*} workInProgress 新构建的fiber
 */
export function completeWork(current, workInProgress) {
    const newProps = workInProgress.pengdingProps;
    switch (workInProgress.tag) {
        case HostComponent:
            //这里只是在处理创建或者说挂载新节点的逻辑,后面此处进行区分是否是初次挂载还是更新
            //创建真实DOM节点
            const { type } = workInProgress;
            const instance = createInstance(type, newProps, workInProgress);
            //把自己所有的儿子都添加到自己的身上
            appendAllChildren(instance, workInProgress);
            workInProgress.stateNode = instance;
            finalizeInitialChildren(instance, type, newProps);
            bubbleProperties(workInProgress);
            break;
        case HostRoot:
            bubbleProperties(workInProgress);
            break;
        case HostText:
            //如果完成的fiber是文本节点,那就创建真实的文本节点
            const newText = newProps;
            //创建真实的DOM节点并传入stateNode
            workInProgress.stateNode = createTextInstance(newText);
            //向上冒泡属性
            bubbleProperties(workInProgress);
            break;
        deafult:
            break;
    }
    
}

11.3 ReactDOMHostConfig.js

+import { setInitialProperties } from "./ReactDOMComponent";

export function shouldSetTextContent(type, props) {
  return (
    typeof props.children === "string" || typeof props.children === "number"
  );
}
+export const appendInitialChild = (parent, child) => {
+  parent.appendChild(child);
+};
+export const createInstance = (type, props, internalInstanceHandle) => {
+  const domElement = document.createElement(type);
+  return domElement;
+};
+export const createTextInstance = (content) => document.createTextNode+(content);
+export function finalizeInitialChildren(domElement, type, props) {
+  setInitialProperties(domElement, type, props);
+}

11.4 ReactDOMComponent.js

import { setValueForStyles } from "./CSSPropertyOperations";
import setTextContent from "./setTextContent";
import { setValueForProperty } from "./DOMPropertyOperations";

const CHILDREN = "children";
const STYLE = "style";
function setInitialDOMProperties(tag, domElement, nextProps) {
  for (const propKey in nextProps) {
    if (nextProps.hasOwnProperty(propKey)) {
      const nextProp = nextProps[propKey];
      if (propKey === STYLE) {
        setValueForStyles(domElement, nextProp);
      } else if (propKey === CHILDREN) {
        if (typeof nextProp === "string") {
          setTextContent(domElement, nextProp);
        } else if (typeof nextProp === "number") {
          setTextContent(domElement, `${nextProp}`);
        }
      } else if (nextProp != null) {
        setValueForProperty(domElement, propKey, nextProp);
      }
    }
  }
}
export function setInitialProperties(domElement, tag, props) {
  setInitialDOMProperties(tag, domElement, props);
}

11.5 CSSPropertyOperations.js

export function setValueForStyles(node, styles) {
  const { style } = node;
  for (const styleName in styles) {
    if (styles.hasOwnProperty(styleName)) {
      const styleValue = styles[styleName];
      style[styleName] = styleValue;
    }
  }
}

11.6 setTextContent.js

function setTextContent(node, text) {
  node.textContent = text;
}

export default setTextContent;

11.7 DOMPropertyOperations.js

export function setValueForProperty(node, name, value) {
  if (value === null) {
    node.removeAttribute(name);
  } else {
    node.setAttribute(name, value);
  }
}

12.commitRoot

12.1 ReactFiberWorkLoop.js

import { scheduleCallback } from "scheduler";
import { createWorkInProgress } from "./ReactFiber";
import { beginWork } from "./ReactFiberBeginWork";
import { completeWork } from "./ReactFiberCompleteWork";
+import { MutationMask, NoFlags } from "./ReactFiberFlags";
let workInProgress = null;
export function scheduleUpdateOnFiber(root) {
  ensureRootIsScheduled(root);
}
function ensureRootIsScheduled(root) {
  scheduleCallback(performConcurrentWorkOnRoot.bind(null, root));
}
function performConcurrentWorkOnRoot(root) {
  renderRootSync(root);
  //开始进入提交阶段,就是执行副作用,修改真实DOM
+ const finishedWork = root.current.alternate;
+ root.finishedWork = finishedWork;
+ commitRoot(root);
}
+function commitRoot(root) {
+  const { finishedWork } = root;
  //判断子树有没副作用
+  const subtreeHasEffects =
+    (finishedWork.subtreeFlags & MutationMask) !== NoFlags;
+  const rootHasEffect = (finishedWork.flags & MutationMask) !== NoFlags;
  //如果自己有副作用或者子节点有副作用就进行DOM操作
+  if (subtreeHasEffects || rootHasEffect) {
+    console.log("commitRoot");
+  }
  //等DOM变更后,就可以把让root的current指向新的fiber树
+  root.current = finishedWork;
+}

function prepareFreshStack(root) {
  workInProgress = createWorkInProgress(root.current, null);
}
function renderRootSync(root) {
  prepareFreshStack(root);
  workLoopSync();
}

function workLoopSync() {
  while (workInProgress !== null) {
    performUnitOfWork(workInProgress);
  }
}
function performUnitOfWork(unitOfWork) {
  const current = unitOfWork.alternate;
  const next = beginWork(current, unitOfWork);
  unitOfWork.memoizedProps = unitOfWork.pendingProps;
  if (next === null) {
    completeUnitOfWork(unitOfWork);
  } else {
    workInProgress = next;
  }
}

function completeUnitOfWork(unitOfWork) {
  let completedWork = unitOfWork;
  do {
    const current = completedWork.alternate;
    const returnFiber = completedWork.return;
    completeWork(current, completedWork);
    const siblingFiber = completedWork.sibling;
    if (siblingFiber !== null) {
      workInProgress = siblingFiber;
      return;
    }
    completedWork = reurnFiber;
    workInProgress = completedWork;
  } while (completedWork !== null);
}

13.commitMutationEffectsOnFiber

13.1 ReactFiberWorkLoop.js

import { scheduleCallback } from "scheduler";
import { createWorkInProgress } from "./ReactFiber";
import { beginWork } from "./ReactFiberBeginWork";
import { completeWork } from "./ReactFiberCompleteWork";
import { MutationMask, NoFlags } from "./ReactFiberFlags";
+import { commitMutationEffectsOnFiber } from "./ReactFiberCommitWork";
let workInProgress = null;
export function scheduleUpdateOnFiber(root) {
  ensureRootIsScheduled(root);
}
function ensureRootIsScheduled(root) {
  scheduleCallback(performConcurrentWorkOnRoot.bind(null, root));
}
function performConcurrentWorkOnRoot(root) {
  renderRootSync(root);
  const finishedWork = root.current.alternate;
  root.finishedWork = finishedWork;
  commitRoot(root);
}
function commitRoot(root) {
  const { finishedWork } = root;
  const subtreeHasEffects =
    (finishedWork.subtreeFlags & MutationMask) !== NoFlags;
  const rootHasEffect = (finishedWork.flags & MutationMask) !== NoFlags;
  if (subtreeHasEffects || rootHasEffect) {
+   commitMutationEffectsOnFiber(finishedWork, root);
  }
  root.current = finishedWork;
}
function prepareFreshStack(root) {
  workInProgress = createWorkInProgress(root.current, null);
}
function renderRootSync(root) {
  prepareFreshStack(root);
  workLoopSync();
}

function workLoopSync() {
  while (workInProgress !== null) {
    performUnitOfWork(workInProgress);
  }
}
function performUnitOfWork(unitOfWork) {
  const current = unitOfWork.alternate;
  const next = beginWork(current, unitOfWork);
  unitOfWork.memoizedProps = unitOfWork.pendingProps;
  if (next === null) {
    completeUnitOfWork(unitOfWork);
  } else {
    workInProgress = next;
  }
}

function completeUnitOfWork(unitOfWork) {
  let completedWork = unitOfWork;
  do {
    const current = completedWork.alternate;
    const returnFiber = completedWork.return;
    completeWork(current, completedWork);
    const siblingFiber = completedWork.sibling;
    if (siblingFiber !== null) {
      workInProgress = siblingFiber;
      return;
    }
    completedWork = returnFiber;
    workInProgress = completedWork;
  } while (completedWork !== null);
}

13.2 ReactFiberCommitWork.js

import { HostRoot, HostComponent, HostText } from "./ReactWorkTags";
import { MutationMask, Placement } from "./ReactFiberFlags";
function recursivelyTraverseMutationEffects(root, parentFiber) {
  if (parentFiber.subtreeFlags & MutationMask) {
    let { child } = parentFiber;
    while (child !== null) {
      commitMutationEffectsOnFiber(child, root);
      child = child.sibling;
    }
  }
} 
/**
 * 把此fiber的真实DOM插入到父DOM里
 * @param {*} finishedWork
 */
function ç(finishedWork) {
  console.log("commitPlacement", finishedWork);
}
function commitReconciliationEffects(finishedWork) {
  const { flags } = finishedWork;
  //如果此fiber要执行插入操作的话
  if (flags & Placement) {
    //进行操作插件,也就是把此fiber对应的真实DOM节点添加到父真实DOM节点上
    commitPlacement(finishedWork);
    //把flags里Placement删除
    finishedWork.flags &= ~Placement;
  }
}
/**
 * 遍历fiber树,执行fiber上的副作用
 * @param {*} finishedWork fiber节点
 * @param {*} root 根节点
 */
export function commitMutationEffectsOnFiber(finishedWork, root) {
  switch (finishedWork.tag) {
    case HostRoot:
    case HostComponent:
    case HostText: {
      //先遍历它们的子节点,处理它们的子节点上的副作用
      recursivelyTraverseMutationEffects(root, finishedWork);
      //再处理自己身上的副作用
      commitReconciliationEffects(finishedWork);
      break;
    }
    default: {
      break;
    }
  }
}

14.commitPlacement

image.png

14.1 ReactFiberCommitWork.js

import { HostRoot, HostComponent, HostText } from "./ReactWorkTags";
import { MutationMask, Placement } from "./ReactFiberFlags";
+import {
+  insertBefore,
+  appendChild,
+} from "react-dom-bindings/src/client/ReactDOMHostConfig";
function recursivelyTraverseMutationEffects(root, parentFiber) {
  if (parentFiber.subtreeFlags & MutationMask) {
    let { child } = parentFiber;
    while (child !== null) {
      commitMutationEffectsOnFiber(child, root);
      child = child.sibling;
    }
  }
}
+function isHostParent(fiber) {
+  return fiber.tag === HostComponent || fiber.tag === HostRoot;
+}
+function getHostParentFiber(fiber) {
+  let parent = fiber.return;
+  while (parent !== null) {
+    if (isHostParent(parent)) {
+      return parent;
+    }
+    parent = parent.return;
+  }
+  return parent;
+}
/**
 * 把子节点对应的真实DOM插入到父节点DOM中
 * @param {*} node 将要插入的fiber节点
 * @param {*} parent 父真实DOM节点
 */
+function insertOrAppendPlacementNode(node, before, parent) {
+  const { tag } = node;
  //判断此fiber对应的节点是不是真实DOM节点
+  const isHost = tag === HostComponent || tag === HostText;
  //如果是的话直接插入
+  if (isHost) {
+    const { stateNode } = node;
+    if (before) {
+      insertBefore(parent, stateNode, before);
+    } else {
+      appendChild(parent, stateNode);
+    }
+  } else {
  //如果node不是真实的DOM节点,获取它的大儿子
+    const { child } = node;
+    if (child !== null) {
       //把打儿子添加到父亲DOM节点里面去
+      insertOrAppendPlacementNode(child, before, parent);
+      let { sibling } = child;
+      while (sibling !== null) {
+        insertOrAppendPlacementNode(sibling, before, parent);
+        sibling = sibling.sibling;
+      }
+    }
+  }
+}
  /**
   * 找到要插入的锚点
   * 找到可以插在它的前面的那个fiber对应的真实DOM
   */
+function getHostSibling(fiber) {
+  let node = fiber;
+  siblings: while (true) {
+    // 如果我们没有找到任何东西,让我们试试下一个弟弟
+    while (node.sibling === null) {
+      if (node.return === null || isHostParent(node.return)) {
+        // 如果我们是根Fiber或者父亲是原生节点,我们就是最后的弟弟
+        return null;
+      }
+      node = node.return;
+    }
+    // node.sibling.return = node.return
+    node = node.sibling;
+    while (node.tag !== HostComponent && node.tag !== HostText) {
+      // 如果它不是原生节点,并且,我们可能在其中有一个原生节点
+      // 试着向下搜索,直到找到为止
+      if (node.flags & Placement) {
+        // 如果我们没有孩子,可以试试弟弟
+        continue siblings;
+      } else {
+        // node.child.return = node
+        node = node.child;
+      }
+    } // Check if this host node is stable or about to be placed.
+    // 检查此原生节点是否稳定可以放置
+    if (!(node.flags & Placement)) {
+      // 找到它了!
+      return node.stateNode;
+    }
+  }
+}
/**
 * 把此fiber的真实DOM插入到父DOM里
 * @param {*} finishedWork
 */
function commitPlacement(finishedWork) {
+  const parentFiber = getHostParentFiber(finishedWork);
+  switch (parentFiber.tag) {
+    case HostComponent: {
+      const parent = parentFiber.stateNode;
+      const before = getHostSibling(finishedWork); //获取最近的弟弟真实DOM节点
+      insertOrAppendPlacementNode(finishedWork, before, parent);
+      break;
+    }
+    case HostRoot: {
+      const parent = parentFiber.stateNode.containerInfo;
+      const before = getHostSibling(finishedWork);
+      insertOrAppendPlacementNode(finishedWork, before, parent);
+      break;
+    }
+    default:
+      break;
+  }
}
function commitReconciliationEffects(finishedWork) {
  const { flags } = finishedWork;
  if (flags & Placement) {
    commitPlacement(finishedWork);
    finishedWork.flags &= ~Placement;
  }
}
export function commitMutationEffectsOnFiber(finishedWork, root) {
  switch (finishedWork.tag) {
    case HostRoot:
    case HostComponent:
    case HostText: {
      recursivelyTraverseMutationEffects(root, finishedWork);
      commitReconciliationEffects(finishedWork);
      break;
    }
    default: {
      break;
    }
  }
}

14.2 ReactDOMHostConfig.js

import { setInitialProperties } from "./ReactDOMComponent";
export function shouldSetTextContent(type, props) {
  return (
    typeof props.children === "string" || typeof props.children === "number"
  );
}
export const appendInitialChild = (parent, child) => {
  parent.appendChild(child);
};
export const createInstance = (type, props, internalInstanceHandle) => {
  const domElement = document.createElement(type);
  return domElement;
};
export const createTextInstance = (content) => document.createTextNode(content);
export function finalizeInitialChildren(domElement, type, props) {
  setInitialProperties(domElement, type, props);
}
+export function appendChild(parentInstance, child) {
+  parentInstance.appendChild(child);
+}
+export function insertBefore(parentInstance, child, beforeChild) {
+  parentInstance.insertBefore(child, beforeChild);
+}

15.函数组件

15.1 src/main.jsx

import {createRoot} from 'react-dom/client';
function FunctionComponent(){
    return (
        <div>
            hello<span style={{color: 'red'}}>world</span>
        </div>
    )
};
let element = <FunctionComponent />;
const root = createRoot(documnet.getElementById('root'));
root.render(element);

15.2 ReactWorkTags.js

+ export const indeterminateComponent = 2;
+ export const FunctionComponent = 0;
export const HostRoot = 3;
export const HostComponent = 5;
export const HostText = 6;

15.3 ReactFiberBeginWork.js

import {
  HostRoot,
  HostComponent,
  HostText,
+ IndeterminateComponent,
+ FunctionComponent,
} from "./ReactWorkTags";
import { processUpdateQueue } from "./ReactFiberClassUpdateQueue";
import { mountChildFibers, reconcileChildFibers } from "./ReactChildFiber";
import { shouldSetTextContent } from "react-dom-bindings/src/client/ReactDOMHostConfig";
+import { renderWithHooks } from "react-reconciler/src/ReactFiberHooks";

function reconcileChildren(current, workInProgress, nextChildren) {
  if (current === null) {
    workInProgress.child = mountChildFibers(workInProgress, null, nextChildren);
  } else {
    workInProgress.child = reconcileChildFibers(
      workInProgress,
      current.child,
      nextChildren
    );
  }
}
function updateHostRoot(current, workInProgress) {
  processUpdateQueue(workInProgress);
  const nextState = workInProgress.memoizedState;
  const nextChildren = nextState.element;
  reconcileChildren(current, workInProgress, nextChildren);
  return workInProgress.child;
}
function updateHostComponent(current, workInProgress) {
  const { type } = workInProgress;
  const nextProps = workInProgress.pendingProps;
  let nextChildren = nextProps.children;
  const isDirectTextChild = shouldSetTextContent(type, nextProps);
  if (isDirectTextChild) {
    nextChildren = null;
  }
  reconcileChildren(current, workInProgress, nextChildren);
  return workInProgress.child;
}

+ function mountIndeterminateComponent(current, workInProgress, Component) {
+    const props = workInProgress.props;
+    let value = renderWithHooks(null, workInProgress, Component ,props);
+    workInProgress.tag = FunctionComponent;
+    reconcileChildren(null, workInProgress, value);
+    return workInProgress.child;
+ }
export begingWork(current, workInProgress) {
    switch(workInProgress.tag) {
       + case indeterminateComponent:
       +     return mountIndeterminateComponent(current, workInProgress, workInProgress.type);
       case HostRoot:
           return updateHostRoot(current, workInProgress);
       case HostComponent:
           return updateHostComponent(current, workInProgress);
       case HostText:
           break;
       default:
           return null;
    }
}

15.4 ReactFiberHooks.js

export function renderWithHooks(current, workInProgress, Component, props) {
    const children = Component(props);
    return children;
}

15.5 ReactFiberCommitWork.js

import {
  HostRoot,
  HostComponent,
  HostText,
+ FunctionComponent,
} from "./ReactWorkTags";
import { MutationMask, Placement } from "./ReactFiberFlags";
import {
  insertBefore,
  appendChild,
} from "react-dom-bindings/src/client/ReactDOMHostConfig";
function recursivelyTraverseMutationEffects(root, parentFiber) {
  if (parentFiber.subtreeFlags & MutationMask) {
    let { child } = parentFiber;
    while (child !== null) {
      commitMutationEffectsOnFiber(child, root);
      child = child.sibling;
    }
  }
}
function isHostParent(fiber) {
  return fiber.tag === HostComponent || fiber.tag === HostRoot;
}
function getHostParentFiber(fiber) {
  let parent = fiber.return;
  while (parent !== null) {
    if (isHostParent(parent)) {
      return parent;
    }
    parent = parent.return;
  }
  return parent;
}
function insertOrAppendPlacementNode(node, before, parent) {
  const { tag } = node;
  const isHost = tag === HostComponent || tag === HostText;
  if (isHost) {
    const { stateNode } = node;
    if (before) {
      insertBefore(parent, stateNode, before);
    } else {
      appendChild(parent, stateNode);
    }
  } else {
    const { child } = node;
    if (child !== null) {
      insertOrAppendPlacementNode(child, before, parent);
      let { sibling } = child;
      while (sibling !== null) {
        insertOrAppendPlacementNode(sibling, before, parent);
        sibling = sibling.sibling;
      }
    }
  }
}
function getHostSibling(fiber) {
  let node = fiber;
  siblings: while (true) {
    // 如果我们没有找到任何东西,让我们试试下一个弟弟
    while (node.sibling === null) {
      if (node.return === null || isHostParent(node.return)) {
        // 如果我们是根Fiber或者父亲是原生节点,我们就是最后的弟弟
        return null;
      }
      node = node.return;
    }
    // node.sibling.return = node.return
    node = node.sibling;
    while (node.tag !== HostComponent && node.tag !== HostText) {
      // 如果它不是原生节点,并且,我们可能在其中有一个原生节点
      // 试着向下搜索,直到找到为止
      if (node.flags & Placement) {
        // 如果我们没有孩子,可以试试弟弟
        continue siblings;
      } else {
        // node.child.return = node
        node = node.child;
      }
    } // Check if this host node is stable or about to be placed.
    // 检查此原生节点是否稳定可以放置
    if (!(node.flags & Placement)) {
      // 找到它了!
      return node.stateNode;
    }
  }
}
function commitPlacement(finishedWork) {
  const parentFiber = getHostParentFiber(finishedWork);
  switch (parentFiber.tag) {
    case HostComponent: {
      const parent = parentFiber.stateNode;
      const before = getHostSibling(finishedWork);
      insertOrAppendPlacementNode(finishedWork, before, parent);
      break;
    }
    case HostRoot: {
      const parent = parentFiber.stateNode.containerInfo;
      const before = getHostSibling(finishedWork);
      insertOrAppendPlacementNode(finishedWork, before, parent);
      break;
    }
    default:
      break;
  }
}
function commitReconciliationEffects(finishedWork) {
  const { flags } = finishedWork;
  if (flags & Placement) {
    commitPlacement(finishedWork);
    finishedWork.flags &= ~Placement;
  }
}
export function commitMutationEffectsOnFiber(finishedWork, root) {
  switch (finishedWork.tag) {
    case HostRoot:
+   case FunctionComponent:
    case HostComponent:
    case HostText: {
      recursivelyTraverseMutationEffects(root, finishedWork);
      commitReconciliationEffects(finishedWork);
      break;
    }
    default: {
      break;
    }
  }
}

16.事件注册名

16.1 src/main.jsx

import { createRoot } from 'react-dom/client';
function FunctionComponent() {
    return(
        <div
            onClick={()=>{console.log('onClick FunctionComponent')}}
            onClickCapture={()=>{console.log('onClickCapture FunctionComponent')}}
        >
            hello<span style={{color: 'red'}}>world</span>
        <div>
    )
};
let element = <FunctionComponent />
root.render(element);

16.2 ReactDOMRoot.js

+ import { listenAllToSupportedEvent } from 'react_dom_binding/src/events/DOMPluginEventSystem';

export function ReactDOMRoot(internalRoot) {
    this._internalRoot = internalRoot;
}
ReactDOMRoot.prototype.render = function render(children){
    const root = this._internalRoot;
    root.containerInfo.innerHTML = '';
    updateContainer(children, root);
}
export function createRoot(container) {
    const root = createContainer(container);
    + listenAllToSupportedEvents(container);
    return new ReactDOMRoot(root);
}

16.3 DOMPluginEventSystem.js

import { allNativeEvents } from './EventRegistry.js';
import * as SimpleEventPlugin from './plugins/SimpleEventPlugin.js'

SimpleEventPlugin.registerEvents()
export function listenToAllSupportedEvents(rootContainerElement){
    allNativeEvents.forEach(domEventName => {
        console.log('domEventName')
    })
}

16.4 EventRegistry.js

export const allNativeEvents = new Set();
export function registerTwoPhaseEvent(registrationName, dependencies){
    registerDirectEvent(registrationName, dependencies);
    registerDirectEvent(registrationName + 'Capture', dependencies);
}
export function registerDirectEvent(registrationName, dependencies) {
    for(let i = 0; i<dependencies.length; i++) {
        allNativeEvents.add(dependencies[i])
    }
}

16.5 SimpleEventPlugin.js

import { registerSimpleEvents } from '../DOMEventProperties';
export registerSimpleEvents as SimpleEventPlugin;

16.6 DOMEventProperties.js

import { registerTwoPhaseEvent } from './EventRegistry.js';

const registerEventPluginEvents = ['clikc', ...];

function registerSimpleEvent(domEventName. reactName) {
    registerTwoPhaseEvent(reactName, [domEventName])
}

export function registerSimpleEvents(){
    for(let i=0; i<registerEventPluginEvents.length; i++) {'
        const eventName = registerEventPluginEvents[i];
        const domEventName = eventName.toLowerCase();
        const capitalizedEvent = eventName.slice(0).toUpperCase()+domEventName.slice(1);
        registerSimleEvent(domEventName, on${capitalizedEvent});
    }
}

17.listenToNativeEvent

17.1 DOMEventPluginSystem.js

import { allNativeEvents } from './EventRegistry';
import * as SimpleEventPlugin from './SimpleEventPlugin';
+ import { IS_CAPTURE_PHASE } from './EventSystemFlags';
+ import {createEventListenerWrapperWithPriority} from './ReactDOMEventListener';
+ import { addEventCaptureListener, addEventBubbleListener } from './EventListener';

SimpleEventPlugin.registerEvents();
export function listenToAllSupported(rootContainerElement) {
    allNativeEvents.forEach(domEventName => {
+        listenToNativeEvent(domEventName, true, rootContainerElement)
+        listenToNativeEvent(domEventName, fale, rootContainerElement)
    })
}

+function listenToNativeEvent(domEventName, isCapturePhaseListener, target){
+    let eventSystemFlags = 0;
+    if(isCapturePhaseListener) {
+        eventSystemFlags |= IS_CAPTURE_PHASE
+    }
+    addTrappedEventListener(target, domEventName, eventSystemFlags, isCapturePhaseListener);
+}

+function addTrappedEventListener(targetContainer,domEventName, eventSystemFlags, isCapturePhaseListener) {
+    const listener = createEventListenerWrapperWithPriority(targetContainer, domEvent, eventSystemFlags);
+    if(isCapturePhaseListener) {
+        addEventCaptureListener(targetContainer, domEventName, listener)
+    }else {
+        addEventBubbleListener(targetContainer, domEventname, listener)
+    }
+}

17.2 EventSystemFlags.js

export const IS_CAPTURE_PHASE = 1 >> 2;

17.3 ReactDOMEventListener.js

export function createEventListenerWrapperWithPriority(targetContainer, domEventName, eventSystemFlags){
    const listenerWrapper = dispatchDiscreteEvent;
    listenerWrapper.bind(null, domEventName, eventSystemFlags, targetContainer);
}

function dispatchDiscreteEvent(domEventName, eventSystemFlags, container, nativeEvent) {
    dispatchEvent(domEventName, eventSystemFlags, container, nativeEvent)
}

function dispatchEvent(domEventName, eventSystemFlags, container, nativeEvent) {
    console.log(domEventName, eventSystemFlags, container, nativeEvent)
}

17.4 EventListener.js

export function addEventCaptureListener(target, eventType, listener) {
    target.addEventListener(eventType, listener, true);
    return listener;
}

export function addEventbubbleListener(target, eventType, listener) {
    target.addEventListener(eventType, listener, false);
    return listener;
}

18.extractsEvents

image.png

18.1 ReactDOMEventListener.js

+ import getEventTarget from './getEventTarget';
+ import { getClosestInstanceFromNode } from '../client/ReactDOMComponentTree';
+ import { dispatchEventForPluginEventSystem } from 'DOMPluginEventSystem';

export function createEventListenerWrapperWithPriority(targetContainer, domEventName, eventSystemFlags) {
    const listenerWrapper = dispatchDiscreteEvent(targetContainer, domEventName, eventSystemFlags);
    listenerWrapper.bind(null, domEventName, eventSystemFlags, targetContainer);
}

function dispatchDiscreteEvent(domEventName, eventSystemFlags, container, nativeEvent) {
    dispatchEvent(domEventName, eventSystemFlags, container, nativeEvent);
}

dispatchEvent(domEventName, eventSystemFlags, targetContainer, nativeEvent) {
+    const nativeEventTarget = getEventTarget(nativeEvent);
+    const targetIns = getClosestInstanceFromNode(nativeEventTarget);
+    dispatchEventForPluginEventSystem(
+        domEventName,
+        eventSystemFlags,
+        nativeEvent,
+        targetIns,
+        targetContainer
+    )
}

18.2 getEventTarget.js

function getEventTarget(nativeEvent){
    const target = nativeEvent.target || nativeEvent.srcElement || window;
    return target
}
export default getEventTarget;

18.3 ReactDOMComponentTree.js

const randomKey = Math.random().toString(36).slice(2);
const internalInstanceKey = "__reactFiber$" + randomKey;
const internalPropsKey = "__reactProps$" + randomKey;

export function getClosestInstanceFromNode(targetNode){
    const targetIns = targetNode[internalInstanceKey];
    if(targetIns) {
        return targetIns
    }
    return null;
}

export function getFiberCurrentPropsFromNode(node) {
  return node[internalPropsKey] || null;
}

/**
 * 提前缓存fiber节点的实例到DOM节点上
 * @param {*} hostInst
 * @param {*} node
 */
export function precacheFiberNode(hostInst, node) {
  node[internalInstanceKey] = hostInst;
}

export function updateFiberProps(node, props) {
  node[internalPropsKey] = props;
}

18.4 DOMPluginEventSystem.js

import { allNativeEvents } from "./EventRegistry";
import * as SimpleEventPlugin from "./plugins/SimpleEventPlugin";
import { createEventListenerWrapperWithPriority } from "./ReactDOMEventListener";
import { IS_CAPTURE_PHASE } from "./EventSystemFlags";
import { addEventCaptureListener, addEventBubbleListener } from "./EventListener";
+import getEventTarget from "./getEventTarget";
+import getListener from "./getListener";
+import { HostComponent } from "react-reconciler/src/ReactWorkTags";

SimpleEventPlugin.registerEvents();
export function listenToAllSupportedEvents(rootContainerElement){
    allnativeEvents.forEach(domEventName => {
        listenerToNativeEvent(domEventName, true, rootContainerElement)
        listenerToNativeEvent(domEventName, false, rootContainerElement)
    })
}
function listenerToNativeEvent(domEventName, isCapturePhaseListener, target) {
    let eventSystemFlags = 0;
    if(isCapturePhaseListener) {
        eventSystemFlags |= IS_CAPTURE_PHASE
    }
    addTrappedEventListener(target, domEventName, eventSystemFlags, isCapturePhaseListener)
}
function addTrappedEventListener(targetContainer, domEventName, eventSystemFlags, isCapturePhaseListener) {
    const listener = createEventListenerWrapperWithPriority(targetContainer, domEventName, eventSystemFlags);
    if(isCapturePhaseListener) {
        addEventCaptureListener(targetContainer, domEventName, listener)
    } else {
        addEventBubbleListener(targetContainer, domEventName, listener)
    }
}

+ export function dispatchEventForPluginEventSystem(domEventName, eventSystemFlags, nativeEvent, targetIns, targetContainer){
+    dispatchEventForPlugins(domEventName, eventSystemFlags, nativeEvent, targetIns, targetContainer)
+ }

+ function dispatchEventForPlugins(domEventName, eventSystemFlags, nativeEvent, targetIns, targetContainer){
    const nativeEventTarget = getEventTarget(natvieEvent);
    const dispatchQueue = [];
    
    extractEvents(dispatchQueue, domEventName, targetIns, nativeEvent, nativeEventTarget, eventSystemFlags, targetContainer);
    console.log('dispatchQueue', dispatchQueue)
+ }

+ function extractEvents(dispatchQueue, domEventName, targetIns, nativeEvent, nativeEventTarget, eventSystemFlags, targetContainer) {
+    SimpleEventPlugin.extractEvents(
+        dispatchQueue,
+        domEventName,
+        targetIns,
+        nativeEvent,
+        nativeEventTarget,
+        eventSystemFlags,
+        targetContainer
+    )
+ }

+export function accumulateSinglePhaseListeners(targetFiber, reactName, nativeEventType, inCapturePhase) {
+    const captureName = reactName + "Capture";
+    const reactEventName = inCapturePhase ? captureName ; reactName;
+    const listeners = [];
+    let instance = targetFiber;
+    while(instance !== null) {
+        const { stateNode, tag } = instance;
+        if(tag === HostComponent && stateNode !== null) {
+            if(reactEventName !== null) {
+                const listener = getListener(instance, reactName);
+                if(listener !== null && listener !== undefined) {
+                    listeners.push(createDispatchListener(instance, listener, stateNode))
+                }
+            }
+        }
+        instance = instance.return;
+    }
+    return listeners;
+}
+function creaetDispatchListener(instance, listener, currentTarget) {
+    return {
+        instance,
+        listener,
+        currentTarget,
+    }
+}

18.5 getListener.js

import { getFiberCurrentPropsFromNode } from "../client/ReactDOMComponentTree";

/**
 * 获取此fiber上对应的回调函数
 */
export default function getListener(inst, registrationName) {
  const stateNode = inst.stateNode;
  if (stateNode === null) {
    return null;
  }
  //从真实DOM节点上获取fiber当前的属性
  const props = getFiberCurrentPropsFromNode(stateNode);
  if (props === null) {
    return null;
  }
  const listener = props[registrationName];//props.onClick
  return listener;
}

18.6 SimpleEventPlugin.js

import { registerSimpleEvents, toLevelEventsToReactNames } from '../DOMEventProperties';
import { IS_CAPTURE_PHASE } from '../EventSystemFlags';
+import { SyntheticMouseEvent } from '../SyntheticEvent';
+import { accumulateSinglePhaseListener } from '../DOMPluginEventSystem';

+ function extractEvents(
+    dispatchQueue, 
+    domEventName, 
+    targetIns, 
+    nativeEvent, 
+    nativeEventTarget, //click => onClick
+    eventSystemFlags, 
+    targetContainer
+  ){
+    const reactName = toLevelEventsToReactNames.get(domEventName);
+    //合成事件构建函数
+    let SyntheticEventCtor;
+    switch(domEventName){
+        case 'click':
+            SyntheticEventCtor = SyntheticMouseEvent;
+            break;
+        default:
+            break;
+    }
+    //是否是捕获阶段
+    const inCapturePhase = (eventSystemFlags & IS_CAPTURE_PHASE) !== 0;
+    const listeners = accumulateSinglePhaseListener(targetIns, reactName, nativeEvent.type, inCapturePhase);
+    //如果有要执行的监听函数的话[onClickCapture, onClickCapture]=[ChildCapture, ParentCapture]
+    if(listeners.length > 0) {
+        const event = new SyntheticEventCtor(reactName, domEventName, targetIns, nativeEvent, nativeEventTarget);
+        dispatchQueue.push({
+            event, //合成事件实例
+            listeners, //监听函数数组
+        })
+    }
+ }
export { registerSimpleEvents as registerEvents, extractEvents }

18.7 SyntheticEvent.js

import assign from "shared.assign";

function functionThatReturnsTrue(){
    return true
}
function functionThatReturnsFalse(){
    return false
}
const MouseEventInterface = {
    clientX: 0,
    clientY: 0,
}

function createSyntheticEvent(Interface){
  /**
   *合成事件的基类
   * @param {*} reactName React属性名 onClick
   * @param {*} reactEventType click
   * @param {*} targetInst 事件源对应的fiber实例
   * @param {*} nativeEvent 原生事件对象
   * @param {*} nativeEventTarget 原生事件源 span 事件源对应的那个真实DOM
   */
    function SyntheticBaseEvent(reactName, reactEventType, targetInst, nativeEvent, nativeEventTarget){
        this._reactName = reactName;
        this.type = reactEventType;
        this._targetInst = targetInst;
        this.nativeEvent = nativeEvent;
        this.target = nativeEventTarget;
        
        //把此接口上对应的属性从原生事件上拷贝到合成事件实例上
        for(const propName in Interface) {
            if(!Interface.haseOwnProperty(propName)) {
                continue;
            }
            this[propName] = nativeEvent[propName];
        }
        
        //是否已经阻止默认事件
        this.isDefaultPrevented = functionThatReturnsFalse;
        //是否已经阻止继续传播
        this.isPropagationStopped = functionThatReturnsFalse;
        return this;
    }
    assign(SyntheticBaseEvent.prototype, {
        preventDefault() {
            const event = this.nativeEvent;
            if(event.preventDefault) {
                event.preventDefault();
            }else{
                event.returnValue = false;
            }
            this.isDefaultPrevented = functionThatReturnsTure;
        },
        stopPropagation() {
            const event = this.nativeEvent;
            if(event.stopPropagation) {
                event.stopPropagation();
            }else{
                event.cancleBubble = true;
            }
            this.isPropagationStopped = functionThatReturnsTure;
        }
    })
    
    return SyntheticBaseEvent;
}
export const syntheticMouseEvent = createSyntheticEvent(MouseEventInterface);

18.8 ReactDOMHostConfig.js

import { setInitialProperties } from "./ReactDOMComponent";
+import { precacheFiberNode, updateFiberProps } from "./ReactDOMComponentTree";

export function shouldSetTextContent(type, props) {
  return typeof props.children === "string" || typeof props.children === "number";
}
export const appendInitialChild = (parent, child) => {
  parent.appendChild(child);
};
export const createInstance = (type, props, internalInstanceHandle) => {
  const domElement = document.createElement(type);
+ precacheFiberNode(internalInstanceHandle, domElement);
+ updateFiberProps(domElement, props);
  return domElement;
};
export const createTextInstance = (content) => document.createTextNode(content);
export function finalizeInitialChildren(domElement, type, props) {
  setInitialProperties(domElement, type, props);
}
export function appendChild(parentInstance, child) {
  parentInstance.appendChild(child);
}
export function insertBefore(parentInstance, child, beforeChild) {
  parentInstance.insertBefore(child, beforeChild);
}

18.9 DOMEventProperties.js

import { registerTwoPhaseEvent } from "./EventRegistry";

+ export const toLevelEventsReactNames = new Map();
const simpleEventPluginEvents = ['click', ...];

function registerSimpleEvent(domEventName, reactName) {
  //onClick在哪里可以取到
  //workInProgress.pendingProps=React元素或者说虚拟DOM.props;
  //const newProps = workInProgress.pendingProps;
  //在源码里 让真实DOM元素 updateFiberProps(domElement, props);
  //const internalPropsKey = "__reactProps$" + randomKey;
  //真实DOM元素[internalPropskey] = props; props.onClick;
  
  //把原生事件名和处理函数的名字进行映射或者说绑定,click=>onClick;
+   toLevelEventsReactNames.set(domEventName, reactName);
    registerTwoPhaseEvent(reactName, [domEventName]);
}

export function registerSimpleEvents(){
    for(let i=0; i<simpleEventPluginEvents.length; i++) {
        const evnetName = simpleEventPluginEvents[i];
        const domEventName = eventName.toLowerCase();
        const capitalizedEvent = eventName[0].toUpperCase + eventName.slice(1);
        registerSimpleEvent(domEventName, on${capitalizedEvent})
    }
}

19.processDispatchQueue

19.1 DOMPluginEventSystem.js

import { allNativeEvents } from "./EventRegistry";
import * as SimpleEventPlugin from "./plugins/SimpleEventPlugin";
import { createEventListenerWrapperWithPriority } from "./ReactDOMEventListener";
import { IS_CAPTURE_PHASE } from "./EventSystemFlags";
import { addEventCaptureListener, addEventBubbleListener } from "./EventListener";
import getEventTarget from "./getEventTarget";
import getListener from "./getListener";
import { HostComponent } from "react-reconciler/src/ReactWorkTags";

SimpleEventPlugin.registerEvents();

export function listenToAllSupportedEvents(rootContainerElement) {
  allNativeEvents.forEach((domEventName) => {
    listenToNativeEvent(domEventName, true, rootContainerElement);
    listenToNativeEvent(domEventName, false, rootContainerElement);
  });
}

export function listenToNativeEvent(domEventName, isCapturePhaseListener, target) {
  let eventSystemFlags = 0; // 冒泡 = 0 捕获 = 4
  if (isCapturePhaseListener) {
    eventSystemFlags |= IS_CAPTURE_PHASE;
  }
  addTrappedEventListener(target, domEventName, eventSystemFlags, isCapturePhaseListener);
}
function addTrappedEventListener(targetContainer, domEventName, eventSystemFlags, isCapturePhaseListener) {
  const listener = createEventListenerWrapperWithPriority(targetContainer, domEventName, eventSystemFlags);
  if (isCapturePhaseListener) {
    addEventCaptureListener(targetContainer, domEventName, listener);
  } else {
    addEventBubbleListener(targetContainer, domEventName, listener);
  }
}
export function dispatchEventForPluginEventSystem(
  domEventName,
  eventSystemFlags,
  nativeEvent,
  targetInst,
  targetContainer
) {
  dispatchEventsForPlugins(domEventName, eventSystemFlags, nativeEvent, targetInst, targetContainer);
}

function dispatchEventsForPlugins(domEventName, eventSystemFlags, nativeEvent, targetInst, targetContainer) {
  const nativeEventTarget = getEventTarget(nativeEvent);
  const dispatchQueue = [];
  extractEvents(
    dispatchQueue,
    domEventName,
    targetInst,
    nativeEvent,
    nativeEventTarget,
    eventSystemFlags,
    targetContainer
  );
+ processDispatchQueue(dispatchQueue, eventSystemFlags);
}

+export function processDispatchQueue(dispatchQueue, eventSystemFlags) {
  //判断是否在捕获阶段
+  const inCapturePhase = (eventSystemFlags & IS_CAPTURE_PHASE) !== 0;
+  for (let i = 0; i < dispatchQueue.length; i++) {
+    const { event, listeners } = dispatchQueue[i];
+    processDispatchQueueItemsInOrder(event, listeners, inCapturePhase); //  event system doesn't use pooling.
+  }
+}
+function processDispatchQueueItemsInOrder(event, dispatchListeners, inCapturePhase) {
+  if (inCapturePhase) {//[dispatchListener[子,父]]
+    for (let i = dispatchListeners.length - 1; i >= 0; i--) {
+      const { currentTarget, listener } = dispatchListeners[i];
+      if (event.isPropagationStopped()) {
+        return;
+      }
+      executeDispatch(event, listener, currentTarget);
+    }
+  } else {
+    for (let i = 0; i < dispatchListeners.length; i++) {
+      const { currentTarget, listener } = dispatchListeners[i];
+      if (event.isPropagationStopped()) {
+        return;
+      }
+      executeDispatch(event, listener, currentTarget);
+    }
+  }
+}
+function executeDispatch(event, listener, currentTarget) {
  //合成事件实例currentTarget是在不断的变化的
  //event nativeEventTarget 它的是原始的事件源,是永远不变的
  //event currentTarget 当前的事件源,它是会随着事件回调的执行不断变化的
+  event.currentTarget = currentTarget;
+  listener(event);
+  event.currentTarget = null;
+}
function extractEvents(
  dispatchQueue,
  domEventName,
  targetInst,
  nativeEvent,
  nativeEventTarget,
  eventSystemFlags,
  targetContainer
) {
  SimpleEventPlugin.extractEvents(
    dispatchQueue,
    domEventName,
    targetInst,
    nativeEvent,
    nativeEventTarget,
    eventSystemFlags,
    targetContainer
  );
}

export function accumulateSinglePhaseListeners(targetFiber, reactName, nativeEventType, inCapturePhase) {
  const captureName = reactName + "Capture";
  const reactEventName = inCapturePhase ? captureName : reactName;
  const listeners = [];
  let instance = targetFiber;
  while (instance !== null) {
    const { stateNode, tag } = instance;
    if (tag === HostComponent && stateNode !== null) {
      if (reactEventName !== null) {
        const listener = getListener(instance, reactEventName);
        if (listener !== null && listener !== undefined) {
          listeners.push(createDispatchListener(instance, listener, stateNode));
        }
      }
    }
    instance = instance.return;
  }

  return listeners;
}
function createDispatchListener(instance, listener, currentTarget) {
  return {
    instance,
    listener,
    currentTarget,
  };
}

20.mountReducer

image.png

image.png

20.1 src/main.jsx

import * as React from 'react';
import createRoot from 'react-dom/client';

const reducer = (action, state) => {
    if(action.type === 'add') return state + 1;
    return state;
}
function FunctionComponent() {
    const [Number, setNumber] = React.useReducer(reducer, 0);
    return (
        <button onClick={ () => setNumber({type: "add"}) }>Number</button>
    )
}
let element = <FunctionComponet />;
const root = createRoot(document.getElementById('root'));
root.render(element);

20.2 react/index.js

import { 
        useReducer,
        __SELECT_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED 
       } from './src/React.js';

20.3 React.js

import { useReducer } from './ReactHooks';
import { ReactSharedInternals } from './ReactSharedInternals';

export { 
        useReducer,
        ReactSharedInternals as __SELECT_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED
       }

20.4 ReactFiberHooks.js

import ReactSharedInternals from 'shared/ReactSharedInternals';

const { ReactCurrentDispatcher } = ReactSharedInternals;

let currnetlyRenderingFiber = null;
let workInProgressHook = null;

const HooksDispatchOnMountInDev = {
    useReducer: mountReducer
}

function mountWorkInProgressHook() {
    const hook = {
        memoizedState: null,
        queue: null,
        next: null
    }
    if(workInProgressHook === null) {
       currentlyRenderingFiber.memoizedState = workInProgressHook = hook
    }else {
        workInProgress = workInProgressHook.next = hook
    }
    return workInProgress;
}

function dispatchReducerAction(fiber, queue, action) {
    console.log('dispatchReducerAction =>')
}

function mountReducer(reducer, initialArg, init) {
    const hook = mountWorkInProgressHook();
    hook.memomizedState = initialArg;
    const queue = {
        pendding: null,
        dispatch: null
    }
    hook.queue = queue;
    const dispatch = queue.dispath = dispatchReducerAction.bind(null, currentlyRenderingFiber, queue);
    return [hook.memomizedState, dispatch]
}

export function renderWithHooks(current, workInProgress, Compent, props) {
    currentlyRenderingFiber = workInProgress;
    if(current !== null && current.memoizedState !== null) {
    
    } esle {
        ReactCurrentDispatcher.current = HooksDispatchOnMountInDev;
    }
    const children = Componet(props);
    currentlyRenderingFiber = null;
    workInProgressHook = null;
    return children;
}

20.5 ReactHooks.js

import ReactCurrentDispatcher from './ReactCurentDispatcher';

function resolveDispatcher(){
    const dispatcher = ReactCurrentDispatcher.current;
    return dispatcher;
}

export function useReducer(reducer, initialArg, init) {
    const dispatcher = resolveDispatcher();
    return dispatcher.useReducer(reducer, initialArg, init);
}

20.6 ReactCurrentDispatcher.js

const ReactCurrentDispatcher = {
    current: null;
}
export default ReactCurrentDispatcher;

20.7 ReactSharedInternals.js

import ReactCurrentDispatcher from './ReactSharedInternals'; 

const ReactSharedInternals = {
    ReactCurrentDispatcher
}
export deafult ReactSharedInternals;

20.8 ReactSharedInternals.js

import * as React from 'react';
const ReactSharedInternals = React.__SELECT_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED;
export default ReactSharedInternals;

21.updateReducer

image.png

image.png

21.1 ReactFiberHooks.js

import ReactSharedInternals from 'shared/ReactSharedInternals';
import { enqueueConcurrentHookUpdate } from './ReactFiberConcurrentUpdates';
import { scheduleUpdateOnFiber } from './ReactFiberWorkLoop';

const { ReactCurrentDispatcher } = ReactSharedInternals;
let currentlyRenderingFiber = null;
let workInProgressHook = null;
let currentHook = null;

const HooksDispatcherOnMountInDEV = {
    useReducer: mountReducer
}
const HooksDispatcherOnUpdateInDEV = {
    useReducer: updateReducer
}

function mountWorkInProgressHook() {
    const hook = {
        memoizedState: null,
        queu: null,
        next: null
    }
    if(workInProgressHook === null) {
        currentlyRenderingFiber.memoizedState = workInProgressHook = hook
    }else {
        workInProgressHook = workInProgressHook.next = hook
    }
    return hook;
}

function updateWorkInProgressHook() {
    if(currentHook === null) {
        const current = currentlyRenderingFiber.alternal;
        currentHook = current.memoizedState;
    } esle {
        currentHook = currentHook.next
    }
    const newHook = {
        memoizedState: currentHook.memoizedState,
        queue: currentHook.queue,
        next: null
    }
    if(workInProgressHook === null) {
        currentlyRenderingFiber.memoizedState = workInProgressHook = newHook
    } else {
        workInprogressHook = workInProgressHook.next = newHook
    }
    return workInProgressHook;
}

function mountReducer(reducer, initialArg) {
    const hook = mountWorkInProgressHook();
    hook.memoizedState = initialArg;
    const queue = {
        pendding: null,
        dispatch: null
    }
    hook.queue = queue;
    const dispatch = queue.dispatch = dispatchReducerAction.bind(null, currentlyRenderingFiber, queue);
    return [hook.memoizedState, dispatch]
}

fucntion updateReducer(reducer) {
    const hook = updateWorkInProgressHook();
    const queue = hook.queue;
    queue.lastRenderedReducer = reducer;
    const current = currentHook;
    let newState = current.memoizedState;
    const penddingQueue = queue.pending;
    
    if(pendingQueue !== null) {
        queue.pending = null;
        const first = pendingQueue.next;
        let update = first;
        do {
            if(update.hasEagerState) {
                newState = update.eagerState
            } else {
                const { action } = update;
                newState = reducer(newState, action)
            }
            update = update.next
        } while(update !== null && update !== first)
    }
    hook.memoizedState = queue.lastRenderedState = newState;
    return [hook.memoizedState, queue.dispatch]
}

function dispatchReducerAction(fiber, queue, action) {
    const update = {
        action,
        next: null
    }
    const root = enqueueConcurrentHookUpdate(fiber, queue, update);
    scheduleUpdateOnFiber(root, fiber);
}

export function renderWithHook(curent, workInProgress, Component, props){
    currentlyRenderingFiber = workInProgress;
    if(current !== null && current.memoizedState !== null) {
        ReactCurrentDispatcher.current = HooksDispatcherOnUpdateInDEV
    } else {
        ReactCurrentDispatcher.current = HooksDispatcherOnMountInDEV
    }
    const children = Component(props);
    currentlyRenderingFiber = null;
    workInProgressHook = null;
    currentHook = null;
    return children;
}

21.2 ReactFiberConcurrentUpdates.js

import { HostRoot } from './ReactWorkTags'

let concurrentQueues = [];
let concurrentQueuesIndex = 0;

export function markUpdateLaneFromFiberToRoot(sourceFiber) {
    let node = sourceFiber;
    let parent = sourceFiber.return;
    while(parent !== null) {
        node = parent;
        parent = parent.return;
    }
    return node.tag === HostRoot ? node.stateNode : null
}

export function enqueueConcurrentHookUpdate(fiber, queue, update) {
    enqueueUpdate(fiber, queue, update);
    return getRootForUpdateFiber(fiber);
}

function enqueueUpdate(fiber, queue, update) {
    concurrentQueues[concurrentQueuesIndex++] = fiber;
    concurrentQueues[concurrentQueuesIndex++] = queue;
    concurrentQueues[concurrentQueuesIndex++] = update;
}

function getRootForUpdateFiber(sourceFiber) {
    let node = sourceFiber;
    let parent = sourceFiber.return;
    while(parent !== null) {
        node = parent;
        node = parent.reutrn;
    }
    return node.tag === HostRoot ? node.stateNode : null;
}

export function finishingQueueConcurrentUpdate() {
    let endIndex = concurrentQueuesIndex;
    concurrentQueuesIndex = 0;
  
    let i = 0;
    while(i < endIndex) {
        const fiber = concurrentQueues[i++];
        const queue = concurrentQueues[i++];
        const update = concurrentQueues[i++];
        if(queue !== null && update !== null){
            const pending = queue.pending;
            if(pending === null) {
                update.next = update
            } else {
                update.next = pending.next;
                pending.next = update;
            }
            queue.pending = update
        }
    }
}

21.3 ReactFiberWorkLoop.js

import { scheduleCallback } from "scheduler";
import { createWorkInProgress } from "./ReactFiber";
import { beginWork } from "./ReactFiberBeginWork";
import { completeWork } from "./ReactFiberCompleteWork";
import { MutationMask, NoFlags } from "./ReactFiberFlags";
import { commitMutationEffectsOnFiber } from "./ReactFiberCommitWork";
+import { finishQueueingConcurrentUpdates } from "./ReactFiberConcurrentUpdates";

let workInProgress = null;
export function scheduleUpdateOnFiber(root) {
  ensureRootIsScheduled(root);
}
function ensureRootIsScheduled(root) {
  scheduleCallback(performConcurrentWorkOnRoot.bind(null, root));
}
function performConcurrentWorkOnRoot(root) {
  renderRootSync(root);
  const finishedWork = root.current.alternate;
  root.finishedWork = finishedWork;
  commitRoot(root);
}
function commitRoot(root) {
  const { finishedWork } = root;
  const subtreeHasEffects = (finishedWork.subtreeFlags & MutationMask) !== NoFlags;
  const rootHasEffect = (finishedWork.flags & MutationMask) !== NoFlags;
  if (subtreeHasEffects || rootHasEffect) {
    commitMutationEffectsOnFiber(finishedWork, root);
  }
  root.current = finishedWork;
}
function prepareFreshStack(root) {
  workInProgress = createWorkInProgress(root.current, null);
  //完成队列的并发更新
+ finishQueueingConcurrentUpdates();
}
function renderRootSync(root) {
  prepareFreshStack(root);
  workLoopSync();
}

function workLoopSync() {
  while (workInProgress !== null) {
    performUnitOfWork(workInProgress);
  }
}
function performUnitOfWork(unitOfWork) {
  const current = unitOfWork.alternate;
  const next = beginWork(current, unitOfWork);
  unitOfWork.memoizedProps = unitOfWork.pendingProps;
  if (next === null) {
    completeUnitOfWork(unitOfWork);
  } else {
    workInProgress = next;
  }
}

function completeUnitOfWork(unitOfWork) {
  let completedWork = unitOfWork;
  do {
    const current = completedWork.alternate;
    const returnFiber = completedWork.return;
    completeWork(current, completedWork);
    const siblingFiber = completedWork.sibling;
    if (siblingFiber !== null) {
      workInProgress = siblingFiber;
      return;
    }
    completedWork = returnFiber;
    workInProgress = completedWork;
  } while (completedWork !== null);
}

21.4 ReactFiberBeginWork.js

21.5 ReactChildFiber.js

21.6 ReactFiberCompleteWork.js

21.7 ReactFiberFlags.js

21.8 ReactDOMHostConfig.js

21.9 ReactDOMComponent.js

22.commitUpdate

22.1 DOMPluginEventSystem.js

22.2 ReactFiberCompleteWork.js

22.3 ReactFiberCommitWork.js

22.4 ReactDOMHostConfig.js

22.5 ReactDOMComponent.js

23.useState