在2019中进行代码拆分

是2019年! 每个人都认为他们知道代码拆分。 所以-让我们仔细检查!



代码拆分代表什么?


简而言之-代码拆分几乎不会加载整个东西。 然后,您正在阅读此页面,而不必加载整个网站。 当您从数据库中选择一行时-您不必全部使用。
明显吗? 代码拆分也很明显,不仅涉及数据,还涉及代码。


谁(什么?)正在拆分代码?


React.lazy吗? 不-它只使用它。 代码拆分是在捆绑程序级别上完成的-Webpack,宗地,或者只是您的文件系统,以防使用“本地” esm模块。 代码拆分只是文件,可以在“后期”加载的文件。 所以-对于“ 什么是增强代码拆分能力? ”的问题-答案是-“捆绑程序”。


谁在使用代码拆分?


React.lazy正在使用。 仅使用捆绑程序的代码拆分。 渲染时只调用import。 仅此而已。


什么是React-loadable?


React.lazy取代了它。 并提供了更多功能,例如Suspense来控制加载状态。 所以-使用React.Lazy代替。


是的,仅此而已。 感谢您的阅读,并祝您愉快。

为什么文章没有完成?


好吧 我忘了提到关于React.lazy和代码拆分的一些灰色区域。


灰色区域1-测试


由于React.lazy异步性,要测试它并不容易。 只要尚未加载(即使已加载),它就是“空”- Promisesimport返回,并且懒惰地接受, 保证 ,总是在下一个tick中执行。


