文章目录
- 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:error、pastDelay、timedOut。
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; } }delay和timeout都可以在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 的做法是:
- 启动服务前调
Loadable.preloadAll()把所有动态模块全部加载完 - 渲染时用
<Loadable.Capture>收集这次请求实际渲染了哪些模块 - 用 Webpack 插件
ReactLoadablePlugin生成react-loadable.json,记录模块到 bundle 的映射 - 服务端
getBundles(stats, modules)拿到这个请求需要的 bundle 列表,注入<script>标签 - 客户端入口调
Loadable.preloadReady()等已加载的 bundle 就绪,再ReactDOM.hydrate
配套 Babel 插件react-loadable/babel会自动注入modules和webpack两个字段,免去手动写require.resolveWeak。
6、 现在还得用它吗
React 16.6 之后官方出了React.lazy加Suspense,覆盖了 react-loadable 大部分客户端场景,API 更简洁。但 react-loadable 仍有它的位置:服务端渲染的 bundle 收集逻辑、pastDelay与timedOut这种细粒度状态、还有历史项目兼容,这些都是官方 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)两个扩展库。