反应和代码拆分

我在很久以前的2008年遇到了代码拆分,当时Yandex有点暂停,而同步连接到该站点的Yandex.Direct脚本只是杀死了该站点。 通常,在那些日子里,如果您的“脚本”是您以唯一正确的顺序连接的10个文件,那仍然很正常,但仍然可以(延迟)仍然可以正常工作。
然后,我开始积极地使用卡,它们仍然作为外部脚本(当然是延迟加载)连接在一起。 然后,作为Yandex.Maps团队的成员,我积极地使用ymodules在客户端上进行树状摇动,从而提供了完美的代码分割。


然后我去了webpackReact ,到了一个受惊的白痴国家,他们看着require.ensure像公羊一样在新的大门前,仍然这样做。


代码拆分不是哇的功能,它是必须具备的功能。 SSR仍然不会干扰...



小介绍


如今,当捆绑包每天变得越来越胖时,代码拆分变得比以往任何时候都更加重要。 最初,人们只是通过为应用程序的每个页面创建单独的入口点来摆脱这种情况,这通常很好,但不适用于SPA。
然后是require.ensure函数,今天称为dynamic import (仅导入),通过它您可以简单地请求一个模块,稍后您将收到。


关于React的这种情况的第一个库是react-loadable ,对我来说还不是很清楚,而且已经消失了(它不再是为了取悦作者)了。


现在,或多或少的“官方”选择将是React.lazyloadable-components (只是@loadable ),它们之间的选择是显而易见的:


  • 一般来说,React.lazy完全无法进行SSR(服务器端渲染)。 即使在测试中,如果没有铃鼓的特殊舞蹈(例如“同步许诺”),它也会掉落。
  • 在支持Suspense的同时,可加载SSR可以比React.Lazy更好。

特别是,可加载性支持漂亮的包装器来加载库(loadable.lib,您可以将moment.js放入React renderProp中),并帮助服务器端的webpack收集用于预取的已用脚本,样式和资源的列表(webpack本身并不真正知道)。 通常,请阅读官方文档


固态继电器


通常,整个问题出在SSR中。 对于CSR(客户端渲染),无论是React.lazy还是只有10行的小脚本都适合-绝对足够,并且连接大型外部库没有任何意义。 但是在服务器上,这还不够。 而且,如果您确实不需要SSR,则可以跳过进一步的阅读。 您没有需要长期努力解决的问题。


SSR是一种痛苦。 我(以某种方式)是可加载组件的维护者之一,令人震惊的是有多少个错误出自不同的地方。 随着每一次更新,webpack都飞得更多。


SSR + CSS


CSS带来了更大的问题。
如果您拥有样式化组件-并没有那么大的麻烦-它们附带了transform-stream ,它本身将为最终代码添加所需的内容。 最主要的是,到处都必须有一个版本的SC,否则焦点将不起作用-一个版本的SC将无法说明其自身的其他信息,并且SC喜欢复制(检查您的捆绑包)。 老实说,正是由于这种限制,焦点通常会失败


C情感更简单-他们的styled适配器仅在组件本身前面吐出<style> ,问题就解决了。 简单,便宜,开朗。 原则上,它非常适合移动设备使用,并且极大地优化了第一视图。 但是第二个有点宠坏了。 而且就我个人而言,我的良心不允许我使用这种内联样式。


使用普通的CSS(包括从各种CSS-in-JS库获得的具有不同魔力的CSS)更加容易-在webpack列中有关于它们的信息,并且“知道”需要连接哪些CSS。


连接顺序


狗在这里埋葬自己。 我什么时候应该连接?
SSR友好代码拆分的含义是,在调用ReactDOM.hydrate之前,您需要下载服务器响应中已经存在的所有“组件”,但是客户端当前加载的脚本负担不起。


因此,所有库都提供了一个特定的回调,当所有需要加载的所有东西都将被调用时,您可以启动大脑 。 这就是SSR代码分割库的工作含义。


可以随时加载JS,通常将它们的列表添加到HTML的末尾,但是CSS(因此没有FOUC)必须添加到开头。
所有库都可以对旧的renderToString执行此renderToString ,而所有库都不能renderToNodeStream执行此renderToNodeStream
无论您只有JS(不会发生)还是SC / Emotion(会自动添加),都没有关系。 但是-如果您只有“ CSS”-就是这样。 它们要么在末尾,要么您必须使用renderToString或其他缓冲,这将提供TTFB(至第一个字节的时间)延迟,并总体上略微降低使用此SSR的感觉。


