Open lz-lee opened 4 years ago
fiber
ref
commit
commitAllHostEffects
dom
commitDetachRef
commitAllLifeCycles
commitAttachRef
ref 是函数
ref 函数
instance
// createRef function createRef(): RefObject { const refObject = { current: null, }; if (__DEV__) { Object.seal(refObject); } return refObject; }
ref 方法
class instanc
refs[stringRef]
this.refs.stringRef
// ReactChildFiber.js 调和子节点复用或者创建新的fiber 节点时处理 ref function coerceRef( returnFiber: Fiber, current: Fiber | null, element: ReactElement, ) { let mixedRef = element.ref; if ( mixedRef !== null && typeof mixedRef !== 'function' && typeof mixedRef !== 'object' ) { // 处理字符串 ref // createElement -> 传入 owner 为 ReactCurrentOwner.current ,而在 finishClassComponent 中设置 ReactCurrentOwner.current= workInProgress; // 之后子节点会在 instance.render 当中被创建,进入到调和子节点中 // _owner 即 fiber 对象 if (element._owner) { const owner: ?Fiber = (element._owner: any); let inst; if (owner) { const ownerFiber = ((owner: any): Fiber); // classComponent 的实例 inst = ownerFiber.stateNode; } const stringRef = '' + mixedRef; // Check if previous string ref matches new string ref if ( current !== null && current.ref !== null && typeof current.ref === 'function' && current.ref._stringRef === stringRef // _stringRef 如果没有变化,不需要重新生成 ref 方法 ) { return current.ref; } const ref = function(value) { // 即组件里使用的 this.refs let refs = inst.refs; if (refs === emptyRefsObject) { // This is a lazy pooled frozen object, so we need to initialize. refs = inst.refs = {}; } // dom 节点 或者 instance 被挂载的时候会调用 ref 这个方法,value 即传入它自己的实例 if (value === null) { delete refs[stringRef]; } else { // 设置到 this.refs 的 stringRef 属性上 refs[stringRef] = value; } }; ref._stringRef = stringRef; // 这个方法在 commit 阶段被调用 return ref; } else { // 。。。 } } return mixedRef; }
commitAllHostEffects 先卸载原有的 ref
// dom 节点的插入、删除、更新 function commitAllHostEffects() { while (nextEffect !== null) { recordEffect(); const effectTag = nextEffect.effectTag; // 判断内部是否为文字节点 if (effectTag & ContentReset) { // 文字节点设置为空字符串 commitResetTextContent(nextEffect); } // ref操作 if (effectTag & Ref) { const current = nextEffect.alternate; // 非首次渲染 if (current !== null) { commitDetachRef(current); } } // ... } }
// commitDetachRef
function commitDetachRef(current: Fiber) { const currentRef = current.ref; if (currentRef !== null) { if (typeof currentRef === 'function') { // 如果是函数则传入 null 执行 currentRef(null); } else { // 其他类型直接赋值 null currentRef.current = null; } } }
### commitAttachRef - `commitAllLifeCycles` 中将更新后的 `instance` 挂载到 `ref` 上 ```js function commitAttachRef(finishedWork: Fiber) { const ref = finishedWork.ref; if (ref !== null) { const instance = finishedWork.stateNode; let instanceToUse; // 获取 instance switch (finishedWork.tag) { case HostComponent: instanceToUse = getPublicInstance(instance); break; default: instanceToUse = instance; } if (typeof ref === 'function') { // 这里即函数式 ref 真正挂载 instance, 字符串 ref 创建的 ref 函数也在这执行,将 this.refs[stringRef] = instanceToUse ref(instanceToUse); } else { // 其他类型直接赋值 ref.current ref.current = instanceToUse; } } }
ref
fiber
的时候(调和子节点里面)处理ref
commit
开始前,第二次循环执行commitAllHostEffects
(dom
的增、删、改)过程中,调用commitDetachRef
将ref
从之前挂载的地方卸载commitAllLifeCycles
调用commitAttachRef
将更新过后的节点挂载到ref
上,如果ref 是函数
则执行ref 函数
将fiber
的instance
作为参数传入coerceRef
ref
,需要创建一个ref 方法
,挂载在fiber
的ref
属性上,在这个方法里将更新过的dom
节点或者class instanc
e 挂载在实例的refs[stringRef]
上(即commitAttachRef
过程)this.refs.stringRef
获取ref
,即将被废弃commitDetachRef
commitAllHostEffects
先卸载原有的ref
// commitDetachRef
function commitDetachRef(current: Fiber) { const currentRef = current.ref; if (currentRef !== null) { if (typeof currentRef === 'function') { // 如果是函数则传入 null 执行 currentRef(null); } else { // 其他类型直接赋值 null currentRef.current = null; } } }