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
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
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
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
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
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
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
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
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
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
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);
}