React 是一个开源 JavaScript 库,开发人员使用它来创建基于 Web 和移动的应用程序,并且支持构建交互式用户界面和 UI 组件。React 是由 Facebook 软件工程师 Jordan Walke 创建,React 的第一个版本在七年前问世,现在,Facebook 负责维护。React框架自首次发布以来,React 的受欢迎程度直线飙升,热度不减。

2020 年 10 月,React 17 发布了,但令人惊讶的是——“零新功能”。当然,这并不是真的表示没有任何新添加的功能,让广大程序员使用者兴奋。事实上,这个版本为我们带来了很多重大功能的升级及16版本的bug修复,并推出了:Concurrent Mode 和Suspense。

虽然这两个功能尚未正式发布,这些功能已提供给开发人员进行测试。一旦发布,它们将改变 React 呈现其 UI 的方式,从而达到双倍提高性能和用户体验。

简要说明, Concurrent Mode 和Suspense 可以使用户无缝处理数据加载,加载状态,用户界面操作更加平滑和无缝切换。 在Concurrent Mode 下,React可以暂停高消耗的,非紧急的组件的渲染,并聚焦在更加紧迫的任务处理,如UI 渲染,始终保持应用为可响应式,避免白屏,卡顿等现象。

本文主要分享深入了解Concurrent Mode 和Suspense 模式下的数据提取功能。

为什么需要 Concurrent Mode?

众所周知,JavaScript 框架或库是单线程的工作。因此,当一个代码块运行时,其余的块必须等待执行。无法并发执行多线程工作。界面渲染也是一样的。

一旦 React 开始渲染某些东西,无法中断直到运行完成。React 开发人员将这种渲染称为“阻塞渲染”。 这种阻塞渲染会创建一个不稳定的用户界面,并且随时可能停止响应。

具体问题

假如,我们需要显示一个很长的可选列表用于过滤产品的应用程序。我们使用搜索框用于过滤记录,设计方案是当用户点击搜索按钮后,用户界面需要重新刷新列出相关联的数据。

如果列表过长,数据过多,UI“卡顿”,即渲染对用户可见。这种卡顿也会大大降低产品性能。开发人员可以使用一些技术,如节流和防抖,这些技术会有一定帮助,但不是完美的解决方案。

节流限制特定函数被调用的次数。使用节流,我们可以避免重复调用昂贵和耗时的API或函数。这个过程能够提高性能,尤其是在用户界面上呈现信息。

防抖会在预定的时间内忽略对函数的调用。函数调用仅在经过预定时间后进行。

下图描述了卡顿现象:

在等待非紧急 API 调用完成时,UI 卡顿,从而阻止呈现用户界面。解决方案是使用并发模式进行可中断渲染。

无中断渲染

通过可中断渲染,React.js 在处理和重新渲染列表时不会阻塞 UI。它通过暂停琐碎的工作、更新 DOM 并确保 UI 不会卡顿,使 React.js 更加细化。React 使用用户输入并行更新或重绘输入框。React 使用用户输入并重绘输入框并行执行。它还更新内存中的列表。React 完成更新后,它会更新 DOM 并在用户的显示器上重新呈现列表。本质上,无中断渲染使 React 能够“多任务”。此功能提供了更流畅的 UI 体验。

并发模式

并发模式是一组功能,可帮助 React 应用程序保持响应并平滑地适应用户的设备和网络速度能力。并发模式将其拥有的任务划分为更小的块。 React 的调度程序可以挑选并选择要执行的作业。作业的调度取决于它们的优先级。通过对任务进行优先级排序,它可以停止琐碎或不紧急的事情,或者进一步推动它们。 React 始终将用户界面更新和渲染放在首位。

使用并发模式,我们可以:

  • 控制首次渲染过程
  • 优先处理渲染过程
  • 暂停和恢复组件的渲染
  • 缓存和优化组件的运行时渲染
  • 隐藏显示内容直到需要展示时

随着 UI 渲染,并发模式改进了对传入数据的响应,懒加载控件,异步处理过程。并发模式保证了用户界面始终处于激活状态,并且持续在后台更新数据,并发模式也始终使用React 的两个钩挂:useTransitionuseDeferredValue

使用useDeferredValue Hook

useDeferredValue Hook 的定义如下:

const deferredValue = useDeferredValue(value, { timeoutMs: <some value> });

此命令设置值在timeoutMs中设置的时间后“滞后”。 用户界面是必须立即更新还是必须等待数据,该命令使用户界面保持激活状态和响应性,该Hook避免了 UI 卡顿,并始终保持用户界面响应,以保持获取数据滞后的较小成本。

