You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
functioncreatePortal(children: ReactNodeList,container: DOMContainer,key: ?string=null,){invariant(isValidContainer(container),'Target container is not a DOM element.',);// TODO: pass ReactDOM portal implementation as third argumentreturnReactPortal.createPortal(children,container,null,key);}functioncreatePortal(children: ReactNodeList,containerInfo: any,// TODO: figure out the API for cross-renderer implementation.implementation: any,key: ?string=null,): ReactPortal{return{// This tag allow us to uniquely identify this as a React Portal$$typeof: REACT_PORTAL_TYPE,key: key==null ? null : ''+key,
children,
containerInfo,
implementation,};}
调和子节点中为 portal 类型组件创建 fiber 节点
// reconcileChildFibersfunctionreconcileChildFibers(returnFiber: Fiber,currentFirstChild: Fiber|null,newChild: any,expirationTime: ExpirationTime,): Fiber|null{// ....constisObject=typeofnewChild==='object'&&newChild!==null;if(isObject){switch(newChild.$$typeof){caseREACT_ELEMENT_TYPE:
returnplaceSingleChild(reconcileSingleElement(returnFiber,currentFirstChild,newChild,expirationTime,),);caseREACT_PORTAL_TYPE:
returnplaceSingleChild(reconcileSinglePortal(returnFiber,currentFirstChild,newChild,expirationTime,),);}}// ....}// reconcileSinglePortalfunctionreconcileSinglePortal(returnFiber: Fiber,currentFirstChild: Fiber|null,portal: ReactPortal,expirationTime: ExpirationTime,): Fiber{constkey=portal.key;// 原有的子节点letchild=currentFirstChild;while(child!==null){// TODO: If key === null and child.key === null, then this only applies to// the first item in the list.if(child.key===key){if(child.tag===HostPortal&&// 原有子节点的 container 与新的 portal<createPortal返回的那个对象> 相同,说明可复用child.stateNode.containerInfo===portal.containerInfo&&child.stateNode.implementation===portal.implementation){// 删除兄弟节点deleteRemainingChildren(returnFiber,child.sibling);// 复用当前节点constexisting=useFiber(child,portal.children||[],expirationTime,);existing.return=returnFiber;returnexisting;}else{// 不能复用删除所有deleteRemainingChildren(returnFiber,child);break;}}else{// key 不相同 删除所有deleteChild(returnFiber,child);}// 循环找知道找到可复用的节点为止child=child.sibling;}// 如果没有可以复用的,那么要创建新的 fiberconstcreated=createFiberFromPortal(portal,returnFiber.mode,expirationTime,);created.return=returnFiber;returncreated;}
case HostPortal:
returnupdatePortalComponent(current,workInProgress,renderExpirationTime,);functionupdatePortalComponent(current: Fiber|null,workInProgress: Fiber,renderExpirationTime: ExpirationTime,){// 有单独的挂载点,container 需要 pushpushHostContainer(workInProgress,workInProgress.stateNode.containerInfo);// 获取新的 childrenconstnextChildren=workInProgress.pendingProps;// 首次调用if(current===null){// Portals are special because we don't append the children during mount// but at commit. Therefore we need to track insertions which the normal// flow doesn't do during mount. This doesn't happen at the root because// the root always starts with a "current" with a null child.// TODO: Consider unifying this with how the root works.workInProgress.child=reconcileChildFibers(workInProgress,null,nextChildren,renderExpirationTime,);}else{// 相当于 mountChildFibers// 更新渲染reconcileChildren(current,workInProgress,nextChildren,renderExpirationTime,);}returnworkInProgress.child;}
functionforwardRef<Props,ElementType: React$ElementType>(render: (props: Props,ref: React$Ref<ElementType>)=>React$Node,){if(__DEV__){if(typeofrender!=='function'){warningWithoutStack(false,'forwardRef requires a render function but was given %s.',render===null ? 'null' : typeofrender,);}else{warningWithoutStack(// Do not warn for 0 arguments because it could be due to usage of the 'arguments' objectrender.length===0||render.length===2,'forwardRef render functions accept exactly two parameters: props and ref. %s',render.length===1
? 'Did you forget to use the ref parameter?'
: 'Any additional parameter will be undefined.',);}if(render!=null){warningWithoutStack(render.defaultProps==null&&render.propTypes==null,'forwardRef render functions do not support propTypes or defaultProps. '+'Did you accidentally pass a React component?',);}}return{$$typeof: REACT_FORWARD_REF_TYPE,
render,// render 即 function component};}
updateForwardRef 更新
case ForwardRef: {consttype=workInProgress.type;constunresolvedProps=workInProgress.pendingProps;constresolvedProps=workInProgress.elementType===type
? unresolvedProps
: resolveDefaultProps(type,unresolvedProps);returnupdateForwardRef(current,workInProgress,type,resolvedProps,renderExpirationTime,);}// updateForwardReffunctionupdateForwardRef(current: Fiber|null,workInProgress: Fiber,type: any,nextProps: any,renderExpirationTime: ExpirationTime,){// render 即 React.forwardRef 传入的 function componentconstrender=type.render;// 调用组件产生的 refconstref=workInProgress.ref;if(hasLegacyContextChanged()){// Normally we can bail out on props equality but if context has changed// we don't do the bailout and we have to reuse existing props instead.}elseif(workInProgress.memoizedProps===nextProps){// 之前的 ref constcurrentRef=current!==null ? current.ref : null;// 两个ref 相同则跳过更新if(ref===currentRef){returnbailoutOnAlreadyFinishedWork(current,workInProgress,renderExpirationTime,);}}letnextChildren;if(__DEV__){ReactCurrentOwner.current=workInProgress;ReactCurrentFiber.setCurrentPhase('render');nextChildren=render(nextProps,ref);ReactCurrentFiber.setCurrentPhase(null);}else{// React.forwardRef((props, ref) => {})// 传递 ref 并获取新 children,没有传 contextnextChildren=render(nextProps,ref);}// 调和子节点reconcileChildren(current,workInProgress,nextChildren,renderExpirationTime,);returnworkInProgress.child;}
// functioncreateHostRootFiber(isConcurrent: boolean): Fiber{// 这里在调用 ReactDOM.render 方法时 isConcurrent 为false,所以传递到子节点一直都是 NoContextletmode=isConcurrent ? ConcurrentMode|StrictMode : NoContext;if(enableProfilerTimer&&isDevToolsPresent){// Always collect profile timings when DevTools are present.// This enables DevTools to start capturing timing at any point–// Without some nodes in the tree having empty base times.mode|=ProfileMode;}returncreateFiber(HostRoot,null,null,mode);}// reconcileChildFibersfunctionreconcileChildFibers(returnFiber: Fiber,currentFirstChild: Fiber|null,newChild: any,expirationTime: ExpirationTime,): Fiber|null{// ...case REACT_ELEMENT_TYPE:
returnplaceSingleChild(reconcileSingleElement(returnFiber,currentFirstChild,newChild,expirationTime,),);// ... }// reconcileSingleElementfunctionreconcileSingleElement(returnFiber: Fiber,currentFirstChild: Fiber|null,element: ReactElement,expirationTime: ExpirationTime,): Fiber{// ....// 创建新的节点if(element.type===REACT_FRAGMENT_TYPE){// Fragment 传的elements传入的是 element.props.children, --> createFiber接收的 penddingProps 即这个 childrenconstcreated=createFiberFromFragment(element.props.children,returnFiber.mode,expirationTime,element.key,);created.return=returnFiber;returncreated;}else{// 根据 element type来创建不同 fiber 对象constcreated=createFiberFromElement(element,returnFiber.mode,expirationTime,);created.ref=coerceRef(returnFiber,currentFirstChild,element);created.return=returnFiber;returncreated;}}// createFiberFromTypeAndPropsfunctioncreateFiberFromTypeAndProps(type: any,// React$ElementTypekey: null|string,pendingProps: any,owner: null|Fiber,mode: TypeOfMode,expirationTime: ExpirationTime,){letfiber;letfiberTag=IndeterminateComponent;// The resolved type is set if we know what the final type will be. I.e. it's not lazy.letresolvedType=type;if(typeoftype==='function'){if(shouldConstruct(type)){// component 组件fiberTag=ClassComponent;}}elseif(typeoftype==='string'){fiberTag=HostComponent;}else{
getTag: switch(type){caseREACT_FRAGMENT_TYPE:
returncreateFiberFromFragment(pendingProps.children,mode,expirationTime,key,);// concurrent mode// NoContext | ConcurrentMode | StrictMode -> 0b011caseREACT_CONCURRENT_MODE_TYPE:
returncreateFiberFromMode(pendingProps,mode|ConcurrentMode|StrictMode,expirationTime,key,);// strict mode// NoContext | StrictMode -> 0b010caseREACT_STRICT_MODE_TYPE:
returncreateFiberFromMode(pendingProps,mode|StrictMode,expirationTime,key,);// ....returnfiber;}
createFiberFromMode 根据 mode 创建 Fiber 节点
type 赋值:mode & ConcurrentMode 等于 NoContext 则为 StrictMode,否则为 ConCurrentMode
依次给子节点也添加上父组件 mode 对应的 type
functioncreateFiberFromMode(pendingProps: any,mode: TypeOfMode,expirationTime: ExpirationTime,key: null|string,): Fiber{constfiber=createFiber(Mode,pendingProps,key,mode);// TODO: The Mode fiber shouldn't have a type. It has a tag.// 0b011 & 0b001 => 0b001 => ConCurrentMode// 0b010 & 0b001 => 0b000 => StrictModeconsttype=(mode&ConcurrentMode)===NoContext
? REACT_STRICT_MODE_TYPE
: REACT_CONCURRENT_MODE_TYPE;fiber.elementType=type;fiber.type=type;fiber.expirationTime=expirationTime;returnfiber;}
updateMode更新 Mode
functionupdateMode(current: Fiber|null,workInProgress: Fiber,renderExpirationTime: ExpirationTime,){constnextChildren=workInProgress.pendingProps.children;// children 来源?reconcileChildren(current,workInProgress,nextChildren,renderExpirationTime,);returnworkInProgress.child;}
functionmemo<Props>(
type: React$ElementType,
compare?: (oldProps: Props, newProps: Props) =>boolean,){return{$$typeof: REACT_MEMO_TYPE,// 这个 type 即传入进来的 function Component
type,compare: compare===undefined ? null : compare,};}// function createFiberFromTypeAndProps if(typeoftype==='object'&&type!==null){switch(type.$$typeof){case REACT_PROVIDER_TYPE:
fiberTag=ContextProvider;break getTag;case REACT_CONTEXT_TYPE:
// This is a consumerfiberTag=ContextConsumer;break getTag;case REACT_FORWARD_REF_TYPE:
fiberTag=ForwardRef;break getTag;case REACT_MEMO_TYPE:
fiberTag=MemoComponent;break getTag;case REACT_LAZY_TYPE:
fiberTag=LazyComponent;resolvedType=null;break getTag;}}
updateMemoComponent 更新 memo
memo 组件的 props 都是作用于传入的 function 组件内,memo 组件的意义在于进行一次组件包裹,可传入自定义比较函数
创建 child 没有使用调和子节点方式,而是直接创建
compare 函数如果没传入,则使用 shallowEqual,返回 true 则跳过更新。
case MemoComponent: {consttype=workInProgress.type;constunresolvedProps=workInProgress.pendingProps;constresolvedProps=resolveDefaultProps(type.type,unresolvedProps);returnupdateMemoComponent(current,workInProgress,type,resolvedProps,updateExpirationTime,renderExpirationTime,);}functionupdateMemoComponent(current: Fiber|null,workInProgress: Fiber,Component: any,nextProps: any,updateExpirationTime,renderExpirationTime: ExpirationTime,): null|Fiber{// 首次渲染if(current===null){// 拿到传入的那个 function componentlettype=Component.type;// 如果是纯函数组件没有 contruct、defaultProps 的组件且没有比较函数,则按 SimpleMemoComponent 更新if(isSimpleFunctionComponent(type)&&Component.compare===null){// If this is a plain function component without default props,// and with only the default shallow comparison, we upgrade it// to a SimpleMemoComponent to allow fast path updates.// 更新 tag,下次更新直接走 updateSimpleMemoComponentworkInProgress.tag=SimpleMemoComponent;workInProgress.type=type;returnupdateSimpleMemoComponent(current,workInProgress,type,nextProps,updateExpirationTime,renderExpirationTime,);}// reconcileChildFibers 会把 props 用于当前组件本身的 children 调和的过程 // 而对于 memo 组件的 child 来自于传入的那个 function component ,因此要将 props 传递给 function component,来创建 children// 直接创建,而不是调和子节点,letchild=createFiberFromTypeAndProps(Component.type,null,nextProps,null,workInProgress.mode,renderExpirationTime,);child.ref=workInProgress.ref;child.return=workInProgress;workInProgress.child=child;returnchild;}letcurrentChild=((current.child: any): Fiber);// This is always exactly one child// 如果没有必要更新if(updateExpirationTime===NoWork||updateExpirationTime>renderExpirationTime){// This will be the props with resolved defaultProps,// unlike current.memoizedProps which will be the unresolved ones.constprevProps=currentChild.memoizedProps;// Default to shallow comparisonletcompare=Component.compare;// compare 有则用,否则为 shallowEqualcompare=compare!==null ? compare : shallowEqual;// 相同则跳过更新if(compare(prevProps,nextProps)&¤t.ref===workInProgress.ref){returnbailoutOnAlreadyFinishedWork(current,workInProgress,renderExpirationTime,);}}letnewChild=createWorkInProgress(currentChild,nextProps,renderExpirationTime,);newChild.ref=workInProgress.ref;newChild.return=workInProgress;workInProgress.child=newChild;returnnewChild;}
updateSimpleMemoComponent
functionupdateSimpleMemoComponent(current: Fiber|null,workInProgress: Fiber,Component: any,// memo 传入的 function componentnextProps: any,updateExpirationTime,renderExpirationTime: ExpirationTime,): null|Fiber{if(// 更新渲染current!==null&&// 优先级比当前低 或者 不需要更新(updateExpirationTime===NoWork||updateExpirationTime>renderExpirationTime)){constprevProps=current.memoizedProps;// 如果 props 浅相同 并且 ref 相同 则跳过更新if(shallowEqual(prevProps,nextProps)&¤t.ref===workInProgress.ref){returnbailoutOnAlreadyFinishedWork(current,workInProgress,renderExpirationTime,);}}// 如果需要更新,纯 function component 按 function Component 方式更新returnupdateFunctionComponent(current,workInProgress,Component,nextProps,renderExpirationTime,);}
The text was updated successfully, but these errors were encountered:
lz-lee
changed the title
portalComponent、ForwardRef、Mode组件更新
portalComponent、ForwardRef、Mode 更新
Sep 5, 2019
lz-lee
changed the title
portalComponent、ForwardRef、Mode 更新
portalComponent、ForwardRef、Mode、memo 更新
Sep 6, 2019
portalComponent
createPortal
创建portal
,返回一个类似reactElement
的对象portal
类型组件创建fiber
节点createFiberFromPortal 创建 portal 类型的 fiber
updatePortalComponent 更新 fiber 节点
ForwardRef
forwardRef
用来传递ref
fiber
是在reconcileSingleElement -> createFiberFromElement -> createFiberFromTypeAndProps
中将fiberTag
标记为ForwardRef
updateForwardRef 更新
Mode 组件
<ConCurrentmode />
和<StrictMode />
Fiber
对象上记录所有子节点所对应的渲染模式,在创建更新时候根据mode
创建expirationTime
typeofMode
createFiberFromElement -> createFiberFromTypeAndProps
中为mode
组件创建fiber
节点,这里的传入的mode
为returnFiber(即父组件)
上的mode
,而父组件上的mode
最终来自hostRootFiber
上,默认为NoContext
Mode组件
它会有一个特殊的的Fiber
对象上的mode
属性conCurrentMode
传入的mode
为NoContext | ConcurrentMode | StrictMode -> 0b011
StrictMode
传入的Mode
为NoContext | StrictMode -> 0b010
Mode 组件
的子节点过程中,传入的mode
则是这个Mode 组件
它fiber
对象上的mode
属性,对于Mode 组件
它所有子树上的子节点都会具有这个Mode 组件
它fiber
对象上的mode
属性,以此来记录这个子树处于那个渲染模式下,才会有后期创建更新时根据mode
属性如ConcurrentMode
会对expirationTime
有个特殊的计算过程createFiberFromMode 根据 mode 创建 Fiber 节点
type
赋值:mode & ConcurrentMode
等于NoContext
则为StrictMode
,否则为ConCurrentMode
mode
对应的type
updateMode更新 Mode
Memo 组件
React.memo(com, compare)
创建一个具有pureComponent
特性的functionComponent
React.memo()
$$typeof
标记fiberTag
为MemoComponent
updateMemoComponent 更新 memo
memo
组件的props
都是作用于传入的function
组件内,memo
组件的意义在于进行一次组件包裹,可传入自定义比较函数child
没有使用调和子节点方式,而是直接创建compare
函数如果没传入,则使用shallowEqual
,返回true
则跳过更新。updateSimpleMemoComponent
The text was updated successfully, but these errors were encountered: