React的Suspense功能,简单说就是让组件渲染遇到需要异步操作的时候,可以无缝地“悬停”(suspense)一下,等到这个异步操作有结果的时候,再无缝地继续下去。

这里所说的异步操作,可以分为两类:

  • 异步加载代码
  • 异步加载数据

很好辨认,写程序嘛,折腾的无外乎就是“代码”和“数据”这两样东西。

为什么要异步加载代码呢?

本来,代码打包成一个文件就行,但是当代码量很庞大,而且也不是所有代码都是页面加载的时候就用得上,把他们拉进唯一的打包文件,除了打包过程简单没有任何好处。所以,为了榨取性能,就要考虑把代码打包成若干文件,这样每个打包文件就可以比较小,根据需要来加载。这是一个好主意,不过让每个开发人员都实现这套机制也是够扯的,所以,React就用Suspense提供了统一的无缝的代码分割(Code Splitting)兼异步加载方法,在v16.6.0就实现了这样的Suspense功能。

大家有兴趣自己去玩,这种Suspense不是今天要讲的重点,今天要讲的是“异步加载数据”的Suspense,也就是利用Suspense来调用服务器API之类的操作。

根据React官方的路线图,利用Suspense来做数据加载,要等到今年(2019)的中期才发布,你如果看到这篇文章比较晚,可能已经发布了。

今天要说的,是Suspense做数据加载,和React v16的重头戏异步渲染有一点矛盾的地方

在之前的Live 《深入理解React v16新功能》中我说过,从React v16开始,一个组件的生命周期可以分为两个阶段:render阶段+commit阶段。在render阶段的生命周期函数,因为Fiber的设计特点,可能会被打断,被打断之后,会重新被调用;而commit阶段一旦开始,就绝不会被打断。render阶段和commit阶段的分界线是render函数,注意,render函数本身属于render阶段。

举个例子,一个组件被渲染,执行到render函数里面,这时候用户突然在某个input控件里输入了什么,这时候React决定去优先处理input控件里的按键事件,就会打断这个组件的渲染过程,也就是不管render返回啥,渲染过程都就此打住,不画了,专心去处理input控件的事情去了。等到那边的事情处理完,再来渲染这个组件,但是这时候从原来位置重新开始,那肯定是不靠谱的,因为刚才的按键事件处理可能改变了一些状态,为了保证绝对靠谱,React决定……还是从头走一遍吧, 于是,重新去调getDerivedStateFromProps、shouldComponentUpdate然后调用render。

看到没哟,render之前的生命周期函数都会被调用,而且,因为这种“打断”是完全是不可预期的,所以,现在就要求在render阶段的所有生命周期函数不要做有副作用的操作

什么叫副作用?就是纯函数不该做的操作。

什么叫纯函数?就是除了根据输入参数返回结果之外,不做任何多与事情的操作。

如果一个函数修改全局变量,那就不是一个纯函数;如果一个函数修改类实例状态,那就不是一个纯函数;如果一个函数抛出异常,那就不是一个纯函数;如果一个函数通过AJAX访问服务器API,那就不是一个纯函数。

就拿访问服务器API为例,假如render阶段的生命周期函数做了访问服务器API的AJAX操作,那么,很有可能产生连续对服务器的访问,因为异步渲染下render阶段会被打断而重复执行啊。

class Foo extends React.Component {shouldComoponentUpdate() {callAPI(); // 你只看到了一行代码,但是可能会被多次调用return true;}render() {callAPI(); // 你只看到了一行代码,但是可能会被多次调用return JSX;}
}

再说一遍,在render阶段的所有生命周期函数不要做有副作用的操作,这些函数必须是纯函数。

那么,现在问题来了,使用Suspense来获取数据,会不会违反者这个规定呢?

虽然Suspense这方面的API还没有确定,但是代码形式还是明确的,利用试玩版的react-cache展示一下。

import React, { Suspense } from "react";
import { unstable_createResource as createResource } from "react-cache";// 模拟一个AJAX调用
const mockApi = () => {return new Promise((resolve, reject) => {setTimeout(() => resolve("Hello"), 1000);});
};const resource = createResource(mockApi);const Greeting = () => {// 注意看这里,这里依然是在render阶段,可能会被重复调用哦!const result = resource.read();return <div>{result} world</div>;
};const SuspenseDemo = () => {return (<Suspense fallback={<div>loading...</div>}><Greeting /></Suspense>);
};export default SuspenseDemo;

这里就有意思了,使用Suspense来获取数据,既然数据是在render函数(或者像上面例子一样在函数类型组件中)使用,那么获取数据的过程肯定是在render阶段,但是,获取数据的过程是要调用AJAX的啊,AJAX是副作用操作,这不就和“render阶段不能做有副作用操作“的规定矛盾了吗?

简单说来,就是降低了要求,只需要render阶段的操作是”幂等“(indempotent)就可以了。

所谓幂等,就是一次调用和N次调用产生一样的结果。

还是举例来说吧。

// 这是纯函数,没有副作用
function foo1(a, b) {return a + b;
}// 这不是纯函数,有副作用,而且不幂等
function foo2(a, b) {sendAjax(a + b);return a + b;
}// 这不是纯函数,有副作用,但是——幂等
let called = false;
function foo3(a, b) {if (!called) {sendAjax(a + b);}called = true;return a + b;
}

上面的foo3这个函数,的确有副作用,但是,利用代码巧妙地防一手,只让第一次调用发出AJAX,之后的调用就不发AJAX了,这样,调用多少次,产生的效果都一样,这就是”幂等“。

幂等虽然没有纯函数那么纯,但是也足够好了,至少对于React这样无法做到”纯“的框架,这也是最好的结果。

试玩版react-cache的unstable_createResource函数,接受一个返回Promise的函数为参数,获取的Promise是会被cache住的,所以,虽然会被打断重复多次,resource.read()如果有返回结果,那么返回的结果都是一样 ,也就达到了”幂等“的效果。

这么一看,矛盾也就解决了。

总结一下,实际上我们要把”在render阶段的所有生命周期函数不要做有副作用的操作,这些函数必须是纯函数“这个要求改一下,改成”在render阶段的所有生命周期函数都应该幂等“。

虽然一说React往往都说要说“函数式编程”,但是React真的先天和崇尚纯函数的“函数式编程”有很大距离,包括Hooks,表面上看推崇函数式组件,似乎是向函数式编程迈进了一步,但是,所有的Hooks函数都是有状态的,怎么能算纯函数呢。

当然,有洁癖一样追求纯函数也没有必要,既然“幂等”能够解决问题,我们也乐见其成。

如果你想开发小程序或者了解小程序更多的内容,可以通过第三方专业开发平台,来帮助你实现开发需求:厦门在乎科技-专注厦门小程序开发、app开发、网站开发、H5小游戏开发

React Supense和React 异步渲染的一点矛盾相关推荐