使用 Transition Hook

useTransition Hook React 中主要用于挂起的Hook,假设这样的场景下:其中有一个带有用户名按钮的网页。只需点击一个按钮,网页就会在屏幕上显示用户的详细信息。

假设用户首先单击一个按钮,然后单击下一个。屏幕要么变成空白,要么我们在屏幕上看到一个微调器。如果获取详细信息花费的时间太长,用户界面可能会冻结。

useTransition 方法返回两个Hook的值:startTransition isPending。定义的语法如下:

const [startTransition, isPending] = useTransition({ timeoutMs: 3000 });

startTransition 定义的语法:

<button disabled={isPending}
startTransition(() => {
<fetch Calls>
});
</button>
{isPending? " Loading...": null}

使用 useTransition 钩子,React.js 继续显示没有用户详细信息的用户界面,直到用户详细信息准备好,但 UI 是响应式的。React 优先考虑用户界面,以在并行获取数据时保持响应。

为获取数据的Suspense

SuspenseReact与并发模式一起引入的另一个实验性功能。Suspense使组件能够在渲染前等待一段预定的时间。

Suspense的主要作用是从组件异步读取数据,而无需担心数据的来源。Suspense最适合延迟加载的概念。Suspense允许数据获取库通知React数据组件是否可以使用。在必要的组件准备就绪之前,React不会更新 UI。

使用Suspense的好处:

1.数据获取库和React组件之间的集成

2.控制视觉加载状态

3.避免竞争条件

Spinner组件的基本语法如下:


import Spinner from './Spinner';
<Suspense fallback={<Spinner />}>
<SomeComponent />
</Suspense>

Concurrent Mode中使用的Suspense允许耗时的组件在等待数据的同时开始渲染。同时显示占位符。这种组合产生了更流畅的UI体验。

Suspense 和 懒加载组件

React.lazy是一个新功能,它使React.js能够延迟加载组件。懒加载意味着仅在需要时才加载组件(检索和呈现它们的代码)。他们会优先考虑最关键的用户界面组件。React开发人员建议将懒加载组件包装在Suspense组件中。

这样做可确保组件在渲染时不会出现“不良状态”。用户界面在整个过程中保持响应,并带来更流畅的用户体验。

启用并发模式

要启用并发模式,请安装最新的测试版本。安装 React 的先决条件是节点数据包管理器 (npm)。要安装测试版本,请执行以下命令:


npm install react@experimental react-dom@experimental

要测试是否设置了测试版本,请创建一个示例 React 应用程序。没有测试功能的渲染代码如下:

import * as React from 'react';
import { render } from 'react-dom';
render(<App />, document.getElementById('root'));

并发模式的,具体代码如下:


import * as React from 'react';
import { createRoot } from 'react-dom';
createRoot(document.getElementById('root')).render(<App />);

这将为整个应用程序启用并发模式。React 将渲染调用分为两部分:

  1. 创建根元素
  2. 使用渲染调用

目前,React 计划维护三种模式:

  1. 传统模式是向后兼容的传统或当前模式
  2. 阻塞模式是并发模式开发的中间阶段
  3. 并发模式

阻塞模式是使用createBlockingRoot 调用来替换createRoot 调用,在并发模式的开发情况下,阻塞模式为开发者提供了机会来修复bug或解决问题。

React 官方文档中也说明了每种模式支持的功能:

示例应用:

本文也创建了一个测试程序来验证并发模式和其他模式的用法和效果。本文以像素应用为例在150*150的画布上随机分布像素并包含一个搜索框,每次用户点击搜索框时候,画布会重新渲染自己。

即使UI 界面无法在并发模式下渲染,用户输入也不会停止更新。像素画布在处理完成后重新渲染。在传统模式下,快速键入时,UI 会停止,有时会在再次渲染画布之前停止。用户输入也会停止并且不会更新。

构建像素应用程序的主要文件是 canvas.js。我们还制作了一个输入框,用户可以在其中输入任何内容。每次按下一个键都会重新渲染像素画布。

代码示例:

  • Index.js

