# react_fiber **Repository Path**: aeipyuan/react_fiber ## Basic Information - **Project Name**: react_fiber - **Description**: No description available - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2020-05-29 - **Last Updated**: 2020-12-19 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README ## 生成`virtual DOM`树 bable转换jsx的结果 ```jsx let style = { border: "1px solid orange", margin: "5px" } let element = (
A1
B1
C1
C2
B2
) //babel解析结果: // React.createElement("div", { id: "A1", style: style }, // " A1", // React.createElement("div", { id: "B1", style: style }, // " B1", // React.createElement("div", { id: "C1", style: style }, "C1"), // React.createElement("div", { id: "C2", style: style }, "C2") // ), // React.createElement("div", { id: "B2", style: style }, "B2") // ); ``` 实现createElment ```javascript import { ELEMENT_TEXT } from './constants' function createElement(type, props, ...children) { delete props.__source; delete props.__self; return { type, props: { ...props, children: children.map(child => { /* 文本节点特殊处理 */ return typeof child === 'object' ? child : { type: ELEMENT_TEXT, props: { text: child, children: [] } } }) } } } ``` React.createElement处理过后的`virtual DOM` ![](https://imgconvert.csdnimg.cn/aHR0cHM6Ly9naXRlZS5jb20vYWVpcHl1YW4vcGljdHVyZV9iZWQvcmF3L21hc3Rlci9pbWFnZXMvMjAyMDA1MjkxNDUzNDkucG5n?x-oss-process=image/format,png) ## 实现render方法 render使用 ```javascript ReactDOM.render( element, document.getElementById('root') ); ``` react-dom入口文件 ```javascript import { TAG_ROOT } from "../react/constants" import scheduleRoot from './schedule' function render(element, container) { /* 根fiber */ let rootFiber = { tag: TAG_ROOT, stateNode: container, props: { children: [element] } }; scheduleRoot(rootFiber); } export default { render} ``` ### 1. 初始化 ```javascript let wrokProgressRoot = null;//记录根节点 let nextUnitOfWork = null;//记录当前工作单元 function scheduleRoot(rootFiber) { wrokProgressRoot = rootFiber; nextUnitOfWork = rootFiber; } ``` ### 2. 工作循环 `requestIdleCallback`作用 实现在浏览器空闲时运行`workLoop`,若超过500ms之不管是否空闲都运行(60帧显示器每16.6ms刷新一次,存在空闲时间则执行`performUnitOfwork`,一直到达500ms无空闲时间则强制执行) ```javascript // 工作循环 function workLoop(deadline) { // 时间片未到 if ((deadline.timeout || deadline.timeRemaining() > 0) && nextUnitOfWork) { /* 执行工作单元,返回下一单元 */ nextUnitOfWork = performUnitOfWork(nextUnitOfWork); } /* 还有工作单元未执行 */ if (!nextUnitOfWork && wrokProgressRoot) { console.log("Render 完成"); // 生成DOM commitRoot(); } requestIdleCallback(workLoop, { timeout: 500 }); } requestIdleCallback(workLoop, { timeout: 500 }); ``` #### 2.1 performUnitOfWork执行工作单元 ```javascript function performUnitOfWork(currentFiber) { //构建 beginWork(currentFiber); /* 子元素 */ if (currentFiber.child) { return currentFiber.child; } /* 没有子元素释放自己并往上找 */ while (currentFiber) { /* fiber子元素全部完成,将自身合并到副作用链 */ completeUnitOfWork(currentFiber); if (currentFiber.sibling) { return currentFiber.sibling; } /* 往上找 */ currentFiber = currentFiber.return; } } ``` #### 2.1.1 beginWork分类构建 ```javascript function beginWork(currentFiber) { //根元素 if (currentFiber.tag === TAG_ROOT) { upDateRoot(currentFiber); } //原生节点 if (currentFiber.tag === TAG_HOST) { upDateHost(currentFiber); } //文本节点 if (currentFiber.tag === TAG_TEXT) { upDateText(currentFiber); } } /* 更新根元素 */ function upDateRoot(currentFiber) { /* 构建子元素 */ let newChildren = currentFiber.props.children; reconcileChildren(currentFiber, newChildren); } /* 更新原生元素 */ function upDateHost(currentFiber) { /* 创建DOM */ if (!currentFiber.stateNode) { currentFiber.stateNode = createDOM(currentFiber); } /* 获取并构建子元素 */ let newChildren = currentFiber.props.children; reconcileChildren(currentFiber, newChildren); } /* 更新文本元素 */ function upDateText(currentFiber) { if (!currentFiber.stateNode) { currentFiber.stateNode = createDOM(currentFiber); } } ``` createDOM方法创建Dom,设置属性 ```javascript /* 创建DOM */ function createDOM(currentFiber) { if (currentFiber.tag === TAG_TEXT) { /* 直接船舰文本节点 */ return document.createTextNode(currentFiber.props.text); } else if (currentFiber.tag === TAG_HOST) { let stateNode = document.createElement(currentFiber.type);//
setProps(stateNode, {}, currentFiber.props); //
return stateNode; } } ``` reconcileChildren遍历子节点并记录父子兄弟fiber关系 ![](https://imgconvert.csdnimg.cn/aHR0cHM6Ly9naXRlZS5jb20vYWVpcHl1YW4vcGljdHVyZV9iZWQvcmF3L21hc3Rlci9pbWFnZXMvMjAyMDA1MjkxNTIzMTAucG5n?x-oss-process=image/format,png) ```javascript /* 创建子元素fiber并连接到父元素上 */ function reconcileChildren(returnFiber, newChildren) { let newChildIndex = 0,prevSibling = null; //遍历returnFiber的子节点 while (newChildIndex < newChildren.length) { let newChild = newChildren[newChildIndex]; /* 标识文本和原生组件 */ let tag; if (newChild.type === ELEMENT_TEXT) { tag = TAG_TEXT; } else if (typeof newChild.type === "string") { tag = TAG_HOST; } /* 创建Fiber */ let newFiber = { tag, type: newChild.type, props: newChild.props, stateNode: null, return: returnFiber,//父节点 effectTag: PLACEMENT,//操作 nextEffect: null//下一个节点 } /* 连接Fiber,第一个子元素作为儿子,其他作为兄弟连接 */ if (newChildIndex === 0) { returnFiber.child = newFiber; } else { prevSibling.sibling = newFiber; } prevSibling = newFiber; newChildIndex++; } } ``` #### 2.1.2 completeUnitOfWork构建副作用链 ![](https://imgconvert.csdnimg.cn/aHR0cHM6Ly9naXRlZS5jb20vYWVpcHl1YW4vcGljdHVyZV9iZWQvcmF3L21hc3Rlci9pbWFnZXMvMjAyMDA1MjkxNTMxNDAucG5n?x-oss-process=image/format,png) ```javascript function completeUnitOfWork(currentFiber) { /* 获取父节点 */ let returnFiber = currentFiber.return; if (returnFiber) { /* 将自己连接到父元素 */ if (!returnFiber.firstEffect) { returnFiber.firstEffect = currentFiber.firstEffect; } if (currentFiber.lastEffect) { /* 将当前fiber头部接到父fiber尾部 */ if (returnFiber.lastEffect) { returnFiber.lastEffect.nextEffect = currentFiber.firstEffect; } /* 当前尾部作为最终尾部 */ returnFiber.lastEffect = currentFiber.lastEffect; } /* 连接子元素 */ if (currentFiber.effectTag === PLACEMENT) { /* 当前元素尾部有元素才会连接 */ if (returnFiber.lastEffect) { returnFiber.lastEffect.nextEffect = currentFiber; } else { returnFiber.firstEffect = currentFiber; } /* 更新尾巴 */ returnFiber.lastEffect = currentFiber; } } } ``` #### 2.2 commit挂载DOM ```javascript function commitWork(currentFiber) { /* 获取父元素 */ let returnDom = currentFiber.return.stateNode; /* 副作用类型 */ if (currentFiber.effectTag === PLACEMENT) { returnDom.appendChild(currentFiber.stateNode); } /* 去除副作用 */ currentFiber.effectTag = null; } function commitRoot() { /* 获取链表头 */ let currentFiber = wrokProgressRoot.firstEffect; while (currentFiber) { commitWork(currentFiber); currentFiber = currentFiber.nextEffect; } wrokProgressRoot = null; } ``` ## 总结 ReactDOM的render方法首先将虚拟DOM树进行扩充,记录节点的孩子和兄弟,然后将副作用节点按照自底向上的顺序记录在一个链表中,commit时实现从里到外改变DOM。