  1. 不挂载 组件渲染_让你的 React 组件性能跑得再快一点「实践」

    作者:天泽 转发链接:https://www.zoo.team/article/react-render 性能和渲染(Render)正相关 React 基于虚拟 DOM 和高效 Diff 算法的完美配 ...

  2. [译] Don’t call me, I’ll call you:使用 Redux-Saga 管理 React 应用中的异步 action (上)...

    原文地址:Don't call me, I'll call you: Side effects management with Redux-Saga (Part 1) 原文作者:David Dvora ...

  3. 【React学习】React更新渲染原理

    当我们调用 setState 之后发生了什么?react经历了怎样的过程将新的 state 渲染到页面上? 一次react更新,核心就是对虚拟dom进行diff,找出最少的需要变化的dom节点,然后对 ...

  4. [react] 什么是浅层渲染?

    [react] 什么是浅层渲染? 当为 React 写单元测试时,浅层渲染(Shallow Renderer) 会变得十分有用.浅层渲染使你可以渲染 "单层深度" 的组件,并且对组 ...

  5. [react] React怎样跳过重新渲染?

    [react] React怎样跳过重新渲染? 生命周期 shouldComponentUpdate return false ? 个人简介 我是歌谣,欢迎和大家一起交流前后端知识.放弃很容易, 但坚持 ...

  6. [react] 如何提高组件的渲染效率呢?

    [react] 如何提高组件的渲染效率呢? 类组件: 1.继承PureComponent 2.使用shouldComponentUpdate优化 函数组件: 1.memo模拟PureComponent ...

  7. [react] 怎样有条件地渲染组件?

    [react] 怎样有条件地渲染组件? {condition && <Component />} or {condition ? <Component /> : ...

  8. react native text换行_基于React+Koa实现React SSR服务端渲染

    React Server-Side Rendering 其实这个概念很早之前就有了解了,出于没有应用场景原因,之前一直都只停留在了解API的层面,未曾去实践.快到周末闲来无事,自己复盘了下之前做的新商 ...

  9. 使用React和axios设置服务器端渲染的最简单方法

    by Simone Busoli 通过西蒙娜·布索利(Simone Busoli) 使用React和axios设置服务器端渲染的最简单方法 (The easiest way to set up ser ...

最新文章

  1. Markdown学习之路
  2. 13_MFC工具条和状态栏
  3. java反射(reflect)机制模拟javabean的实现
  4. Vue.js实现前段评论展示
  5. javascript 之 面向对象【理解对象】
  6. Ext JS - Combobox 加载下拉框数据 级联下拉框
  7. 安装漂亮的Faenza1.3与Faience0.5图标主题
  8. ppap文件过程流程图制作_收藏 | 据说PPAP的精华都在这个PPT里!一起来鉴定下
  9. 窗体控件绑定数组 c# 1613698204
  10. defineProperty AND defineProperties
  11. swift中变量的几种类型
  12. 余额宝宣布开放 中欧基金首批入驻
  13. java dateutils详解_java DateUtils
  14. 【数理统计】显著性检验
  15. python tk/ttk制作 安卓群控助手,多台设备多任务多线程执行
  16. Postman之Pre-request Script 使用详解
  17. iOS开发中Touch ID的使用
  18. aes hex 加密
  19. java文档注释用什么开头,极其重要
  20. mov ah,4ch int 21的作用

热门文章

  1. 正则表达式学习日记_《学习正则表达式》笔记_Mr_Ouyang
  2. 基于javaweb的小区停车收费系统-计算机毕业设计
  3. 【Typora篇】Calibre软件运行报错QT解决方案
  4. 几个流量控制算法总结
  5. Android打开系统自带文件管理器,选择指定类型的文件
  6. [USACO21DEC] Air Cownditioning B(差分)
  7. 57_解决adb一直重启
  8. nodejs+vue的地方美食分享网站
  9. 21亿春节红包撒出去,能缓解快手的流量焦虑吗?
  10. ajax请求php返回xml数据格式,ajax传输的数据格式(XML,json)怎么获取解析