completeWork方法具体做了哪些事呢? 对于HostComponent和HostText(也就是dom节点和文字节点),执行创建节点更新节点等,对于其他的component, 比如 class Component ,pop context(因为在调和阶段push了相关的context)
completeWork部分代码
typescript
// 原生的DOM节点
case HostComponent: {
// 拿到的就是ReactDOM.render的第二个参数dom节点
const rootContainerInstance = getRootHostContainer();
const type = workInProgress.type;
if (current !== null && workInProgress.stateNode != null) {
// 更新
updateHostComponent(
current,
workInProgress,
type,
newProps,
rootContainerInstance,
);
if (current.ref !== workInProgress.ref) {
markRef(workInProgress);
}
} else {
const currentHostContext = getHostContext();
let wasHydrated = popHydrationState(workInProgress);
if (wasHydrated) {
// ...
} else {
// 创建DOM节点
let instance = createInstance(
type,
newProps,
rootContainerInstance,
currentHostContext,
workInProgress,
);
// 构建DOM树
appendAllChildren(instance, workInProgress, false, false);
if (
finalizeInitialChildren(
instance,
type,
newProps,
rootContainerInstance,
currentHostContext,
)
) {
markUpdate(workInProgress);
}
workInProgress.stateNode = instance;
}
}
break;
}
case HostText: {
let newText = newProps;
if (current && workInProgress.stateNode != null) {
const oldText = current.memoizedProps;
updateHostText(current, workInProgress, oldText, newText);
} else {
const rootContainerInstance = getRootHostContainer();
const currentHostContext = getHostContext();
let wasHydrated = popHydrationState(workInProgress);
if (wasHydrated) {
// ...
} else {
workInProgress.stateNode = createTextInstance(
newText,
rootContainerInstance,
currentHostContext,
workInProgress,
);
}
}
break;
}createInstance
typescript
export function createInstance(
type: string,
props: Props,
rootContainerInstance: Container,
hostContext: HostContext,
internalInstanceHandle: Object,
): Instance {
let parentNamespace: string;
if (__DEV__) {
// ...
} else {
parentNamespace = ((hostContext: any): HostContextProd);
}
// 创建DOM节点
const domElement: Instance = createElement(
type,
props,
rootContainerInstance,
parentNamespace,
);
// 将当前Fiber节点挂在到DOM节点上
precacheFiberNode(internalInstanceHandle, domElement);
// 将当前的props挂在到DOM节点上
updateFiberProps(domElement, props);
return domElement;
}createElement
创建dom节点利用的document.createElement方法
typescript
export function createElement(
type: string,
props: Object,
rootContainerElement: Element | Document,
parentNamespace: string,
): Element {
let isCustomComponentTag;
// 当前的document对象
const ownerDocument: Document = getOwnerDocumentFromRootContainer(
rootContainerElement,
);
let domElement: Element;
let namespaceURI = parentNamespace;
if (namespaceURI === HTML_NAMESPACE) {
namespaceURI = getIntrinsicNamespace(type);
}
if (namespaceURI === HTML_NAMESPACE) {
if (type === 'script') {
const div = ownerDocument.createElement('div');
div.innerHTML = '<script><' + '/script>';
const firstChild = ((div.firstChild: any): HTMLScriptElement);
domElement = div.removeChild(firstChild);
} else if (typeof props.is === 'string') {
domElement = ownerDocument.createElement(type, {is: props.is});
} else {
domElement = ownerDocument.createElement(type);
if (type === 'select') {
const node = ((domElement: any): HTMLSelectElement);
if (props.multiple) {
node.multiple = true;
} else if (props.size) {
node.size = props.size;
}
}
}
} else {
domElement = ownerDocument.createElementNS(namespaceURI, type);
}
return domElement;
}appendAllChildren
当前方法做了什么呢?当走到这里的时候,说明当前节点的子节点的dom节点都创建了, 然后将子节点的dom节点都append到当前节点里,做一个DOM树的构建
typescript
appendAllChildren = function(
parent: Instance,
workInProgress: Fiber,
needsVisibilityToggle: boolean,
isHidden: boolean,
) {
let node = workInProgress.child;
while (node !== null) {
if (node.tag === HostComponent || node.tag === HostText) {
// 将子节点的dom节点appendChild到父节点里面 并不会对嵌套的child进行append操作
// 只会append直接子节点
appendInitialChild(parent, node.stateNode);
} else if (node.tag === HostPortal) {
// If we have a portal child, then we don't want to traverse
// down its children. Instead, we'll get insertions from each child in
// the portal directly.
} else if (node.child !== null) {
// 如果当前node不是HostComponent也不是HostText 并且存在child
// 那么就去查找当前子节点 因为Fiber节点是存在比如class组件的
// 但是DOM树需要挂载的是真实的DOM, 所以要去子节点里面找真实的DOM
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;
}
// 总结:这个方法的意思是什么呢?
// 其实就是构建真实DOM的过程,将当前parent节点的直接真实的子DOM节点找到,
// appendChild 构建DOM树
};finalizeInitialChildren
typescript
export function finalizeInitialChildren(
domElement: Instance,
type: string,
props: Props,
rootContainerInstance: Container,
hostContext: HostContext,
): boolean {
// 初始化事件监听,以及child,style,children等属性
setInitialProperties(domElement, type, props, rootContainerInstance);
return shouldAutoFocusHostComponent(type, props);
}setInitialProperties
当前方法代码量比较多,具体做了什么事呢 ?
- 针对事件prop,做一个事件监听
- 设置props接受的styles
- 设置textContent(文本子节点)
updateHostComponent
更新DOM的方法
- 如果前后dom接受的props没变, 就不需要做任何处理
- 否则,需要针对dom的属性,包括styles做一个对比,然后变更
typescript
updateHostComponent = function(
current: Fiber,
workInProgress: Fiber,
type: Type,
newProps: Props,
rootContainerInstance: Container,
) {
const oldProps = current.memoizedProps;
if (oldProps === newProps) {
return;
}
const instance: Instance = workInProgress.stateNode;
const currentHostContext = getHostContext();
// 对比dom的属性的变化,其中包括styles等等
const updatePayload = prepareUpdate(
instance,
type,
oldProps,
newProps,
rootContainerInstance,
currentHostContext,
);
workInProgress.updateQueue = (updatePayload: any);
if (updatePayload) {
markUpdate(workInProgress);
}
};prepareUpdate
typescript
export function prepareUpdate(
domElement: Instance,
type: string,
oldProps: Props,
newProps: Props,
rootContainerInstance: Container,
hostContext: HostContext,
): null | Array<mixed> {
return diffProperties(
domElement,
type,
oldProps,
newProps,
rootContainerInstance,
);
}diffProperties
- 首先对比新老props
- 收集变更的属性返回 然后将需要变动的属性添加到当前节点的Fiber的updateQueue上,在commit阶段再去做一个真实的变更操作
updateHostText
创建文本节点,对比文本是否一致,然后更新他