当前位置: 首页 > news >正文

【React的Fiber及中断-重启逻辑的设计】

React的Fiber

  • 前言
  • 中断-重启的效果
  • 中断-重启逻辑
    • 入口
    • 中断
    • 重启准备
    • MessageChannel(扩展)
    • 重启
  • 结语

前言

react的循环渲染逻辑可以参考我的这篇文章。【react循环渲染】

中断-重启的效果

以前的同步阻塞渲染的火焰峰图
在这里插入图片描述

现在开启中断-重启逻辑的火焰峰图
在这里插入图片描述
可以看到React的中断-重启分片的设计能够降低原本同步循环渲染带来的卡顿问题,分片的设计也使得React能够交换不同优先级任务间的执行顺寻。不过这也为React引入了另一个问题,并发渲染导致的同一页面不同组件在同一帧读取到不同store值的问题(react提供的解决方式useSyncExternalStore)。

中断-重启逻辑

入口

前言的文章即是按照同步阻塞渲染逻辑来debugger的。中断-重启分片渲染逻辑与阻塞渲染逻辑分道扬镳始于这个判断(因本人不知道如何才能让react自动走到这个判断上来,索性debugger的时候直接把shouldTimeSlice改成true了)
在这里插入图片描述

// 中断-重启调用栈
performConcurrentWorkOnRoot -> renderRootConcurrent -> workLoopConcurrent -> performUnitOfWork -> beginWork
// 相较于同步循环渲染区别就在于workLoopConcurrent的调用
function workLoopConcurrent() {// Perform work until Scheduler asks us to yieldwhile (workInProgress !== null && !shouldYield()) {performUnitOfWork(workInProgress);}
}
// 通过额外添加!shouldYield()来增加跳出循环的时机,释放栈给其它任务然后再在某一时候重启中断的循环

shouldYield()函数会在执行时间超过5ms的时候返回true进而跳出当前的while循环

function shouldYield () {const timeElapsed = performance.now() - startTimeif (timeElapsed  < 5) {return false}return true
}

中断

通过控制shouldYield函数返回true来跳出workLoopConcurrent的while循环,进而进入为之后重启做准备的逻辑。
在这里插入图片描述

