React Loadable:给 React 应用做代码分割的老牌方案

文章目录

  • React Loadable:给 React 应用做代码分割的老牌方案
    • 1、 它解决什么问题
    • 2、 基本用法
    • 3、 loading 组件不只是个spinner
    • 4、 预加载与多资源并行
    • 5、 服务端渲染这块
    • 6、 现在还得用它吗

React Loadable:给 React 应用做代码分割的老牌方案

react-loadable 在 GitHub 上拿到了 16.5K Star。

jamiebuilds 出品的这个库只干一件事:用动态 import 把组件拆成单独的 bundle,按需加载。API 简洁,配套了服务端渲染和 Babel 插件,在 React 16 时代是代码分割的主流选择之一。

1、 它解决什么问题

Webpack 打包 React 应用,默认会把所有代码塞进一个 bundle。项目小没问题,业务一上场,bundle 体积轻松冲到几百 KB,首屏白屏时间跟着涨。

代码分割就是把那一个大包拆成多个小包,按页面或组件粒度切分,用到啥加载啥。Webpack 2+ 原生支持import()语法做动态加载,但手写一遍要处理加载状态、错误重试、超时、服务端渲染兼容,啰嗦得很。

react-loadable 把这层麻烦封装成一个高阶组件。传入loader和一个loading组件,剩下的它管。

2、 基本用法

安装:

yarnaddreact-loadable

最小例子:

import Loadable from 'react-loadable'; import Loading from './my-loading-component'; const LoadableComponent = Loadable({ loader: () => import('./my-component'), loading: Loading, }); export default class App extends React.Component { render() { return <LoadableComponent/>; } }

loader返回一个 Promise,解析出来的模块就是LoadableComponent渲染的内容。加载过程中显示Loading组件。

3、 loading 组件不只是个spinner

loading组件会收到三个状态 prop:errorpastDelaytimedOut

  • error:loader 抛错时拿到 Error 对象,可以渲染重试按钮
  • pastDelay:超过 delay 阈值(默认 200ms)才为 true,避免加载太快闪一下"Loading"
  • timedOut:超过 timeout 阈值才为 true,提示用户网络卡了

完整模板:

function Loading(props) { if (props.error) { return <div>Error! <button onClick={props.retry}>Retry</button></div>; } else if (props.timedOut) { return <div>Taking a long time... <button onClick={props.retry}>Retry</button></div>; } else if (props.pastDelay) { return <div>Loading...</div>; } else { return null; } }

delaytimeout都可以在Loadable()配置里改。

4、 预加载与多资源并行

LoadableComponent.preload()是静态方法,可以在用户 hover 按钮时提前触发import(),等真正渲染时 chunk 已经在缓存里。

Loadable.Map用来并行加载多个资源:组件、JSON 数据、CSS 都能塞。render方法必填,它会把loaded对象交给你自己组装。

Loadable.Map({ loader: { Bar: () => import('./Bar'), i18n: () => fetch('./i18n/bar.json').then(res => res.json()), }, render(loaded, props) { let Bar = loaded.Bar.default; let i18n = loaded.i18n; return <Bar {...props} i18n={i18n}/>; }, });

5、 服务端渲染这块

服务端没有import()可用,react-loadable 的做法是:

  1. 启动服务前调Loadable.preloadAll()把所有动态模块全部加载完
  2. 渲染时用<Loadable.Capture>收集这次请求实际渲染了哪些模块
  3. 用 Webpack 插件ReactLoadablePlugin生成react-loadable.json,记录模块到 bundle 的映射
  4. 服务端getBundles(stats, modules)拿到这个请求需要的 bundle 列表,注入<script>标签
  5. 客户端入口调Loadable.preloadReady()等已加载的 bundle 就绪,再ReactDOM.hydrate

配套 Babel 插件react-loadable/babel会自动注入moduleswebpack两个字段,免去手动写require.resolveWeak

6、 现在还得用它吗

React 16.6 之后官方出了React.lazySuspense,覆盖了 react-loadable 大部分客户端场景,API 更简洁。但 react-loadable 仍有它的位置:服务端渲染的 bundle 收集逻辑、pastDelaytimedOut这种细粒度状态、还有历史项目兼容,这些都是官方 lazy 没给的。

新项目用React.lazy即可;老项目迁不动、或者需要服务端 bundle 精确注入的,react-loadable 还能继续干。BBC News、Cloudflare、Atlassian、Tinder、Unsplash 这些公司在用户名单里。

社区也衍生了react-loadable-visibility(基于可视区域加载)和react-loadable-ssr-addon(增强 SSR)两个扩展库。

、Unsplash 这些公司在用户名单里。

社区也衍生了react-loadable-visibility(基于可视区域加载)和react-loadable-ssr-addon(增强 SSR)两个扩展库。