import React from "react";
import ReactDOM from "react-dom";
import App from "./App";
// Traditional or non-Concurrent Mode react
const rootTraditional = document.getElementById("root");
ReactDOM.render(<App caption="Traditional or Block Rendering" />,
rootTraditional);
// Concurrent Mode enabled
const rootConcurrent = document.getElementById("root-concurrent");
ReactDOM.createRoot(rootConcurrent).render(<App caption="Interruptible
Rendering" />);
  • App.js

import React, { useState, useDeferredValue } from "react";
import "./App.css";
import { Canvas } from "./Canvas";
export default function App(props)
{ const [value, setValue] = useState(""); //This is available only in the Concurrent mode. const deferredValue = useDeferredValue(value, {
timeoutMs: 5000
}); const keyPressHandler = e => {
setValue(e.target.value);
}; return (
<div className="App">
<h1>{props.caption}</h1>
<input onKeyUp={keyPressHandler} />
<Canvas value={deferredValue} />
</div>
);
}
  • Canvas.js
import React from "react";
const CANVAS_SIZE = 70;
const generateRandomColor = () => {
var letters = "0123456789ABCDEF";
var color = "#";
for (var i = 0; i < 6; i++) {
color += letters[Math.floor(Math.random() * 16)];
}
return color;
};
const createCanvas = (rows, columns) => {
let array = [];
for (let i = 0; i < rows; i++) {
let row = [];
for (let j = 0; j < columns; j++) {
row.push(0);
}
array.push(row);
}
return array;
};
//This is the square with the pixels
const drawCanvas = value => {
const canvas = createCanvas(CANVAS_SIZE, CANVAS_SIZE);
return canvas.map((row, rowIndex) => {
let cellsArrJSX = row.map((cell, cellIndex) => {
let key = rowIndex + "-" + cellIndex;
return (
<div
style={{ backgroundColor: generateRandomColor() }}
className="cell"
key={"cell-" + key}
/>
);
});
return (
<div key={"row-" + rowIndex} className="canvas-row">
{cellsArrJSX}
</div>
);
});
};
export const Canvas = ({ value }) => {
return (
<div>
<h2 style={{ minHeight: 30 }}>{value}</h2>
<div className="canvas">{drawCanvas(value)}</div>
</div>
);
};
  • Index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta
name="viewport"
content="width=device-width, initial-scale=1, shrink-to-fit=no"
/>
<meta name="theme-color" content="#000000" />
<title>React App Concurrent Mode</title>
</head>
<body>
<noscript>
You need to enable JavaScript to run this app.
</noscript>
<div id="container">
<div id="root" class="column"></div>
<div id="root-concurrent" class="column"></div>
</div>
</body>
</html>

运行示例

让我们看看我们的代码。我们看到的第一个屏幕是初始屏幕。使用传统或块渲染是现在React 的做法。可中断渲染是并发模式的测试功能。我们先看看传统的渲染工作。

像素画布在每次击键时重新渲染。在传统渲染中,整个 UI 会在每次击键时暂停,直到它可以重新渲染屏幕。在此期间,即使我们继续打字,用户输入不会更新。

下图显示可中断渲染。在可中断渲染中,用户可以继续输入。在为每次击键并行重新渲染画布时,UI 不会停止或停止。

重新渲染完成后,React 会更新 UI。虽然在静态截图中很难看到,但我们可以看到网格在变化,但用户仍然可以打字而不会出现 UI 卡顿的情况。

总结

在本文中,我们研究了 React 的测试并发功能和 Suspense。使用并发模式,React.js 始终保持用户界面响应。它将应用程序的任务分解为更小的块,并允许对用户界面任务进行优先级排序。因此,此模式可提供更流畅和无缝的用户体验,并提高应用程序的整体性能。

结合并发模式,Suspense 允许用户界面保持响应。同时,数据获取等繁重耗时的任务可以并行完成,从而提供整体无缝体验。

有关并发模式的完整详细信息可在 React 官方文档中了解。

随着React版本的改进, React框架越来越被更多的中国前端开发者所熟知并且广泛应用到他们的项目开发中。是继续Vue.js 后又一备受欢迎的前端主流框架,现在也因此衍生除了很多支持与React框架集成的功能工具, 如前端报表ActiveReportsJS控件,提供了与 React 直接集成的在线编辑器和报表展示工具,完善前端的数据展示功能。