当然,所有这些都与webpack绑定,没有其他方式。 因此,在充分尊重Greg的同时,可加载组件的作者-我建议考虑其他选择。


接下来是一个由三部分组成的议程,其主要思想是要做一些不被杀死并且不依赖捆绑程序的事情。

1. React导入组件


React-Imported-Component并不是一个坏的“加载器”,具有或多或少的标准接口,与可加载组件非常相似,可加载组件可以对所有移动进行SSR。


这个想法很简单。


  • 扫描源代码,找到所有import并将其复制到单独的文件中
  • 使用babel plugin每次import调用都会变成一些糖
     const AsyncComponent1 = imported(() => import('./MyComponent')); ///// const AsyncComponent1 = imported(() => importedWrapper("imported_18g2v0c_component", import('./MyComponent'))); 
  • 当调用“ import” function.toString时,魔术数字就被取出。 因此,很清楚是什么引起的。 (是的-这对代码施加了一些限制,但比根本无法“不导入”的其他加载器要少)
  • 在客户端上,我们有一个文件,其中收集了所有可能的导入,并且可以重复进行任何导入。

无需stats.json ,即可进行webpack优化(连接或通用代码)-您只需要在数组键中匹配一次导入的“标签”,然后再次导入即可。 如何将其作为特定捆绑软件的一部分执行,实际上将下载多少文件以及从哪里下载都不是他的问题。


减号-加载“旧”块的加载开始于加载主包之后,该主束存储了映射,这比“可加载组件”的情况“稍晚”,后者会将这些信息直接添加到HTML。


是的,使用CCS,无论如何都无法正常工作。


2.二手样式


但是, 二手样式仅适用于CSS,但与react-imported-components的方式几乎相同。


  • 扫描所有CSS(在构建目录中)
  • 记得在哪里定义了哪个类
  • 扫描输出renderToNodeStream(或renderToString响应)
  • 找到class =“ XXX”,匹配文件并在服务器响应中将其吐出。
  • (好,然后将所有这些样式传送到头部,以免破坏水合物)。 样式组件的工作原理相同。

没有TTBT延迟,它与捆绑器无关-童话。 如果样式写得好,可以像手表一样工作。


React-import-component +使用样式+宗地工作示例。


不是最明显的好处-在服务器上,这两个库都将在启动期间执行“所需的一切”,直到快递服务器可以接收第一个客户端为止,并且在服务器上和测试期间都将完全同步。

3. react-prerendered组件


图书馆关闭了进行“部分补液”的前三名,并且以祖父的方式完成了,我马上就想知道。 她确实添加了“ divas”。


  • 在服务器上:
    • 用“著名ID”将一块木头包裹在div中
  • 在客户端上:
    • 组件构造函数找到自己的div
    • 在React接受之前复制其innerHTML。
    • 使用此HTML,直到客户端准备好hydrate
    • 从技术上讲,这允许使用混合SSR(Rendertron)

 const AsyncLoadedComponent = loadable(() => import('./deferredComponent')); const AsyncLoadedComponent = imported(() => import('./deferredComponent')); <PrerenderedComponent live={AsyncLoadedComponent.preload()} // when Promise got resolve - component will go "live" > <AsyncLoadedComponent /> // meanwhile you will see "preexisting" content </PrerenderedComponent> 

此焦点不适用于可加载组件,因为它不会从preload promise中返回 。 对于像React-Snap这样的库(和其他“ Prerenders”)来说,这些库具有“内容”但没有通过“真实的” SSR,这尤其重要。



从代码的角度来看,这是10行,加上考虑到代码的加载和执行的随机顺序,可以获得更多的稳定SSR-CSR UID


奖金:


  • 您无需等待“所有脚本的加载”就可以开始动脑筋 -大脑在准备就绪时就会开始
  • 根本不需要加载大脑,只需要保留SSR版本的数据即可(如果没有SSR版本,那么大脑仍将加载)。 就像在jQuery时代一样。
  • 您还可以再次使用转换流来实现大型渲染块的流缓存 (理论上与Suspence兼容)。
  • 并在jQuery中将状态序列化到/从HTML反序列化/反序列化

原则上,序列化和反序列化是创建库以解决复制状态问题的主要思想(摘自有关SSR的图片)。 缓存稍后到达。


合计


总共,有三种方法可以改变您对SSR和代码拆分的看法。 第一种适用于JS代码拆分,并且不会中断。 第二个可以与CSS代码分割一起使用,并且不会中断。 第三个在HTML级别工作,简化并加速了某些过程,并且再次,它没有中断。


链接到库:



文章(英文)


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


All Articles