各种进阶资源 + 联系方式请看下方链接
资源
/**
* 渲染入口
* element 要进行渲染的 ReactElement
* container 渲染容器
* callback 渲染完成后执行的回调函数
*/
export function render(
element: React$Element<any>,
container: Container,
callback: ?Function,
) {
// 检测 container 是否是符合要求的渲染容器
// 即检测 container 是否是真实的DOM对象
// 如果不符合要求就报错
invariant(
isValidContainer(container),
'Target container is not a DOM element.',
);
// 在开发环境下
if (__DEV__) {
// 检测 container 是否已经传递给 ReactDOM.createRoot() 方法
// 如果已经传递, 则 container 不能再传递给 render 方法
// 防止 render 方法和 createRoot 方法重复调用
const isModernRoot =
isContainerMarkedAsRoot(container) &&
container._reactRootContainer === undefined;
// 如果 container 已经传递给过 createRoot 方法
if (isModernRoot) {
// 在控制台报错
console.error(
'You are calling ReactDOM.render() on a container that was previously ' +
'passed to ReactDOM.createRoot(). This is not supported. ' +
'Did you mean to call root.render(element)?',
);
}
}
return legacyRenderSubtreeIntoContainer(
// 父组件 初始渲染没有父组件 传递 null 占位
null,
// 渲染的react元素
element,
// 渲染的目标容器 也就是把element渲染到哪去
container,
// 是否为服务器端渲染 false 不是服务器端渲染 true 是服务器端渲染,如果是服务器端渲染
// 我们可以复用container
false,
callback,
);
}
/**
* 判断 node 是否是符合要求的 DOM 节点
* 1. node 可以是元素节点
* 2. node 可以是 document 节点
* 3. node 可以是 文档碎片节点
* 4. node 可以是注释节点但注释内容必须是 react-mount-point-unstable
* react 内部会找到注释节点的父级 通过调用父级元素的 insertBefore 方法,
* 将 element 插入到注释节点的前面
*/
export function isValidContainer(node: mixed): boolean {
return !!(
node &&
(node.nodeType === ELEMENT_NODE ||
node.nodeType === DOCUMENT_NODE ||
node.nodeType === DOCUMENT_FRAGMENT_NODE ||
(node.nodeType === COMMENT_NODE &&
(node: any).nodeValue === ' react-mount-point-unstable '))
);
}
/**
* 将子树渲染到容器中 (初始化 Fiber 数据结构: 创建 fiberRoot 及 rootFiber)
* parentComponent: 父组件, 初始渲染传入了 null
* children: render 方法中的第一个参数, 要渲染的 ReactElement
* container: 渲染容器
* forceHydrate: true 为服务端渲染, false 为客户端渲染
* callback: 组件渲染完成后需要执行的回调函数
**/
function legacyRenderSubtreeIntoContainer(
parentComponent: ?React$Component<any, any>,
children: ReactNodeList,
container: Container,
forceHydrate: boolean,
callback: ?Function,
) {
/**
* 检测 container 是否已经是初始化过的渲染容器
* react 在初始渲染时会为最外层容器添加 _reactRootContainer 属性
* react 会根据此属性进行不同的渲染方式
* root 不存在 表示初始渲染
* root 存在 表示更新
*/
// 获取 container 容器对象下是否有 _reactRootContainer 属性
let root: RootType = (container._reactRootContainer: any);
// 即将存储根 Fiber 对象
let fiberRoot;
if (!root) {
// 初始渲染
// 初始化根 Fiber 数据结构
// 为 container 容器添加 _reactRootContainer 属性
// 在 _reactRootContainer 对象中有一个属性叫做 _internalRoot
// _internalRoot 属性值即为 FiberRoot 表示根节点 Fiber 数据结构
// legacyCreateRootFromDOMContainer
// createLegacyRoot
// new ReactDOMBlockingRoot -> this._internalRoot
// createRootImpl
root = container._reactRootContainer = legacyCreateRootFromDOMContainer(
container,
forceHydrate,
);
// 获取 Fiber Root 对象
fiberRoot = root._internalRoot;
/**
* 改变 callback 函数中的 this 指向
* 使其指向 render 方法第一个参数的真实 DOM 对象
*/
// 如果 callback 参数是函数类型
if (typeof callback === 'function') {
// 使用 originalCallback 存储 callback 函数
const originalCallback = callback;
// 为 callback 参数重新赋值
callback = function () {
// 获取 render 方法第一个参数的真实 DOM 对象
// 实际上就是 id="root" 的 div 的子元素
// rootFiber.child.stateNode
// rootFiber 就是 id="root" 的 div
const instance = getPublicRootInstance(fiberRoot);
// 调用 callback 函数并改变函数内部 this 指向
originalCallback.call(instance);
};
}
// 初始化渲染不执行批量更新
// 因为批量更新是异步的是可以被打断的, 但是初始化渲染应该尽快完成不能被打断
// 所以不执行批量更新
unbatchedUpdates(() => {
updateContainer(children, fiberRoot, parentComponent, callback);
});
} else {
// 非初始化渲染 即更新
fiberRoot = root._internalRoot;
if (typeof callback === 'function') {
const originalCallback = callback;
callback = function () {
const instance = getPublicRootInstance(fiberRoot);
originalCallback.call(instance);
};
}
// Update
updateContainer(children, fiberRoot, parentComponent, callback);
}
// 返回 render 方法第一个参数的真实 DOM 对象作为 render 方法的返回值
// 就是说渲染谁 返回谁的真实 DOM 对象
return getPublicRootInstance(fiberRoot);
}
/**
* 判断是否为服务器端渲染 如果不是服务器端渲染
* 清空 container 容器中的节点
*/
function legacyCreateRootFromDOMContainer(
container: Container,
forceHydrate: boolean,
): RootType {
// container =>
// 检测是否为服务器端渲染
const shouldHydrate =
forceHydrate || shouldHydrateDueToLegacyHeuristic(container);
// 如果不是服务器端渲染
if (!shouldHydrate) {
let rootSibling;
// 开启循环 删除 container 容器中的节点
while ((rootSibling = container.lastChild)) {
// 删除 container 容器中的节点
container.removeChild(rootSibling);
/**
* 为什么要清除 container 中的元素 ?
* 为提供首屏加载的用户体验, 有时需要在 container 中放置一些占位图或者 loading 图
* 就无可避免的要向 container 中加入 html 标记.
* 在将 ReactElement 渲染到 container 之前, 必然要先清空 container
* 因为占位图和 ReactElement 不能同时显示
*
* 在加入占位代码时, 最好只有一个父级元素, 可以减少内部代码的循环次数以提高性能
*
* placement
*
placement
*
placement
*
*/
}
}
return createLegacyRoot(
container,
shouldHydrate
? {
hydrate: true,
}
: undefined,
);
}
/**
* 通过实例化 ReactDOMBlockingRoot 类创建 LegacyRoot
*/
export function createLegacyRoot(
container: Container,
options?: RootOptions,
): RootType {
// container =>
// LegacyRoot 常量, 值为 0,
// 通过 render 方法创建的 container 就是 LegacyRoot
return new ReactDOMBlockingRoot(container, LegacyRoot, options);
}
/**
* 类, 通过它可以创建 LegacyRoot 的 Fiber 数据结构
*/
function ReactDOMBlockingRoot(
container: Container,
tag: RootTag,
options: void | RootOptions,
) {
// tag => 0 => legacyRoot
// container =>
// container._reactRootContainer = {_internalRoot: {}}
this._internalRoot = createRootImpl(container, tag, options);
}
function createRootImpl(
container: Container,
tag: RootTag,
options: void | RootOptions,
) {
// container =>
// tag => 0
// options => undefined
const root = createContainer(container, tag, hydrate, hydrationCallbacks);
markContainerAsRoot(root.current, container);
return root;
}
// 创建 container
export function createContainer(
containerInfo: Container,
tag: RootTag,
hydrate: boolean,
hydrationCallbacks: null | SuspenseHydrationCallbacks,
): OpaqueRoot {
// containerInfo =>
// tag: 0
// hydrate: false
// hydrationCallbacks: null
// 忽略了和服务器端渲染相关的内容
return createFiberRoot(containerInfo, tag, hydrate, hydrationCallbacks);
}
// 创建根节点对应的 fiber 对象
export function createFiberRoot(
containerInfo: any,
tag: RootTag,
hydrate: boolean,
hydrationCallbacks: null | SuspenseHydrationCallbacks,
): FiberRoot {
// 创建 FiberRoot
const root: FiberRoot = (new FiberRootNode(containerInfo, tag, hydrate): any);
// 创建根节点对应的 rootFiber
const uninitializedFiber = createHostRootFiber(tag);
// 为 fiberRoot 添加 current 属性 值为 rootFiber
root.current = uninitializedFiber;
// 为 rootFiber 添加 stateNode 属性 值为 fiberRoot
uninitializedFiber.stateNode = root;
// 为 fiber 对象添加 updateQueue 属性, 初始化 updateQueue 对象
// updateQueue 用于存放 Update 对象
// Update 对象用于记录组件状态的改变
initializeUpdateQueue(uninitializedFiber);
// 返回 root
return root;
}
function FiberRootNode(containerInfo, tag, hydrate) {
this.tag = tag;
this.current = null;
this.containerInfo = containerInfo;
this.pendingChildren = null;
this.pingCache = null;
this.finishedExpirationTime = NoWork;
this.finishedWork = null;
this.timeoutHandle = noTimeout;
this.context = null;
this.pendingContext = null;
this.hydrate = hydrate;
this.callbackNode = null;
this.callbackPriority = NoPriority;
this.firstPendingTime = NoWork;
this.firstSuspendedTime = NoWork;
this.lastSuspendedTime = NoWork;
this.nextKnownPendingLevel = NoWork;
this.lastPingedTime = NoWork;
this.lastExpiredTime = NoWork;
if (enableSchedulerTracing) {
this.interactionThreadID = unstable_getThreadID();
this.memoizedInteractions = new Set();
this.pendingInteractionMap = new Map();
}
if (enableSuspenseCallback) {
this.hydrationCallbacks = null;
}
}
export function initializeUpdateQueue<State>(fiber: Fiber): void {
const queue: UpdateQueue<State> = {
baseState: fiber.memoizedState,
baseQueue: null,
shared: {
pending: null,
},
effects: null,
};
fiber.updateQueue = queue;
}
/**
* 获取 container 的第一个子元素的实例对象
*/
export function getPublicRootInstance(
// FiberRoot
container: OpaqueRoot,
): React$Component<any, any> | PublicInstance | null {
// 获取 rootFiber
const containerFiber = container.current;
// 如果 rootFiber 没有子元素
// 指的就是 id="root" 的 div 没有子元素
if (!containerFiber.child) {
// 返回 null
return null;
}
// 匹配子元素的类型
switch (containerFiber.child.tag) {
// 普通
case HostComponent:
return getPublicInstance(containerFiber.child.stateNode);
default:
// 返回子元素的真实 DOM 对象
return containerFiber.child.stateNode;
}
}
export function getPublicInstance(instance: Instance): * {
return instance;
}
/**
* 计算任务的过期时间
* 再根据任务过期时间创建 Update 任务
*/
export function updateContainer(
// element 要渲染的 ReactElement
element: ReactNodeList,
// container Fiber Root 对象
container: OpaqueRoot,
// parentComponent 父组件 初始渲染为 null
parentComponent: ?React$Component<any, any>,
// ReactElement 渲染完成执行的回调函数
callback: ?Function,
): ExpirationTime {
// container 获取 rootFiber
const current = container.current;
// 获取当前距离 react 应用初始化的时间 1073741805
const currentTime = requestCurrentTimeForUpdate();
// 异步加载设置
const suspenseConfig = requestCurrentSuspenseConfig();
// 计算过期时间
// 为防止任务因为优先级的原因一直被打断而未能执行
// react 会设置一个过期时间, 当时间到了过期时间的时候
// 如果任务还未执行的话, react 将会强制执行该任务
// 初始化渲染时, 任务同步执行不涉及被打断的问题 1073741823
const expirationTime = computeExpirationForFiber(
currentTime,
current,
suspenseConfig,
);
// 设置FiberRoot.context, 首次执行返回一个emptyContext, 是一个 {}
const context = getContextForSubtree(parentComponent);
// 初始渲染时 Fiber Root 对象中的 context 属性值为 null
// 所以会进入到 if 中
if (container.context === null) {
// 初始渲染时将 context 属性值设置为 {}
container.context = context;
} else {
container.pendingContext = context;
}
// 创建一个待执行任务
const update = createUpdate(expirationTime, suspenseConfig);
// 将要更新的内容挂载到更新对象中的 payload 中
// 将要更新的组件存储在 payload 对象中, 方便后期获取
update.payload = {element};
// 判断 callback 是否存在
callback = callback === undefined ? null : callback;
// 如果 callback 存在
if (callback !== null) {
// 将 callback 挂载到 update 对象中
// 其实就是一层层传递 方便 ReactElement 元素渲染完成调用
// 回调函数执行完成后会被清除 可以在代码的后面加上 return 进行验证
update.callback = callback;
}
// 将 update 对象加入到当前 Fiber 的更新队列当中 (updateQueue)
enqueueUpdate(current, update);
// 调度和更新 current 对象
scheduleWork(current, expirationTime);
// 返回过期时间
return expirationTime;
}
// 将任务(Update)存放于任务队列(updateQueue)中
// 创建单向链表结构存放 update, next 用来串联 update
export function enqueueUpdate<State>(fiber: Fiber, update: Update<State>) {
// 获取当前 Fiber 的 更新队列
const updateQueue = fiber.updateQueue;
// 如果更新队列不存在 就返回 null
if (updateQueue === null) {
// 仅发生在 fiber 已经被卸载
return;
}
// 获取待执行的 Update 任务
// 初始渲染时没有待执行的任务
const sharedQueue = updateQueue.shared;
const pending = sharedQueue.pending;
// 如果没有待执行的 Update 任务
if (pending === null) {
// 这是第一次更新, 创建一个循环列表.
update.next = update;
} else {
update.next = pending.next;
pending.next = update;
}
// 将 Update 任务存储在 pending 属性中
sharedQueue.pending = update;
}
/**
* 判断任务是否为同步 调用同步任务入口
*/
export function scheduleUpdateOnFiber(
// rootFiber
fiber: Fiber,
expirationTime: ExpirationTime,
) {
/**
* fiber: 初始化渲染时为 rootFiber, 即 对应的 Fiber 对象
* expirationTime: 任务过期时间 =>1073741823
*/
/**
* 判断是否是无限循环的 update 如果是就报错
* 在 componentWillUpdate 或者 componentDidUpdate 生命周期函数中重复调用
* setState 方法时, 可能会发生这种情况, React 限制了嵌套更新的数量以防止无限循环
* 限制的嵌套更新数量为 50, 可通过 NESTED_UPDATE_LIMIT 全局变量获取
*/
checkForNestedUpdates();
// 判断任务是否是同步任务 Sync的值为: 1073741823
if (expirationTime === Sync) {
if (
// 检查是否处于非批量更新模式
(executionContext & LegacyUnbatchedContext) !== NoContext &&
// 检查是否没有处于正在进行渲染的任务
(executionContext & (RenderContext | CommitContext)) === NoContext
) {
// 同步任务入口点
performSyncWorkOnRoot(root);
}
// 忽略了一些初始化渲染不会得到执行的代码
}