React源码解析

254 阅读1分钟

React 源码分析

  • createElement
  • workLoop 通过requesetIdleCallback deadline.timeRemaining()
  • performUnitWork 拆分任务
  • reconcileChildren 构建Fiber
  • commit 把fiber 渲染成 真实dom

jsx -> React.createElement

createElement 最后需要放回一个对象

function createElement(type, config, ...children) {
  const props = {
    ...config,
    children,
  };
  return {
    type,
    props
  };
}

举例: 下面是一个jsx, 通过createElement 会变成什么

<div className="border">
    <p>标题</p>
    <a href="https://bj.58.com">58</a>
</div>

下图就是转成之后的js对象

可以看出对于 text的这种情况不好遍历, 所以改进一下createElement

function createElement(type, config, ...children) {
	const props = {
		...config,
		children: children.map(child => {
			return typeof child === 'object' ? child : createTextNode(child)
		})
	};
	return {
		type,
		props
	}
}

function createTextNode(text){
	return {
		type: "TEXT",
		props: {
			children : [],
			nodeValue: text
		}
	}
}

分析一下Fiber && reconcileChildren && commit

Fiber 是 js 主要是对虚拟 dom 采用深度优先遍历

分解一下fiber 顺序 child sibling

function workLoop(deadline) {
	while( nextUnitOfWork  && deadline.timeRemaining() > 1 ){
		nextUnitOfWork = performUnitOfWork(nextUnitOfWork) //  拆分小任务 
	}
	if(!nextUnitOfWork && wipRoot){ nextUnitOfWork 是null 的时候 需要 commit
		commitRoot()
	}
	requestIdleCallback(workLoop)
}
requestIdleCallback(workLoop)

分解任务

if(fiber.child){
    return fiber.child
}

let nextFiber = fiber;
while(nextFiber){
    if(nextFiber.sibling){
        return nextFiber.sibling  // 表明 如果最后没有sibling的话,以及 return 都没有sibling的话,直接null
    }
    nextFiber = nextFiber.return
}

遍历之前的虚拟dom变成 fiber结构

let prevSlibling = null
for(let i = 0; i < children.length; i++){
    let child = children[i]
    let newFiber = {
        type: child.type,
        props: child.props,
        node: null,
        return: workInProgressFiber,
        effectTag: PLACEMENT
    }
    if(i == 0){
        workInProgressFiber.child = newFiber 
    }else{
        prevSlibling.sibling = newFiber
    }
    prevSlibling = newFiber

}

最后来commit 一下

if(!fiber){ return }
let parentNodeFiber = fiber.return;
while(!parentNodeFiber.node){
	parentNodeFiber = parentNodeFiber.return
}
const parentNode = parentNodeFiber.node
if(fiber.node !== null && fiber.effectTag == PLACEMENT){
    parentNode.appendChild(fiber.node)
}

commitWorker(fiber.child)
commitWorker(fiber.sibling)

commit 阶段就是child sibling 一起遍历完成