手写 React 第 4 节 - 实现 Fiber

  • 前言
  • 1. Fiber 的作用
  • 2. Fiber 数据结构
  • 3. 实现 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 构建完成之后,插入到真实 DOMif(!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);
}

手写 React 第 4 节 - 实现 Fiber相关推荐

  1. 手写 React 第 2 节 - 初探 React 实现机制

    手写 React 第 2 节 - 初探 React 实现机制 前言 1. jsx 语法 2. createElement 实现 3. ReactDOM.render 实现 前言 在上一节[手写 Rea ...

  2. react 日期怎么格式化_手写React的Fiber架构,深入理解其原理

    熟悉React的朋友都知道,React支持jsx语法,我们可以直接将HTML代码写到JS中间,然后渲染到页面上,我们写的HTML如果有更新的话,React还有虚拟DOM的对比,只更新变化的部分,而不重 ...

  3. onclick 源码_精读:手写React框架 解析Hooks源码

    写在开头: 去年发表过一篇手写React,带diff算法,异步setState队列的文章,有一位阿里的朋友在下面评论,让我可以用hooks实现一次,也很简单,我当时觉得,这人有病,现在回过头来看,还是 ...

  4. 手写react优惠券组件

    先看效果图 由于是截图,大小有些失真 实现分析 看到这个图,思考一下,就能明白,其实就两个难点: 左边的锯齿状是如何实现 中间的凹陷是如何实现 上述两个难点解决了,相信有css基础的都能写出这个组件. ...

  5. JS手写上传文件、React手写上传文件

    目录 JS手写 React上传文件 JS手写 <!DOCTYPE html> <html lang="en"><head><meta ch ...

  6. svm手写数字识别_KNN 算法实战篇如何识别手写数字

    上篇文章介绍了KNN 算法的原理,今天来介绍如何使用KNN 算法识别手写数字? 1,手写数字数据集 手写数字数据集是一个用于图像处理的数据集,这些数据描绘了 [0, 9] 的数字,我们可以用KNN 算 ...

  7. 手写简易版 React 来彻底搞懂 fiber 架构

    React 16 之前和之后最大的区别就是 16 引入了 fiber,又基于 fiber 实现了 hooks.整天都提 fiber,那 fiber 到底是啥?它和 vdom 是什么关系? 与其看各种解 ...

  8. React,手写简易redux(二)- By Viga

    React,手写简易redux(二) 本章节会完成一个简易的redux实现 该系列内容会逐步实现简易的redux 使用技术栈:vite+react 该系列感谢@方应杭 的react教学视频 目录 实现 ...

  9. 第15节--神经网络应用--异或判断、手写数字识别

    1. 简单非线性关系数据集测试(XOR): X->y 00–>0 01–>1 10–>1 11–>0 调用上节课的神经网络程序,实现异或预测 from NeuralNet ...

最新文章

  1. 开机运行记事本怎么回事
  2. 2017最新整理传智播客JavaEE第49期 基础就业班
  3. SVN服务器部署并实现双机同步及禁止普通用户删除文件
  4. Coursera自动驾驶课程第3讲:Self-Driving Hardware and Software Architectures
  5. 13--长度最小的子数组
  6. TCPIP详解3.8ifconfig
  7. 计算机dos命令大全TXT,dos命令大全常见命令图解
  8. 使用Python+TensorFlow2构建基于卷积神经网络(CNN)的ECG心电信号识别分类(二)
  9. 答案原文翻译解释《避凶就吉精明鬼,千变万化心不变》这句话是指什么意思?...
  10. Q12矩阵中的路径 回溯法
  11. 2022低压电工培训试题模拟考试平台操作
  12. 安装宝塔面板后原ssh连接失败问题及解决
  13. 什么是Word Embeddings
  14. maple 解代数方程组得多项式_Maple笔记2--常微分方程求解
  15. 中国互联网金融举报信息平台将“代币发行融资”列入举报范围
  16. Python 数字转中文大写
  17. julia编程语言有前途吗?
  18. Vue学习记录三 --- vue的项目实战
  19. Python +tensorflow+pygame 破解任意字体反爬
  20. “链游之王”Axie引入全新保险机制 Ronin桥重新上线 避免重蹈覆辙

热门文章

  1. Retinanet网络结构简介
  2. 利用Python获取历史双色球开奖结果
  3. csapp家庭作业第十章
  4. Kubernetes---Secret配置管理
  5. 【深度学习】一个用于styleGAN图像处理的编码器
  6. 前端背景图放置_如何在css中添加背景图?
  7. C++stack与queue模拟实现
  8. 如何批量压缩pdf文件到最小
  9. ubuntu 有key登入sshd Failed publickey for xxx from xx port Xxx ssh2
  10. 2022-2028年全球与中国老年手机行业发展趋势及竞争策略研究