React组件实现动态导入的几种方案

1. 背景

在React应用中,当我们使用这样的语句导入某个模块(例如:一个组件)的时候,就称之为静态导入

TypeScript 复制代码
import sth from 'module1'

凡是被静态导入的模块,都会被打包到初始的bundle里面

如果一个应用内所有模块都使用静态导入,会导致

  • 初始bundle的体积较大
  • 浏览器加载bundle的速度较慢

因此:针对不需要被包含在首次渲染的模块,可以考虑使用动态导入,这样可以减少初始bundle的体积,从而提升首次渲染的性能

以组件为例,符合以下特征的组件其实都不需要被包含在首次渲染中

  • 依赖用户交互行为的 -> Import on interaction
  • 首屏不可见的 -> Import in viewport
  • 不属于当前路由的 -> Route based splitting

2. Import on interaction

发生交互行为后(例如点击按钮),再加载组件

2.1 React Lazy

使用React提供的lazy导入组件

  • 使用lazy函数导入组件
  • 使用Suspense对组件进行包裹,并设置fallback来展示组件加载中的状态
TypeScript 复制代码
import { Suspense, lazy, useState } from 'react'
import ComponentA from './ComponentA'
import ComponentB from './ComponentB'

const ComponentB = lazy(() => import('./ComponentB'))

export default function () => {
  const [visible, setVisible] = useState(false)
  
  function toggeleVisible() {
    setVisible(v => !v)
  }
  
  return (
    <Suspense fallback={<p>loading...</p>}>
      <ComponentA />
      <button onClick={toggleVisible}>click me to toggle ComponentC</button>
      {
        visible && <ComponentC />
      }
    </Suspense>
  )
}

2.2 import()

EsModule提供的import函数返回的是一个promise,值为module

通过import函数拿到组件的引用后,可以通过createElement的方式创建一个jsx元素进行渲染

TypeScript 复制代码
import { createElement, useState } from 'react'
import ComponentA from './ComponentA'

export default function () => {
  const [componentC, setComponentC] = useState(null)
  
  function showComponentC() {
    import('./ComponentC')
      .then(module => module.default)
      .then(component => {
        setComponentC(createElement(component))
      })
  }
  
  function hideComponentC() {
    setComponentC(null)
  }
  
  return (
    <>
      <ComponentA />
      <button onClick={() => {
        if (componentC) {
          hideComponentC()
        } else {
          showComponentC()
        }
      }}>
        click me to toggle ComponentC
      </button>
      {componentC}
    </>
  )
}

需要注意的是,使用Babel的时候,需要用@babel/plugin-syntax-dynamic-import插件确保Babel能够进行正确的转译

3. Import in viewport

当组件对应的UI进入视窗内的时候,再加载组件

下面提到的这两个库都是基于IntersectionObserver这个API实现的

3.1 react-loadable-visibility

使用 react-loadable-visibility

TypeScript 复制代码
import LoadableVisibility from "react-loadable-visibility/react-loadable"
import ComponentA from './ComponentA'
import ComponentB from './ComponentB'

const ComponentC = LoadableVisibility({
  loader: () => import('./ComponentC')
  loading: <p>Loading...</p>
})

const App = () => {
  return (
    <>
      <ComponentA />
      <ComponentB />
      <ComponentC />
    </>
  )
}

export default App

3.2 react-lazyload

使用react-lazyload

TypeScript 复制代码
import LazyLoad from 'react-lazyload'
import ComponentA from './ComponentA'
import ComponentB from './ComponentB'

const App = () => {
  return (
    <>
      <ComponentA />
      <ComponentB />
      <LazyLoad height='300px'>
        <ComponentC />
      </LazyLoad>
    </>
  )
}

export default App

相比于react-loadable-visibility,react-lazyload的功能更加强大,因为它提供了更多的API,让开发者能够通过这些API进行灵活的自定义配置,例如:

  • offset:如果需要提前加载组件,例如在视窗下边缘距离组件100px时就进行加载,可以设置offset为100

  • placeholder:组件未加载时的占位符

4. Route based splitting

基于路由来分割bundle,每个路由对应的根组件单独生成一个bundle

TypeScript 复制代码
import { RouteConfig } from 'react-router-config'
import { lazy } from 'react'

export const routes: RouteConfig[] = [
  {
    path: '/list',
    component: lazy(() => import('./src/List'))
  },
  {
    path: '/detail',
    component: lazy(() => import('./src/Detail'))
  }
]

参考资料

相关推荐
web1478621072333 分钟前
C# .Net Web 路由相关配置
前端·c#·.net
m0_7482478034 分钟前
Flutter Intl包使用指南:实现国际化和本地化
前端·javascript·flutter
飞的肖37 分钟前
前端使用 Element Plus架构vue3.0实现图片拖拉拽,后等比压缩,上传到Spring Boot后端
前端·spring boot·架构
青灯文案11 小时前
前端 HTTP 请求由 Nginx 反向代理和 API 网关到后端服务的流程
前端·nginx·http
m0_748254881 小时前
DataX3.0+DataX-Web部署分布式可视化ETL系统
前端·分布式·etl
用户30587584891251 小时前
Connected-react-router核心思路实现
react.js
ZJ_.1 小时前
WPSJS:让 WPS 办公与 JavaScript 完美联动
开发语言·前端·javascript·vscode·ecmascript·wps
GIS开发特训营1 小时前
Vue零基础教程|从前端框架到GIS开发系列课程(七)响应式系统介绍
前端·vue.js·前端框架·gis开发·webgis·三维gis
Cachel wood2 小时前
python round四舍五入和decimal库精确四舍五入
java·linux·前端·数据库·vue.js·python·前端框架
学代码的小前端2 小时前
0基础学前端-----CSS DAY9
前端·css