React 并发功能体验-前端的并发模式已经到来。的更多相关文章

  1. 设计C/S架构应用程序的并发功能

    C/S架构的ERP.CRM程序有的是以并发点(Concurrency)来销售,并发点是指同时在线人数.并发数量大时,理论上程序的运行速度会慢,软件供应商(vendor)也以控制并发的上限以解决客户对系 ...

  2. threading模块和queue模块实现程序并发功能和消息队列

    简介: 通过三个例子熟悉一下python threading模块和queue模块实现程序并发功能和消息队列. 说明:以下实验基于python2.6 基本概念 什么是进程? 拥有独立的地址空间,内存,数 ...

  3. 自学Python之路-Python并发编程+数据库+前端

    自学Python之路-Python并发编程+数据库+前端 自学Python之路[第一回]:1.11.2 1.3

  4. Xwork概况 XWork是一个标准的Command模式实现,并且完全从web层脱离出来。Xwork提供了很多核心功能:前端拦截机(interceptor),运行时表单属性验证,类型转换,强大的表达式语言(OGNL – the Object Graph NavigationLanguage),IoC(Inversion of Control反转控制)容器等。 ----------------

    Xwork概况 XWork是一个标准的Command模式实现,并且完全从web层脱离出来.Xwork提供了很多核心功能:前端拦截机(interceptor),运行时表单属性验证,类型转换,强大的表达式 ...

  5. 1.2 操作系统的第二个功能——并发功能 -《zobolの操作系统学习札记》

    1.2 操作系统的第二个功能--并发功能 目录 1.2 操作系统的第二个功能--并发功能 问1:什么是并发功能?并发功能是必要的吗? 问2:并发功能必须要求拥有多核CPU吗? 问3:多核CPU和单核C ...

  6. 基于React的PC网站前端架构分析

    代码地址如下:http://www.demodashi.com/demo/12252.html 本文适合对象 有过一定开发经验的初级前端工程师: 有过完整项目的开发经验,不论大小: 对node有所了解 ...

  7. 并发编程学习笔记(10)----并发工具类CyclicBarrier、Semaphore和Exchanger类的使用和原理

    在jdk中,为并发编程提供了CyclicBarrier(栅栏),CountDownLatch(闭锁),Semaphore(信号量),Exchanger(数据交换)等工具类,我们在前面的学习中已经学习并 ...

  8. 和朱晔一起复习Java并发(五):并发容器和同步器

    本节我们先会来复习一下java.util.concurrent下面的一些并发容器,然后再会来简单看一下各种同步器. ConcurrentHashMap和ConcurrentSkipListMap的性能 ...

  9. Java 并发系列之一:java 并发体系

    1.  java 并发机制的底层原理实现 1.1 volatile 1.2 synchronized 1.3 原子操作 2. java 内存模型(JMM) 3. java并发基础线程 4. java ...

随机推荐

  1. C++ string的size()和length()函数没有区别

    C++标准库中的string中两者的源代码如下:      size_type   __CLR_OR_THIS_CALL   length()   const     { //   return   ...

  2. printf/scanf格式

    (1)打印字符 char c; printf("%c",c); (2)打印整形 int i; printf("%d",i); //有符号十进制数 printf( ...

  3. JPEG头部解析

    6.3 JPEG格式       6.3.1简介  微处理机中的存放顺序有正序(big endian)和逆序(little endian)之分.正序存放就是高字节存放在前低字节在后,而逆序存放就是低字 ...

  4. OOP第四章博客

    OOP第四章博客作业 (1)本单元作业架构设计 1)针对于第一次作业,我是将所给类进行了自己的封装,在MyUmlInteraction类里面进行关系的建立,这里把所给的UmlClass建立好,同时有i ...

  5. Java堆的理解

    堆的核心概述 所有的对象实例以及数组都应当在运行时分配在堆上 从实际实用角度看 --"几乎所有的对象实例都在堆中分配内存" 数组和对象可能永远不会存储在栈上,因为栈帧中保存引用,这 ...

  6. centos 7安装freescale交叉编译工具链

    方法1:可以直接下载gcc包,把文件夹放到/usr/local下即可,然后修改PATH环境变量,既可以使用 方法2:可以下载.rpm包,在本地进行安装,下载地址为(http://www.panduod ...

  7. python @staticmethod @classmethod self cls方法区别

    一直在用这些东西,但是又从来没有总结过,正好今日想起来就总结一下这些东西 @staticmethod 静态方法,名义上归属类管理,不能使用类变量和实例变量,类的工具包放在函数前,不能访问类属性和实例属 ...

  8. python类属性和实例属性的访问

  9. Hadoop MapReduce 一文详解MapReduce及工作机制

    @ 目录 前言-MR概述 1.Hadoop MapReduce设计思想及优缺点 设计思想 优点: 缺点: 2. Hadoop MapReduce核心思想 3.MapReduce工作机制 剖析MapRe ...

  10. Step By Step(Lua输入输出库)

    Step By Step(Lua输入输出库) I/O库为文件操作提供了两种不同的模型,简单模型和完整模型.简单模型假设一个当前输入文件和一个当前输出文件,他的I/O操作均作用于这些文件.完整模型则使用 ...