放心 使用 React.lazy:在快速迭代时安全加载组件的方法
React.lazy 是一种按需加载组件并提升应用性能的绝佳方式。然而,有时它可能会导致一些问题,比如"ChunkLoadError"和"Loading chunk failed"。
困境
如今,在受欢迎的“快速行动并打破常规”的理念下,软件开发正在加速前进。这不是批判——只是事实而已。然而,这种快速节奏有时会导致问题,尤其是在 React 中加载组件时。
如果你正在一个使用 React.lazy 按需加载组件的项目中工作,可能已经遇到过一些问题,比如 ChunkLoadError
和 Loading chunk failed
。以下是一些可能的原因:
- 网络问题,例如,用户的网络连接速度慢或不稳定。
- 用户使用的是应用程序的旧版本,浏览器尝试加载一个已经不存在的 chunk。
通常,简单刷新页面可以解决问题,但这不是一种很好的用户体验。想象一下,当用户导航到另一个路由时,出现一个白屏——这对你的应用来说不是一个很好的形象。
我们能否在追求速度的同时兼顾用户体验的顺畅度呢?当然可以。让我向你展示如何做到(当然是使用 TypeScript)。
解决方案
一种蛮力解决方案是在服务器上保存所有版本的 chunk,这样就不会再有“missing chunk”问题了。随着应用规模的增长,由于磁盘空间需求的增加,这种解决方案可能变得不可行,并且它仍然无法解决网络问题。
考虑到重试或刷新可以解决该问题,我们可以在代码中实现这些解决方案。由于该问题通常发生在用户导航到另一个路由时,我们甚至可以在用户没有注意到的情况下解决它。我们需要做的只是围绕 React.lazy
函数构建一个包装器来处理重试和刷新。
已经有一些关于如何实现这种解决方案的优秀文章,因此我将重点放在解决方案的思想和内部工作原理上。
创建包装器
第一步是创建一个围绕 React.lazy
函数的包装器:
处理重试
对于网络问题,我们可以通过将 importFunction
包裹在 tryImport
函数中来处理重试:
看起来很简单,对吧?你还可以实施 指数退避 算法,以更有效地处理重试。
处理刷新
对于旧版本问题,我们可以通过捕获错误并刷新页面来处理:
然而,这种实现非常危险,因为当错误无法通过刷新解决时,它可能导致无限循环刷新。同时,在刷新期间应用状态将会丢失。因此我们需要 sessionStorage
的帮助来存储我们已经尝试刷新页面的消息:
现在,当我们从 safeLazy
函数捕获到错误时,我们知道它是在刷新时无法解决的问题。
同一页面上的多个懒加载组件
当前实现还有一个隐藏的陷阱。如果在同一页面上有多个懒加载组件,无限刷新循环仍然可能发生,因为其他组件可能会重置 sessionStorage
值。为了解决这个问题,我们可以为每个组件使用一个唯一的 key:
现在,每个组件都有自己唯一的 sessionStorage
key,并且可以避免无限刷新循环。我们还可以继续对解决方案进行挑剔,例如:
- 将所有 key 收集到一个数组中,这样只需要一个存储 key。
- 设置刷新限制,以允许刷新页面多次而不是仅一次后抛出错误。
但我想你已经明白了这个思路。一个带有测试和配置的全 面 TypeScript 解决方案可在 GitHub repository 中找到。我还在 NPM 上发布了 react-safe-lazy
包,你可以立即在你的项目中使用它。
结论
软件开发是一项细致的工作,甚至最小的细节都需要付出努力来解决。我希望这篇文章能帮助你优雅地处理 React.lazy
中的问题,并改善你的应用的用户体验。