React16源码: Suspense与lazy源码实现

Suspense 与 lazy

1 )概述

  • Suspense 是在 react16.6 提供的新 feature
  • 用于加载的缓冲的内置组件

2 )示例程序

lazy.js

js 复制代码
// lazy.js
import React from 'react'
export default () => <p>Lazy Comp</p>

主程序

js 复制代码
import React, { Suspense, lazy } from 'react'

const LazyComp = lazy(() => import('./lazy.js'))

let data = ''
let promise = ''
function requestData() {
  if (data) return data
  if (promise) throw promise
  promise = new Promise(resolve => {
    setTimeout(() => {
      data = 'Data resolved'
      resolve()
    }, 2000)
  })
  throw promise
}

function SuspenseComp() {
  const data = requestData()
  return <p>{data}</p>
}

export default () => (
  <Suspense fallback="loading data">
    <SuspenseComp />
    <LazyComp />
  </Suspense>
)
  • 示例的效果是先显示的是 loading data,然后再显示出来 Data resolved
  • 最终 export出去的这个组件,它是一个 function component
    • 里面使用了 Suspense,然后给它一个 fallback 的 prop
    • 这就是我们看到的 loading data,里面是一个组件(SuspenseComp),也是一个function component
    • 调用了一个 requestData 方法,它渲染的是一个p标签, 填充 data
  • 可以在一个 Suspense 组件,下面渲染一个或者多个这种异步的组件
    • 有任何一个抛出了 promise以及在这个 promise relove 之前
    • 都会显示这个 fallback里的内容
  • 这就是 Suspense 组件的一个原理
    • 其实它具体的内部实现是非常复杂的
  • 注意,16.6版本的React只支持lazy就是异步加载组件
    • 并没有很好的去支持我们的异步数据加载的功能
    • 相关源码没有稳定,跟数据加载相关的是 Suspense 的功能后期发布
    • 在后面的版本进行使用,当前版本是不推荐在生产环境使用
  • Suspense支持 lazy组件,可以非常方便的实现异步组件加载
    • 配合webpack提供的 import 来实现这个功能
    • 比如代码中, const LazyComp = lazy(() => import('./lazy.js'))
    • 这个 LazyComp 也被放到这个 Suspense 里面
  • 在控制台的 network 面板里面,刷新一下
    • 可以看到bundle.js 然后 2.chunk.js
    • 这就是我们进行了一个异步加载组件,自动给我们区分的一个 js 文件
  • 因为其实我们是在本地开发的,异步加载速度非常快
    • 最终 LazyComp 跟 Data resolved 是一起显示出来的
    • 如果在 Suspense 组件内部有多个组件,要等到所有的组件都resolved之后
    • 它才会把fallback里面的内容移除,这是 Suspense 的一个特点
    • 如果里面任何一个组件处于 pending 状态,它还是会显示 fallback

3 ) 源码探究

  • 定位到 React.js, 找到 Suspense

    js 复制代码
     import {
       REACT_SUSPENSE_TYPE,
     } from 'shared/ReactSymbols';
     import { lazy } from './ReactLazy';
    
     const React = {
       // ... 此处省略其他
       lazy,
       // ... 此处省略其他
       Suspense: REACT_SUSPENSE_TYPE
     }
    • 看到 Suspense它是一个常量,就是一个 REACT_SUSPENSE_TYPE,本质上只是一个Symbol
    • 而 lazy 就不是一个Symbol 了
    • 我们可以看到 import { lazy } from './ReactLazy';
  • 基于这个,定位到 ReactLazy.js 查看 lazy 源码

    js 复制代码
    /**
     * Copyright (c) Facebook, Inc. and its affiliates.
     *
     * This source code is licensed under the MIT license found in the
     * LICENSE file in the root directory of this source tree.
     */
    
    import type {LazyComponent, Thenable} from 'shared/ReactLazyComponent';
    
    import {REACT_LAZY_TYPE} from 'shared/ReactSymbols';
    
    export function lazy<T, R>(ctor: () => Thenable<T, R>): LazyComponent<T> {
      return {
        $$typeof: REACT_LAZY_TYPE,
        _ctor: ctor,
        // React uses these fields to store the result.
        _status: -1,
        _result: null,
      };
    }
  • 它是一个方法 export function lazy,并接收一个方法

    • lazy<T, R>(ctor: () => Thenable<T, R>): LazyComponent<T>
    • 它指定这个传入的参数,这个参数是一个方法,并且它要返回一个 Thenable 类型的对象
    • Thenable 是 Promise 这样的一个对象,它具有 .then 方法
  • 最终,返回的是一个 lazy component

    • $$typeof,值为 REACT_LAZY_TYPE
    • _ctor 就是我们传递进来的回调函数
      • 在react渲染的过程当中,渲染到 lazy component 的时候
      • 会去调用这个 _ctor 回调,并返回一个 Thenable 的对象(Promise对象)
      • 这个时候Promise是处于 pending 状态的,对应的是值是 -1
    • _status 用来记录当前这个 Thenable 的对象,所处的状态
      • 后续达到了resolved 或者是 rejected 的这两种状态的时候,这个 _status 会变化
      • 让后期进行渲染的时候可以进行不一样的处理
    • _resultPromise结果
      • 用来记录这个Thenable对象,它 resolve 之后返回的那个属性
      • 在 lazy 里面,最终的组件会挂载到这个 _result
      • 后续渲染这个 lazy 组件的时候,直接拿 _result 里面的组件渲染就可以了
相关推荐
负责的蛋挞1 小时前
异步HttpModule的实现方式
java·服务器·前端
丹宇码农3 小时前
把 HLS 字幕玩出花:zwPlayer 如何让 M3U8 视频支持全文搜索、翻译与码率自适应
前端·javascript·音视频·hls·视频播放器
2501_943782354 小时前
【共创季稿事节】猜数字游戏:二分法思维与交互式反馈
前端·游戏·microsoft·harmonyos·鸿蒙·鸿蒙系统
GV191rLvq4 小时前
基于Socket实现的最简单的Web服务器【ASP.NET原理分析】
服务器·前端·asp.net
吠品4 小时前
LangChain 里 tool_call_id 为空?一次 MCP 工具集成的排查记录
前端
微信开发api-视频号协议4 小时前
企业微信二次开发中的文件系统设计:媒体资源、临时文件与业务附件
前端·微信·企业微信·媒体·ipad·微信开放平台
柒和远方4 小时前
Phase 7.4 学习博客:为什么多 API 项目需要 Swagger / OpenAPI
前端·后端·架构
张龙6874 小时前
拼多多开放平台对接踩坑实录:从 CLIENT_ID 配置到 MD5 签名算法的完整填坑指南
前端
GuWenyue4 小时前
提示词彻底过时?一套上下文工程方案,3步让LLM落地生产,代码直接复用
前端·javascript·人工智能