- PVSM.RU - https://www.pvsm.ru -
It's 2019! Everybody thinks they know code splitting. So - let's double check!

In short – code splitting is just about not loading a whole thing. Then you are reading this page you don't have to load a whole site. When you are selecting a single row from a database – you don't have to take all.
Obvious? Code splitting is also quite obvious, just not about your data, but your code.
React.lazy? No – it just using it. Code splitting is being done on a bundler level – webpack, parcel, or just your file system in case on "native" esm modules. Code splitting is just files, files you can load somewhere "later".
React.lazy is using. Just using code splitting of your bundler. Just calling import when got rendered. And that's all.
React.lazy superseded it. And provided more features, like Suspense to control loading state. So - use React.Lazy instead.
Yep, that's all. Thank you for reading and have a nice day.
Well. There are a few grey zones about React.lazy and code splitting I forgot to mention.
It's not easy to test React.lazy due to its asynchroniosity. It would be just "empty", as long as it is not loaded yet(even if it is) – Promises and import returns, and lazy accepts, promises, which always got executed in the next tick.
Proposed solution? You would not believe, but the proposed solution is to use synchronous thenables — See pull request [1]. So — lets make our imports SYNCHRONOUS!!! (to fix lazy issue for the tests, or any other server side case)
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>,
);
It's not hard to convert import function to a memoized synchronous thenable.
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'));
If you DON'T need SSR – please continue reading the article!
React.lazy is SSR friendly. But it requires Suspense to work, and Suspense is NOT server side friendly.
There are 2 solutions:
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
This this is a good option, but it would not be quite client side friendly. Why? Let's define the 2th possible solution:
It's easy – not react-loadable – it's heavy unmaintained and obsolete, even if still mega popular. (and thank you for popularizing code splitting, yet again)
Loadable-components – might be a very good choice. It very well written, actively maintained and support everything out of the box. Support "full dynamic imports", allowing you to import files depending on the props given, but thus untypable. Supports Suspense, so could replace React.lazy.
Universal-component – actually "inventors" of full dynamic imports – they implemented it in Webpack. And many other things in low level – they did it. I would say – this library is a bit hardcore, and a bit less user friendly. Loadable-components documentation is unbeatable. It's worth if not to use this library, then read documentation - there are so many details you should know…
React-imported-component – is a bit odd. It's bundler independent, so it would never break (there is nothing to break), would work with Webpack 5 and 55, but that comes with a cost. While previous libraries during SSR would add all the used scripts to the page body, and you will be able to load all the scripts in a parallel – imported don't know files names, and will call the original "imports"(that's why bundle independent) to load used chunks, but able to make call only from inside a main bundle – so all additional scripts would be loaded only after the main one got downloaded and executed. Does not support full dynamic imports, like React.lazy, and, as a result – typeable. Also supports Suspense. Uses synchronous thenables on SSR. It also has absolutely different approach for CSS, and perfect stream rendering support.
There is no difference in quality or popularity between listed libraries, and we are all good friends – so pick by your heart.
SSR is a good thing, but, you know, hard. Small projects might want to have a SSR – there are a lot of reasons to have it – but don't want to setup and maintain it.
SSR could be really, REALLY hard. Try razzle or go with Next.js if you want a quick win.
So the easiest my solution for SSR, especially for simple SPA would be prerendering. Like:
Prerendering is "SSR" without "Server". It's SSR using a Client. Magic! And working out of the box… … … but not for code spitting.
So - you just rendered your page in a browser, saved HTML, and asked to load the same stuff. But Server Side Specific Code (to collect all used chunks) was not used, cos THERE IS NO SERVER!

In the previous part, I've pointed to libraries which are bound to webpack in terms of collecting information about used chunks - they could not handle hybrid render at all.
Loadable-components version 2(incompatible with current version 5), was partially supported by react-snap. Support has gone.
React-imported-component could handle this case, as long as it does not bound to the bundler/side, so there is no difference for SSR or Hybrid, but only for react-snap, as long as it support "state hydration", while rendertron does not.
This ability of react-imported-component was found write writing this article, it was not known before - see example [9]. It's quite easy.
And here you have to use another solution, which is just perpendicular to all other libraries.
This library was created for partial hydration, and could partially rehydrate your app, keeping the rest still de-hydrated. And it works for SSR and Hybrid renderers without any difference.
The idea is simple:
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 component goes "live" */ }
</PrerenderedComponent>
);
Автор: kashey
Источник [10]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/javascript/312067
Ссылки в тексте:
[1] See pull request: https://github.com/facebook/react/pull/14626
[2] Universal-component: https://www.npmjs.com/package/react-universal-component
[3] React-loadable: https://www.npmjs.com/package/react-loadable
[4] Loadable-components: https://www.npmjs.com/package/@loadable/component
[5] Imported-component: https://www.npmjs.com/package/react-imported-component
[6] React-async-component: https://github.com/ctrlplusb/react-async-component
[7] React-snap: https://github.com/stereobooster/react-snap
[8] Rendertron: https://github.com/GoogleChrome/rendertron
[9] example: https://github.com/theKashey/react-imported-component/tree/master/examples/hybrid/react-snap
[10] Источник: https://habr.com/ru/post/444402/?utm_campaign=444402
Нажмите здесь для печати.