packages/fiber/beginWork.js
import { extend, typeNumber, isFn, gDSFP, gSBU } from 'react-core/util';
import { fiberizeChildren } from 'react-core/createElement';
import { AnuPortal } from 'react-core/createPortal';
import { Renderer } from 'react-core/createRenderer';
import { createInstance, UpdateQueue } from './createInstance';
import { Fiber } from './Fiber';
import {
PLACE,
ATTR,
HOOK,
CONTENT,
REF,
CALLBACK,
WORKING
} from './effectTag';
import {
guardCallback,
detachFiber,
pushError,
applyCallback
} from './ErrorBoundary';
import { resetCursor } from './dispatcher';
import { getInsertPoint, setInsertPoints } from './insertPoint';
/**
* 基于DFS遍历虚拟DOM树,初始化vnode为fiber,并产出组件实例或DOM节点
* 为instance/fiber添加context与parent, 并压入栈
* 使用再路过此节点时,再弹出栈
* 它需要对updateFail的情况进行优化
*/
export function reconcileDFS(fiber, info, deadline, ENOUGH_TIME) {
var topWork = fiber;
outerLoop: while (fiber) {
if (fiber.disposed || deadline.timeRemaining() <= ENOUGH_TIME) {
break;
}
let occurError;
if (fiber.tag < 3) {
let keepbook = Renderer.currentOwner;
try {
// 为了性能起见,constructor, render, cWM,cWRP, cWU, gDSFP, render
// getChildContext都可能 throw Exception,因此不逐一try catch
// 通过fiber.errorHook得知出错的方法
updateClassComponent(fiber, info); // unshift context
} catch (e) {
occurError = true;
pushError(fiber, fiber.errorHook, e);
}
Renderer.currentOwner = keepbook;
if (fiber.batching) {
delete fiber.updateFail;
delete fiber.batching;
}
} else {
updateHostComponent(fiber, info); // unshift parent
}
//如果没有阻断更新,没有出错
if (fiber.child && !fiber.updateFail && !occurError) {
fiber = fiber.child;
continue outerLoop;
}
let f = fiber;
while (f) {
let instance = f.stateNode;
if (f.tag > 3 || f.shiftContainer) {
if (f.shiftContainer) {
//元素节点与AnuPortal
delete f.shiftContainer;
info.containerStack.shift(); // shift parent
}
} else {
let updater = instance && instance.updater;
if (f.shiftContext) {
delete f.shiftContext;
info.contextStack.shift(); // shift context
}
if (f.hasMounted && instance[gSBU]) {
updater.snapshot = guardCallback(instance, gSBU, [
updater.prevProps,
updater.prevState
]);
}
}
if (f === topWork) {
break outerLoop;
}
if (f.sibling) {
fiber = f.sibling;
continue outerLoop;
}
f = f.return;
}
}
}
function updateHostComponent(fiber, info) {
const { props, tag, alternate: prev } = fiber;
if (!fiber.stateNode) {
fiber.parent = info.containerStack[0];
fiber.stateNode = Renderer.createElement(fiber);
}
const parent = fiber.parent;
fiber.forwardFiber = parent.insertPoint;
parent.insertPoint = fiber;
fiber.effectTag = PLACE;
if (tag === 5) {
// 元素节点
fiber.stateNode.insertPoint = null;
info.containerStack.unshift(fiber.stateNode);
fiber.shiftContainer = true;
fiber.effectTag *= ATTR;
if (fiber.ref) {
fiber.effectTag *= REF;
}
diffChildren(fiber, props.children);
} else {
if (!prev || prev.props !== props) {
fiber.effectTag *= CONTENT;
}
}
}
function mergeStates(fiber, nextProps) {
let instance = fiber.stateNode,
pendings = fiber.updateQueue.pendingStates,
n = pendings.length,
state = fiber.memoizedState || instance.state;
if (n === 0) {
return state;
}
let nextState = extend({}, state); // 每次都返回新的state
let fail = true;
for (let i = 0; i < n; i++) {
let pending = pendings[i];
if (pending) {
if (isFn(pending)) {
let a = pending.call(instance, nextState, nextProps);
if (!a) {
continue;
} else {
pending = a;
}
}
fail = false;
extend(nextState, pending);
}
}
if (fail) {
return state;
} else {
return (fiber.memoizedState = nextState);
}
}
export function updateClassComponent(fiber, info) {
let { type, stateNode: instance, props } = fiber;
let { contextStack, containerStack } = info;
let getContext = type.contextType;
let unmaskedContext = contextStack[0];
//如果这是React16.7的static ContextType
let isStaticContextType = isFn(type.contextType);
let newContext = isStaticContextType ? getContext(fiber): getMaskedContext(
instance,
type.contextTypes,
unmaskedContext
);
if (instance == null) {
fiber.parent = type === AnuPortal ? props.parent : containerStack[0];
instance = createInstance(fiber, newContext);
if (isStaticContextType){
getContext.subscribers.push(instance);
}
}
if (!isStaticContextType){
cacheContext(instance, unmaskedContext, newContext);
}
let isStateful = !instance.__isStateless;
instance._reactInternalFiber = fiber; //更新rIF
if (isStateful) {
//有狀态组件
let updateQueue = fiber.updateQueue;
delete fiber.updateFail;
if (fiber.hasMounted) {
applybeforeUpdateHooks(
fiber,
instance,
props,
newContext,
contextStack
);
} else {
applybeforeMountHooks(
fiber,
instance,
props
);
}
if (fiber.memoizedState) {
instance.state = fiber.memoizedState;
}
fiber.batching = updateQueue.batching;
let cbs = updateQueue.pendingCbs;
if (cbs.length) {
fiber.pendingCbs = cbs;
fiber.effectTag *= CALLBACK;
}
if (fiber.ref) {
fiber.effectTag *= REF;
}
} else if (type === AnuPortal) {
//无狀态组件中的传送门组件
containerStack.unshift(fiber.parent);
fiber.shiftContainer = true;
}
//存放它上面的所有context的并集
//instance.unmaskedContext = contextStack[0];
//设置新context, props, state
instance.context = newContext;
fiber.memoizedProps = instance.props = props;
fiber.memoizedState = instance.state;
if (instance.getChildContext) {
let context = instance.getChildContext();
context = Object.assign({}, unmaskedContext, context);
fiber.shiftContext = true;
contextStack.unshift(context);
}
if (fiber.parent && fiber.hasMounted && fiber.dirty) {
fiber.parent.insertPoint = getInsertPoint(fiber);
}
if (isStateful) {
//上面设置fiber.parent.insertPoint的if分支原来是放这里
if (fiber.updateFail) {
cloneChildren(fiber);
fiber._hydrating = false;
return;
}
delete fiber.dirty;
fiber.effectTag *= HOOK;
} else if (fiber.effectTag == 1){
fiber.effectTag = WORKING;
}
if (fiber.catchError) {
return;
}
Renderer.onBeforeRender(fiber);
fiber._hydrating = true;
Renderer.currentOwner = instance;
let rendered = applyCallback(instance, 'render', []);
resetCursor();
diffChildren(fiber, rendered);
Renderer.onAfterRender(fiber);
}
function applybeforeMountHooks(fiber, instance, newProps) {
fiber.setout = true;
if (instance.__useNewHooks) {
setStateByProps(fiber, newProps, instance.state);
} else {
callUnsafeHook(instance, 'componentWillMount', []);
}
delete fiber.setout;
mergeStates(fiber, newProps);
fiber.updateQueue = UpdateQueue();
}
function applybeforeUpdateHooks(
fiber,
instance,
newProps,
newContext,
contextStack
) {
const oldProps = fiber.memoizedProps;
const oldState = fiber.memoizedState;
let updater = instance.updater;
updater.prevProps = oldProps;
updater.prevState = oldState;
let propsChanged = oldProps !== newProps;
fiber.setout = true;
if (!instance.__useNewHooks) {
let contextChanged = instance.context !== newContext;
if (propsChanged || contextChanged) {
let prevState = instance.state;
callUnsafeHook(instance, 'componentWillReceiveProps', [
newProps,
newContext
]);
if (prevState !== instance.state) {
//模拟replaceState
fiber.memoizedState = instance.state;
}
}
}
let newState = (instance.state = oldState);
let updateQueue = fiber.updateQueue;
mergeStates(fiber, newProps);
newState = fiber.memoizedState;
setStateByProps(fiber, newProps, newState);
newState = fiber.memoizedState;
delete fiber.setout;
fiber._hydrating = true;
if (
!propsChanged &&
newState === oldState &&
contextStack.length == 1 &&
!updateQueue.isForced
) {
fiber.updateFail = true;
} else {
let args = [newProps, newState, newContext];
fiber.updateQueue = UpdateQueue();
if (
!updateQueue.isForced &&
!applyCallback(instance, 'shouldComponentUpdate', args)
) {
fiber.updateFail = true;
} else if (!instance.__useNewHooks) {
callUnsafeHook(instance, 'componentWillUpdate', args);
}
}
}
function callUnsafeHook(a, b, c) {
applyCallback(a, b, c);
applyCallback(a, 'UNSAFE_' + b, c);
}
function isSameNode(a, b) {
if (a.type === b.type && a.key === b.key) {
return true;
}
}
function setStateByProps(fiber, nextProps, prevState) {
fiber.errorHook = gDSFP;
let fn = fiber.type[gDSFP];
if (fn) {
let partialState = fn.call(null, nextProps, prevState);
if (typeNumber(partialState) === 8) {
fiber.memoizedState = Object.assign({}, prevState, partialState);
}
}
}
function cloneChildren(fiber) {
const prev = fiber.alternate;
if (prev && prev.child) {
let pc = prev.children;
let cc = (fiber.children = {});
fiber.child = prev.child;
fiber.lastChild = prev.lastChild;
for (let i in pc) {
let a = pc[i];
a.return = fiber; // 只改父引用不复制
cc[i] = a;
}
setInsertPoints(cc);
}
}
function cacheContext(instance, unmaskedContext, context) {
instance.__unmaskedContext = unmaskedContext;
instance.__maskedContext = context;
}
function getMaskedContext(instance, contextTypes, unmaskedContext) {
var noContext = !contextTypes;
if (instance){
if (noContext){
return instance.context;
}
var cachedUnmasked = instance.__unmaskedContext;
if (cachedUnmasked === unmaskedContext) {
return instance.__maskedContext;
}
}
let context = {};
if (noContext) {
return context;
}
for (let key in contextTypes) {
if (contextTypes.hasOwnProperty(key)) {
context[key] = unmaskedContext[key];
}
}
return context;
}
/**
* 转换vnode为fiber
* @param {Fiber} parentFiber
* @param {Any} children
*/
function diffChildren(parentFiber, children) {
let oldFibers = parentFiber.children; // 旧的
if (oldFibers) {
parentFiber.oldChildren = oldFibers;
} else {
oldFibers = {};
}
let newFibers = fiberizeChildren(children, parentFiber); // 新的
let effects = parentFiber.effects || (parentFiber.effects = []);
let matchFibers = new Object();
delete parentFiber.child;
for (let i in oldFibers) {
let newFiber = newFibers[i];
let oldFiber = oldFibers[i];
if (newFiber && newFiber.type === oldFiber.type) {
matchFibers[i] = oldFiber;
if (newFiber.key != null) {
oldFiber.key = newFiber.key;
}
continue;
}
detachFiber(oldFiber, effects);
}
let prevFiber,
index = 0;
for (let i in newFibers) {
let newFiber = newFibers[i];
let oldFiber = matchFibers[i];
let alternate = null;
if (oldFiber) {
if (isSameNode(oldFiber, newFiber)) {
//&& !oldFiber.disposed
alternate = new Fiber(oldFiber);
let oldRef = oldFiber.ref;
newFiber = extend(oldFiber, newFiber);
delete newFiber.disposed;
newFiber.alternate = alternate;
if (newFiber.ref && newFiber.deleteRef) {
delete newFiber.ref;
delete newFiber.deleteRef;
}
if (oldRef && oldRef !== newFiber.ref) {
// alternate.effectTag *= NULLREF;
effects.push(alternate);
}
if (newFiber.tag === 5) {
newFiber.lastProps = alternate.props;
}
} else {
detachFiber(oldFiber, effects);
}
// newFiber.effectTag = NOWORK;
} else {
newFiber = new Fiber(newFiber);
}
newFibers[i] = newFiber;
newFiber.index = index++;
newFiber.return = parentFiber;
if (prevFiber) {
prevFiber.sibling = newFiber;
newFiber.forward = prevFiber;
} else {
parentFiber.child = newFiber;
newFiber.forward = null;
}
prevFiber = newFiber;
}
parentFiber.lastChild = prevFiber;
if (prevFiber) {
prevFiber.sibling = null;
}
}