Fiber学习

生成virtual DOM

bable转换jsx的结果

let style = { border: "1px solid orange", margin: "5px" }
let element = (
  <div id="A1" style={style}> A1
    <div id="B1" style={style}> B1
      <div id="C1" style={style}>C1</div>
      <div id="C2" style={style}>C2</div>
    </div>
    <div id="B2" style={style}>B2</div>
  </div>
)
//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

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

实现render方法

render使用

ReactDOM.render(
  element,
  document.getElementById('root')
);

react-dom入口文件

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. 初始化

let wrokProgressRoot = null;//记录根节点
let nextUnitOfWork = null;//记录当前工作单元
function scheduleRoot(rootFiber) {
    wrokProgressRoot = rootFiber;
    nextUnitOfWork = rootFiber;
}

2. 工作循环

requestIdleCallback作用

实现在浏览器空闲时运行workLoop,若超过500ms之不管是否空闲都运行(60帧显示器每16.6ms刷新一次,存在空闲时间则执行performUnitOfwork,一直到达500ms无空闲时间则强制执行)

// 工作循环
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执行工作单元

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分类构建

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,设置属性

/* 创建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);//<div></div>
        setProps(stateNode, {}, currentFiber.props);
        //<div id="A1" style="border: 1px solid orange; margin: 5px;"></div>
        return stateNode;
    }
}

reconcileChildren遍历子节点并记录父子兄弟fiber关系

/* 创建子元素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构建副作用链

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

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。

posted @ 2020-05-29 16:00  aeipyuan  阅读(158)  评论(0编辑  收藏  举报