前言:
仍是在 React源码解析之workLoop 中,有一段HostComponent
和HostText
的更新:javascript
case HostComponent:
//更新 DOM 标签
return updateHostComponent(current, workInProgress, renderExpirationTime);
case HostText:
//更新文本节点
return updateHostText(current, workInProgress);
复制代码
本文就解析下updateHostComponent()
和updateHostText()
方法html
1、updateHostComponent
做用:
更新DOM
标签java
源码:node
//更新 DOM 标签
function updateHostComponent(current, workInProgress, renderExpirationTime) {
//===暂时跳过 context
pushHostContext(workInProgress);
//判断可否复用服务端渲染的节点
if (current === null) {
tryToClaimNextHydratableInstance(workInProgress);
}
const type = workInProgress.type;
const nextProps = workInProgress.pendingProps;
const prevProps = current !== null ? current.memoizedProps : null;
let nextChildren = nextProps.children;
//判断该节点是不是文本节点
const isDirectTextChild = shouldSetTextContent(type, nextProps);
//若是是文本节点的话(即里面再也不嵌套其余类型的节点)
if (isDirectTextChild) {
// We special case a direct text child of a host node. This is a common
// case. We won't handle it as a reified child. We will instead handle
// this in the host environment that also have access to this prop. That
// avoids allocating another HostText fiber and traversing it.
//没必要渲染子节点,直接显示其文本便可
nextChildren = null;
}
//若是以前节点不为空且为文本节点,但如今更新为其余类型的节点的话
else if (prevProps !== null && shouldSetTextContent(type, prevProps)) {
// If we're switching from a direct text child to a normal child, or to
// empty, we need to schedule the text content to be reset.
//重置文本节点
workInProgress.effectTag |= ContentReset;
}
//只有 HostComponent 和 ClassComponent 有使用该方法
//由于只有这两个 Component 能拿到 DOM 实例
markRef(current, workInProgress);
// Check the host config to see if the children are offscreen/hidden.
//若是该节点上设置了 hidden 属性,而且是异步渲染(ConcurrentMode)的话,那么它将最后更新
//关于 ConcurrentMode 模式,请参考:https://zh-hans.reactjs.org/docs/concurrent-mode-intro.html
if (
workInProgress.mode & ConcurrentMode &&
renderExpirationTime !== Never &&
shouldDeprioritizeSubtree(type, nextProps)
) {
if (enableSchedulerTracing) {
markSpawnedWork(Never);
}
// Schedule this fiber to re-render at offscreen priority. Then bailout.
//优先级最低,即最后更新
workInProgress.expirationTime = workInProgress.childExpirationTime = Never;
return null;
}
//将 ReactElement 变成 fiber对象,并更新,生成对应 DOM 的实例,并挂载到真正的 DOM 节点上
reconcileChildren(
current,
workInProgress,
nextChildren,
renderExpirationTime,
);
return workInProgress.child;
}
复制代码
解析:
(1) context
相关的之后讲
(2) tryToClaimNextHydratableInstance()
方法的做用是判断可否复用服务端渲染的root
内部已有的DOM
节点
(3) shouldSetTextContent()
的做用是判断该节点是不是文本节点:react
//判断是不是文本节点
export function shouldSetTextContent(type: string, props: Props): boolean {
return (
type === 'textarea' ||
type === 'option' ||
type === 'noscript' ||
typeof props.children === 'string' ||
typeof props.children === 'number' ||
(typeof props.dangerouslySetInnerHTML === 'object' &&
props.dangerouslySetInnerHTML !== null &&
props.dangerouslySetInnerHTML.__html != null)
);
}
复制代码
type
应该表示html
里的标签,如<textarea>
、<option>
、noscript
props.children
指节点里的内容是不是字符串仍是数字dangerouslySetInnerHTML
即innerHTML
,里面内容也是字符串web
关于dangerouslySetInnerHTML
的介绍与使用,请参考:zh-hans.reactjs.org/docs/dom-el…app
也就是说,一旦shouldSetTextContent()
判断为true
,就肯定该节点为文本节点dom
(4) 若是isDirectTextChild
为true
,则表示其内部是文本,故直接渲染便可,nextChildren
置为null
,后面讲到的updateHostText()
的源码也是相似的异步
(5) 若是以前节点不为空且为文本节点,但如今更新为其余类型的节点的话,则设一个ContentReset
的标签oop
(6) markRef
的做用是标记ref
只有HostComponent
和ClassComponent
有使用该方法,由于只有这两个Component
能直接获取到DOM
实例的引用:
//标记 ref
function markRef(current: Fiber | null, workInProgress: Fiber) {
const ref = workInProgress.ref;
if (
(current === null && ref !== null) ||
(current !== null && current.ref !== ref)
) {
// Schedule a Ref effect
workInProgress.effectTag |= Ref;
}
}
复制代码
若是是第一次渲染而且设置了 ref 引用的话,或者不是第一次渲染,可是 ref 的引用发生变化的话,则设置Ref
标签
(7) 若是设置了ConcurrentMode
模式,而且渲染的优先级不是最低的Never
的话,则将该节点的更新优先级重置为最低优先级Never
,return null
则表示不更新
ConcurrentMode
模式,个人理解是异步渲染 UI(随时暂停,随时切换),应该是 React 17 会发布到稳定版的新特性,对此模式感兴趣的同窗,请参考:zh-hans.reactjs.org/docs/concur…
(8) 若是 (7) 条件不成立的话,则往下执行reconcileChildren()
,将 ReactElement 变成 fiber对象,并更新,生成对应 DOM 的实例,并挂载到真正的 DOM 节点上
关于reconcileChildren()
的讲解,请参考:React源码解析之FunctionComponent(上)
2、updateHostText
做用:
更新 host 文本节点
源码:
//更新 host 文本节点
function updateHostText(current, workInProgress) {
if (current === null) {
tryToClaimNextHydratableInstance(workInProgress);
}
// Nothing to do here. This is terminal. We'll do the completion step
// immediately after.
//没有对 DOM 进行操做的地方,直接渲染出来便可
return null;
}
复制代码
解析:
跟1、updateHostComponent
中的(4)
类似,文本节点直接渲染出来便可。
(完)