建议的解决方案? 您不会相信,但是建议的解决方案是使用同步容器 - 请参阅pull request 。 所以-让我们的imports同步! (以解决测试或其他服务器端情况的延迟问题)


 const LazyText = lazy(() => ({ then(cb) { cb({default: Text}); // this is "sync" thenable }, })); const root = ReactTestRenderer.create( <Suspense fallback={<Text text="Loading..." />}> <LazyText text="Hi" /> // this lazy is not very lazy </Suspense>, ); 

将导入函数转换为记忆同步同步并不难。


 const syncImport = (importFn) => { let preloaded = undefined; const promise = importFn().then(module => preloaded = module); // ^ "auto" import and "cache" promise return () => preloaded ? { then: () => preloaded } : promise; // ^ return sync thenable then possible } const lazyImport = isNode ? syncImport : a => a; // ^ sync for node, async for browser const LazyComponent = React.lazy(lazyImport(() => import('./file')); 

灰色区域2-SSR


如果您不需要SSR,请继续阅读本文!

React.lazy是SSR友好的。 但这需要Suspense起作用,并且Suspense 不是服务器端友好的


有两种解决方案:


  • 例如,通过嘲讽将Suspense替换为Fragment。 然后,将更改后的import版本与import一起使用, then使lazy也同步运行。
     import React from 'react'; const realLazy = React.lazy; React.lazy = importer => realLazy(syncImport(importer)); React.Suspense = React.Fragment; // :P // ^ React SSR just got fixed :D 

这是一个不错的选择,但它对客户端不十分友好。 怎么了 让我们定义第二种可能的解决方案:


  • 在React水合之前,使用专门的库来跟踪使用的脚本,块和样式,并将它们加载到客户端(尤其是样式!)上。 否则-您将呈现空洞而不是代码拆分的组件。 再说一遍-您没有加载刚刚拆分的代码,因此您无法渲染任何内容。

看代码拆分库


  • 通用组件 -最古老且仍可维护的库。 它从“教导” Webpack到代码拆分方面“发明”了代码拆分。
  • React-loadable-非常流行,但是没有维护。 使代码随处可见。 问题已经关闭,因此周围没有社区。
  • 可加载组件 -功能齐全的库,很高兴与周围最活跃的社区一起使用。
  • 导入的组件 -不绑定到Webpack的单个库,即能够处理包裹或esm。
  • React-async-component-已经死了的库(至今很流行),它对代码拆分,自定义React树遍历和SSR周围的一切都产生了重大影响。
  • 另一个库-有很多库,其中许多库都无法在Webpack演进或React 16中生存-我没有在这里列出它们,但是如果您知道一个不错的候选人-DM我。

选择哪个图书馆?


它很容易- 不可加载 -它笨重且无法维护,即使它仍然非常流行也是如此。 (再次感谢您推广代码拆分)


可加载组件 -可能是一个很好的选择。 它编写得很好,积极维护并支持所有现成的东西。 支持“完全动态导入”,允许您根据给定的道具导入文件,但无法键入。 支持Suspense,因此可以替换React.lazy。


通用组件 -实际上是完全动态导入的“发明人”-他们在Webpack中实现了它。 还有许多其他低级别的东西-他们做到了。 我会说-这个库有点硬核,对用户的友好程度也不高。 可加载组件文档无与伦比。 如果不使用该库,那么值得阅读文档,这是值得的-您应该知道很多细节...


React-imported-component-有点奇怪。 它是独立于捆绑器的,因此它永不中断(没有中断),可以与Webpack 5和55一起使用,但这需要付出一定的代价。 虽然在SSR期间以前的库会将所有使用的脚本添加到页面正文中,但是您将能够并行加载所有脚本-导入的文件名不知道,并且将调用原始的“导入”(这就是为什么捆绑独立的)加载已使用的块,但只能从主捆绑包内部进行调用-因此,所有其他脚本只有在下载并执行主脚本后才能加载。 不支持像React.lazy这样的完全动态导入,因此不支持输入。 还支持暂挂。 在SSR上使用同步对象 。 对于CSS,它也有完全不同的方法,并提供完善的流呈现支持。


列出的库之间在质量或受欢迎程度上没有区别,我们都是好朋友-因此,请放心选择。


灰色区域3-混合渲染


SSR是一件好事,但是,很难。 小型项目可能希望拥有SSR-有很多理由拥有它-但他们可能不想设置和维护它。


SSR可能真的非常困难。 如果想快速获胜,请尝试razzle或使用Next.js。

因此,我最简单的SSR解决方案(尤其是简单SPA的解决方案)是预渲染。 就像在浏览器中打开SPA并点击“保存”按钮。 像:


  • React-snap-使用puppeteer (又名无头Chrome)在“浏览器”中呈现页面并将结果保存为静态HTML页面。
  • Rendertron-执行相同的操作,但以不同的方式( )进行。

没有“服务器”的预渲染是“ SSR”。 它是使用客户端的SSR。 魔术! 开箱即用……但不是为了代码随地吐痰。
因此,您只是在浏览器中呈现了页面,保存了HTML,并要求加载相同的内容。 但是没有使用服务器端特定代码(收集所有使用的块),因为没有服务器



在上一部分中,我已经指出了与Webpack绑定的库,这些库在收集有关已用块的信息方面完全无法处理混合渲染。


react-snap部分支持可加载组件的版本2(与当前版本5不兼容)。 支持已经消失了。

只要没有绑定到bundler /端,react-imported-component就可以处理这种情况,因此对于SSR或Hybrid,只要支持“状态水合作用”,就没有区别,仅对于react-snap,而rendertron则没有。


在撰写本文期间发现了react-imported-component的这种功能,以前未知,请参见示例 。 这很容易。

在这里,您必须使用另一种解决方案,该解决方案垂直于所有其他库。


React组件


该库是为部分水合而创建的,可以对应用程序进行部分水合,其余部分仍保持脱水状态。 它适用于SSR和Hybrid渲染器,没有任何区别。
这个想法很简单:


  • SSR期间-渲染组件,并用<div />包裹
  • 在客户端上-找到该div,并使用innerHTML,直到Component准备好替换无效的HTML。
  • 您不必加载,并等待一个带有拆分组件的块,然后hydrate不呈现白洞而不是呈现它 -只需使用预渲染的HTML,该HTML绝对等于一个真实组件可以渲染的HTML,并且已经存在-它带有服务器(或液压)响应。

这就是为什么我们必须等待所有块加载后才能水化- 匹配服务器渲染的HTML。 这就是为什么我们可以使用服务器渲染的HTML片段,直到客户端未准备好-它等于我们将要生产的片段。

 import {PrerenderedComponent} from 'react-prerendered-component'; const importer = memoizeOne(() => import('./Component')); // ^ it's very important to keep the "one" promise const Component = React.lazy(importer); // or use any other library with ".prefetch" support // all libraries has it (more or less) const App = () => ( <PrerenderedComponent live={importer()}> {/* ^ shall return the same promise */ } <Component /> {/* ^ would be rendered when a component goes "live" */ } </PrerenderedComponent> ); 

您可以阅读有关该技术的另一篇文章 。 但主要是这里-它以另一种方式解决了“卸载内容的闪烁”,这不是代码拆分库的常见方式。 公开寻求新的解决方案。


TLDR?


  • 不要使用react-loadable,它不会增加任何有价值的价值
  • React.lazy很好,但是太简单了。
  • SSR很难,您应该知道
  • 混合木偶驱动的渲染是一回事。 有时甚至更难的事情。

Source: https://habr.com/ru/post/zh-CN444402/


All Articles