手写 React 第 4 节 - 实现 Fiber
手写 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
- 在
render
中定义根节点rootFiber
,并将下一个单元工作nextUnitOfWork
指向rootFiber
。可以理解为定义一个 Fiber 链表结构的表头。
//1.定义链表结构的表头
function render(vnode,container) {rootFiber = {stateNode:container,props:{children:[vnode]}}nextUnitOfWork = rootFiber;
}
- 开启 requestIdleCallback
//2.开启 requestIdleCallback
requestIdleCallback(workLoop);
- 进入工作循环
首先构建链表结构,然后将链表结构插入到真实 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相关推荐
- 手写 React 第 2 节 - 初探 React 实现机制
手写 React 第 2 节 - 初探 React 实现机制 前言 1. jsx 语法 2. createElement 实现 3. ReactDOM.render 实现 前言 在上一节[手写 Rea ...
- react 日期怎么格式化_手写React的Fiber架构,深入理解其原理
熟悉React的朋友都知道,React支持jsx语法,我们可以直接将HTML代码写到JS中间,然后渲染到页面上,我们写的HTML如果有更新的话,React还有虚拟DOM的对比,只更新变化的部分,而不重 ...
- onclick 源码_精读:手写React框架 解析Hooks源码
写在开头: 去年发表过一篇手写React,带diff算法,异步setState队列的文章,有一位阿里的朋友在下面评论,让我可以用hooks实现一次,也很简单,我当时觉得,这人有病,现在回过头来看,还是 ...
- 手写react优惠券组件
先看效果图 由于是截图,大小有些失真 实现分析 看到这个图,思考一下,就能明白,其实就两个难点: 左边的锯齿状是如何实现 中间的凹陷是如何实现 上述两个难点解决了,相信有css基础的都能写出这个组件. ...
- JS手写上传文件、React手写上传文件
目录 JS手写 React上传文件 JS手写 <!DOCTYPE html> <html lang="en"><head><meta ch ...
- svm手写数字识别_KNN 算法实战篇如何识别手写数字
上篇文章介绍了KNN 算法的原理,今天来介绍如何使用KNN 算法识别手写数字? 1,手写数字数据集 手写数字数据集是一个用于图像处理的数据集,这些数据描绘了 [0, 9] 的数字,我们可以用KNN 算 ...
- 手写简易版 React 来彻底搞懂 fiber 架构
React 16 之前和之后最大的区别就是 16 引入了 fiber,又基于 fiber 实现了 hooks.整天都提 fiber,那 fiber 到底是啥?它和 vdom 是什么关系? 与其看各种解 ...
- React,手写简易redux(二)- By Viga
React,手写简易redux(二) 本章节会完成一个简易的redux实现 该系列内容会逐步实现简易的redux 使用技术栈:vite+react 该系列感谢@方应杭 的react教学视频 目录 实现 ...
- 第15节--神经网络应用--异或判断、手写数字识别
1. 简单非线性关系数据集测试(XOR): X->y 00–>0 01–>1 10–>1 11–>0 调用上节课的神经网络程序,实现异或预测 from NeuralNet ...
最新文章
- 开机运行记事本怎么回事
- 2017最新整理传智播客JavaEE第49期 基础就业班
- SVN服务器部署并实现双机同步及禁止普通用户删除文件
- Coursera自动驾驶课程第3讲:Self-Driving Hardware and Software Architectures
- 13--长度最小的子数组
- TCPIP详解3.8ifconfig
- 计算机dos命令大全TXT,dos命令大全常见命令图解
- 使用Python+TensorFlow2构建基于卷积神经网络(CNN)的ECG心电信号识别分类(二)
- 答案原文翻译解释《避凶就吉精明鬼,千变万化心不变》这句话是指什么意思?...
- Q12矩阵中的路径 回溯法
- 2022低压电工培训试题模拟考试平台操作
- 安装宝塔面板后原ssh连接失败问题及解决
- 什么是Word Embeddings
- maple 解代数方程组得多项式_Maple笔记2--常微分方程求解
- 中国互联网金融举报信息平台将“代币发行融资”列入举报范围
- Python 数字转中文大写
- julia编程语言有前途吗?
- Vue学习记录三 --- vue的项目实战
- Python +tensorflow+pygame 破解任意字体反爬
- “链游之王”Axie引入全新保险机制 Ronin桥重新上线 避免重蹈覆辙