// 进入中断流程之后renderRootConcurrent函数的返回值
function renderRootConcurrent() {if (workInProgress !== null) {return RootInProgress} else {return workInProgressRootExitStatus; // 返回完成状态}
}
// 如果返回的是RootInProgress则会进入后续的重启准备中。像同步流程中结束之后返回workInProgressRootExitStatus,则会进入finishConcurrentRender函数开启commitRoot(虚拟dom到真实dom的创建渲染过程)

调试项目中的自定义组件,主要是展示与上方FiberNode中的type关系,非流程相关的图片,可以忽略不看
在这里插入图片描述

重启准备

在这里插入图片描述
从入口部分粘贴的代码部分中可以看到performConcurrentWorkOnRoot函数即为终端重启调用栈的开始,这里的返回为下一次的开启创造了函数执行的条件

此时函数栈弹出到workLoop函数中,并将continuationCallback赋值成上面返回的函数,最后赋值给currentTask.callback,并在下一次的while循环通过shouldYieldToHost()进行循环的跳出

function workLoop() {while() {if(shouldYieldToHost()) {break; // 通过此处跳出上述循环}const callback = currentTask.callback; // 任务队列中当前任务的执行函数const continuationCallback = callback(); // 任务执行完成后的返回值if (typeof continuationCallback === 'function') {currentTask.callback = continuationCallback;}}if (currentTask !== null) {return true} else {return false}
}

参考图
中断到准备重启函数调用栈弹出顺序

// ---> 代表函数栈弹出方向
workLoopConcurrent ---> renderRootConcurrent -> performConcurrentWorkOnRoot -> flushWork -> performWorkUntilDeadline
// 弹出到performWorkUntilDeadline会执行schedulePerformWorkUntilDeadline()
const performWorkUntilDeadline = () => {let hasMoreWork = true;try {hasMoreWork = scheduledHostCallback(hasTimeRemaining, currentTime);} finally {if (hasMoreWork) {schedulePerformWorkUntilDeadline();} else {isMessageLoopRunning = false;scheduledHostCallback = null;}}
}// schedulePerformWorkUntilDeadline 是一个全局let变量schedulePerformWorkUntilDeadline = () => {port.postMessage(null); // 此时会推送一个为null的消息,等待下一次时间循环被取出};

MessageChannel(扩展)

扩展React这里的port使用的是什么消息发布订阅功能(MessageChannel)。port.postMessage()可以任务具有宏任务的作用,会遵循事件循环来等待任务的调用。

const { port1, port2 } = new MessageChannel();
port1.onmessage = (event) => {console.log("port1收到消息", event);
};
import React from "react";
const MessageChannelIndex = () => {function testEventLoop () {console.log("我是事件循环同步任务的开始");setTimeout(() => {console.log("我是setTimeout");}, 0);port2.postMessage("我是port2");console.log("我是事件循环同步任务的结束");}return (<div><button onClick={testEventLoop}>开始</button></div>);
};export default MessageChannelIndex;

看下打印效果
在这里插入图片描述
宏任务可以让浏览器能够调度高优先级的像用户输入,滚动,I/O等高优先级的任务进行插队,从而防止卡顿。

重启

当事件循环来到port1.onmessage订阅的函数执行时,循环渲染变可以开始重启了。

// 订阅函数
channel.port1.onmessage = performWorkUntilDeadline
// 开始循环渲染的调用栈顺序
performWorkUntilDeadline -> flushWork -> workLoop -> performConcurrentWorkOnRoot -> renderRootConcurrent -> workLoopConcurrent

结语

按照上述流程循环往复知道全部渲染完成。

http://www.dtcms.com/a/540396.html

相关文章:

  • 石狮建设网站网站建设费要摊销
  • 人工智能——K-Means聚类进行青少年市场细分实践
  • 卷积运算全解析:从原理到MATLAB实现
  • BIM+GIS尝试
  • vscode关闭自动激活conda环境
  • jdk动态代理实现原理(二)
  • 上海旅游网站建设精通网站开发
  • 营销型网站建设的优缺点广州建站代运营公司有哪些
  • 6.1.1.4 大数据方法论与实践指南-Flink 任务优化实践
  • 面向中小企业的大模型推理引擎:技术架构与应用实践
  • Object-C 中的证书校验
  • PCIe协议之 SMBus 信号线
  • 赋能国防航天,数字孪生IOC ProMAX版如何重塑智能指挥与运维新标杆
  • GXDE 内核管理器1.0.0——支持 deepin20、23
  • 声呐到底怎么选?
  • 做购物网站是怎么连接银行公众号怎么做小程序
  • 吉林省城乡建设官方网站网站后台修改教程
  • saas模板使用教程
  • 在CentOS 7.9上升级OpenSSH到9.9p2
  • asp 网站支持多语言想建立一个网站
  • Spring Boot3零基础教程,Spring Security 简介,笔记80
  • 调试技巧:从 IDE 调试到生产环境定位问题,提升调试效率的全方位指南
  • 服务器和docker容器时间不一致相关问题
  • Vue+Element Plus 表格工具栏组件:动态按钮 + 搜索控制的优雅实现​
  • 上海网站建设平台什么是seo标题优化
  • 网络编程之WebSocket(1)
  • Electron_Vue3 自定义系统托盘及退出二次确认
  • 为什么 Electron 项目推荐使用 Monorepo 架构 [特殊字符][特殊字符][特殊字符]
  • BLIP2 工业实战(一):从零实现 LAVIS 跌倒检测 (微调与“踩坑”指南)
  • NPM下载和安装图文教程(附安装包)