手写 React 第 4 节 - 实现 Fiber

手写 React 第 4 节 - 实现 Fiber

前言

在上一节【手写 React 第 3 节 - 实现函数组件/类组件】中实现了函数组件和类组件,本小节主要介绍 Fiber 的作用以及如何实现 Fiber。

1. Fiber 的作用

Fiber 是 React 16 之后才出来的,React 16 之前是没有 Fiber 的。Fiber 的作用主要是避免渲染中断出现异常以及控制页面渲染的优先权。

Fiber 的工作机制是将页面渲染的实现逻辑变成颗粒度更小的工作单元,并调用浏览器内置的 requestIdleCallback 方法。该方法会在浏览器空闲时间执行函数,使页面渲染减少卡顿,并实现渲染优先级。

2. Fiber 数据结构

function FiberNode(tag, pendingProps, key, mode) {
  //节点类型
  this.type = null;
  //节点属性
  this.props = props;
  // Fiber对应的真实 DOM 节点
  this.stateNode = null; 
  // 以下属性用于连接其他 Fiber 节点形成 Fiber 树。
  // 指向父Fiber节点
  this.return = null;
  //指向第一个子Fiber节点
  this.child = null;
  //指向右边第一个兄弟Fiber节点
  this.sibling = null;
}
import React from './react/index';
import ReactDOM from './react/react-dom';
import './index.css';
const App = (
    <div>
        <p>
            <h1 className="border">Mini React</h1>
            <span>fiber</span>
        </p>
        <div><span>fiber</span></div>
    </div>
)
ReactDOM.render(App,document.getElementById('root'));

在这里插入图片描述

在以上树结构中,采用深度优先遍历,节点规则如下:
A 节点的 return(父节点) 为 null,child 为 B 节点,sibling 为 null。
B 节点的 return(父节点) 为 A 节点,child 为 D 节点,sibling 为 C 节点。
D 节点的 return(父节点) 为 B 节点,child 为 null,sibling 为 E 节点。
E 节点的 return(父节点) 为 B 节点,child 为 null,sibling 为 null。
C节点的 return(父节点) 为 A 节点,child 为 F 节点,sibling 为 null。
F 节点的 return(父节点) 为 C 节点,child 为 null,sibling 为 null。

通过以上遍历后会构成一个 Fiber 链表结构。

3. 实现 Fiber

  1. render 中定义根节点 rootFiber,并将下一个单元工作 nextUnitOfWork 指向 rootFiber。可以理解为定义一个 Fiber 链表结构的表头。
//1.定义链表结构的表头
function render(vnode,container) {
   rootFiber = {
       stateNode:container,
       props:{
           children:[vnode]
       }
   }
   nextUnitOfWork = rootFiber;
}
  1. 开启 requestIdleCallback
//2.开启 requestIdleCallback
requestIdleCallback(workLoop);
  1. 进入工作循环

首先构建链表结构,然后将链表结构插入到真实 DOM 节点。

function workLoop(deadline) {
    //3.1 构建 fiber 链表结构
    while(nextUnitOfWork && deadline.timeRemaining()>1){
        nextUnitOfWork = performUnitOfWork(nextUnitOfWork)
    }
    //3.2 构建完成之后,插入到真实 DOM
    if(!nextUnitOfWork && rootFiber){
        commitRoot();
    }
}

3.1 构建 fiber 链表结构

首先构建当前 fiber 链表结构,再返回下一个 fiber

function performUnitOfWork(workInProcess) {
    //3.1.1 更新当前 fiber 节点的链表结构
   updateHostComponent(workInProcess);
   //3.1.2 返回下一个 fiber 节点
   return nextFiberWork(workInProcess);
}

3.1.1 更新当前 fiber 节点的链表结构

//3.1.1 更新当前 fiber 节点的链表结构
function updateHostComponent(workInProcess){
    if(!workInProcess.stateNode){
        workInProcess.stateNode = createNode(workInProcess);
    }
    reconcileChildren(workInProcess,workInProcess.props.children)
}
//创建真实 DOM 节点
function createNode(vnode,container) {
    const {type,props} = vnode;
    let node;
    if(type === 'TEXT'){
        node = document.createTextNode("");
    }else if(typeof type === 'string'){
        node = document.createElement(type);
    }else if(typeof type === 'function'){
        node = type.prototype.isReactComponent?updateClassComponent(vnode,container):updateFunctionComponent(vnode,container)
    }
    reconcileChildren(vnode,props.children);
    updateNode(node,props);
    return node;
}
// 创建子节点的 fiber 结构
function reconcileChildren(fiber,children) {
    let prevSibling = null;
    for(let i = 0;i<children.length;i++){
        let child = children[i];
        let newFiber = {
            type:child.type,
            props:child.props,
            stateNode:null,
            return:fiber,
            child:null,
            sibling:null
        }
        if(i === 0){
            fiber.child = newFiber;
        }else {
            prevSibling.sibling = newFiber;
        }
        prevSibling = newFiber;
    }
}

//更新节点属性
function updateNode(node,props) {
    for(let i in props){
        if(i!='children'){
            node[i] = props[i]
        }
    }
}
//更新类组件
function updateClassComponent(workInProcess,node) {
    const {type,props} = workInProcess;
    let instance = type(addProps(props));
    return createNode(instance,node);
}
//更新函数组件
function updateFunctionComponent(workInProcess,node) {
    const {type,props} = workInProcess;
    let instance = new type(addProps(props));
    return createNode(instance.render(),node);
}
//处理类组件和函数组件的属性
function addProps(props) {
    let keys = Object.keys(props);
    if(Array.isArray(keys) && keys.length>0){
        let newProps = {},isProps;
        keys.map(key=>{
            if(key!='children'){
                isProps = true;
                newProps[key] = props[key];
            }
        })
        if(isProps){
            return newProps;
        }
    }
}

3.1.2 返回下一个 fiber 节点

  //3.1.2 返回下一个 fiber 节点
function nextFiberWork(workInProcess) {
   if(workInProcess.child){
       return workInProcess.child
   };
   let nextFiber = workInProcess;
   while (nextFiber){
       if(nextFiber.sibling){
           return nextFiber.sibling
       }else {
           nextFiber = nextFiber.return;
       }
   }
}

3.2 构建完成之后,插入到真实 DOM

//3.2 构建完成之后,插入到真实 DOM
function commitRoot() {
    commitWorker(rootFiber.child);
    rootFiber = null;
}
function commitWorker(fiber){
    if(!fiber){
        return;
    }
    //获取 fiber 父节点
    let parentNodeFiber = fiber.return;
    while(!parentNodeFiber.stateNode){
        parentNodeFiber.stateNode = parentNodeFiber.return;
    }
    //获取真实 DOM 父节点
    let parentNode = parentNodeFiber.stateNode;
    if(fiber.stateNode){
        //将当前真实节点插入到父节点
        parentNode.appendChild(fiber.stateNode);
    }
    commitWorker(fiber.child);
    commitWorker(fiber.sibling);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值