8.9 KiB
@loadable/server
Install
npm install @loadable/server
Guide
1. Install @loadable/babel-plugin
.babelrc
{
"plugins": ["@loadable/babel-plugin"]
}
2. Install @loadable/webpack-plugin
webpack.config.js
const LoadablePlugin = require('@loadable/webpack-plugin')
module.exports = {
// ...
plugins: [new LoadablePlugin()],
}
3. Setup ChunkExtractor server-side
import { ChunkExtractor } from '@loadable/server'
// This is the stats file generated by webpack loadable plugin
const statsFile = path.resolve('../dist/loadable-stats.json')
// We create an extractor from the statsFile
const extractor = new ChunkExtractor({ statsFile })
// Wrap your application using "collectChunks"
const jsx = extractor.collectChunks(<YourApp />)
// Render your application
const html = renderToString(jsx)
// You can now collect your script tags
const scriptTags = extractor.getScriptTags() // or extractor.getScriptElements();
// You can also collect your "preload/prefetch" links
const scriptTags = extractor.getLinkTags() // or extractor.getLinkElements();
// And you can even collect your style tags (if you use "mini-css-extract-plugin")
const styleTags = extractor.getStyleTags() // or extractor.getStyleElements();
4. Add loadableReady client-side
Loadable components loads all your scripts asynchronously to ensure optimal performances. All scripts are loaded in parallel, so you have to wait for them to be ready using loadableReady.
import { loadableReady } from '@loadable/component'
loadableReady(() => {
const root = document.getElementById('main')
hydrate(<App />, root)
})
🚀 Checkout the complete example in this repository
Collecting chunks
The basic API goes as follows:
import { renderToString } from 'react-dom/server'
import { ChunkExtractor } from '@loadable/server'
const statsFile = path.resolve('../dist/loadable-stats.json')
const extractor = new ChunkExtractor({ statsFile })
const html = renderToString(extractor.collectChunks(<YourApp />))
const scriptTags = extractor.getScriptTags() // or extractor.getScriptElements();
The collectChunks method wraps your element in a provider. Optionally you can use the ChunkExtractorManager provider directly, instead of this method. Just make sure not to use it on the client-side.
import { renderToString } from 'react-dom/server'
import { ChunkExtractor, ChunkExtractorManager } from '@loadable/server'
const statsFile = path.resolve('../dist/loadable-stats.json')
const extractor = new ChunkExtractor({ statsFile })
const html = renderToString(
<ChunkExtractorManager extractor={extractor}>
<YourApp />
</ChunkExtractorManager>,
)
const scriptTags = extractor.getScriptTags() // or extractor.getScriptElements();
The extractor.getScriptTags() returns a string of multiple <script> tags marked as "async". You have to wait for them to be ready using loadableReady.
Alternatively the ChunkExtractor also has a getScriptElements() method that returns an array of React elements.
Streaming rendering
Loadable is compatible with streaming rendering, if you use it you have to include script when the stream is complete.
import { renderToNodeStream } from 'react-dom/server'
import { ChunkExtractor } from '@loadable/server'
// if you're using express.js, you'd have access to the response object "res"
// typically you'd want to write some preliminary HTML, since React doesn't handle this
res.write('<html><head><title>Test</title></head><body>')
const statsFile = path.resolve('../dist/loadable-stats.json')
const chunkExtractor = new ChunkExtractor({ statsFile })
const jsx = chunkExtractor.collectChunks(<YourApp />)
const stream = renderToNodeStream(jsx)
// you'd then pipe the stream into the response object until it's done
stream.pipe(
res,
{ end: false },
)
// and finalize the response with closing HTML
stream.on('end', () =>
res.end(`${chunkExtractor.getScriptTags()}</body></html>`),
)
Streaming rendering is not compatible with prefetch
<link>tags.
Prefetching
Webpack prefetching is supported out of the box by Loadable. <link rel="preload"> and <link rel="prefetch"> can be added directly server-side to improve performances.
import path from 'path'
import { ChunkExtractor, ChunkExtractorManager } from '@loadable/server'
const statsFile = path.resolve('../dist/loadable-stats.json')
const extractor = new ChunkExtractor({ statsFile })
const jsx = extractor.collectChunks(<YourApp />)
const html = renderToString(jsx)
const linkTags = extractor.getLinkTags() // or chunkExtractor.getLinkElements();
const html = `<html>
<head>${linkTags}</head>
<body>
<div id="root">${html}</div>
</body>
</html>`
It only works with
renderToStringAPI. Since<link>must be added in the<head>, you can't do it usingrenderToNodeStream.
CSS
Extracted CSS using plugins like "mini-css-extract-plugin" are automatically collected, you can get them using getStyleTags or getStyleElements.
import { renderToString } from 'react-dom/server'
import { ChunkExtractor } from '@loadable/server'
const statsFile = path.resolve('../dist/loadable-stats.json')
const extractor = new ChunkExtractor({ statsFile })
const html = renderToString(extractor.collectChunks(<YourApp />))
const styleTags = extractor.getStyleTags() // or extractor.getStyleElements();
API
ChunkExtractor
Used to collect chunks server-side and get them as script tags or script elements.
| Arguments | Description |
|---|---|
options |
An object options. |
options.statsFile |
Stats file path generated using @loadable/webpack-plugin. |
options.stats |
Stats generated using @loadable/webpack-plugin. |
options.entrypoints |
Webpack entrypoints to load (default to ["main"]). |
options.outputPath |
Optional output path (only for requireEntrypoint). |
You must specify either statsFile or stats to be able to use ChunkExtractor.
Using statsFile will automatically reload stats for you if they change.
import { ChunkExtractor } from '@loadable/server'
const statsFile = path.resolve('../dist/loadable-stats.json')
const chunkExtractor = new ChunkExtractor({ statsFile })
chunkExtractor.collectChunks
Wrap your application in a ChunkExtractorManager.
| Arguments | Description |
|---|---|
element |
JSX element that will be wrapped in ChunkExtractorManager. |
const app = chunkExtractor.collectChunks(<YourApp />)
chunkExtractor.requireEntrypoint
Require the entrypoint of your application as a commonjs module.
| Arguments | Description |
|---|---|
name |
Optional name the entrypoint, default to the first one (main). |
const { default: App } = chunkExtractor.requireEntrypoint()
const app = <App />
chunkExtractor.getScriptTags
Get scripts as a string of <script> tags.
const body = `<body><div id="root">${html}</div>${chunkExtractor.getScriptTags()}</body>`
chunkExtractor.getScriptElements
Get scripts as an array of React <script> elements.
const body = renderToString(
<body>
<div id="root" dangerouslySetInnerHtml={{ __html: html }} />
{chunkExtractor.getScriptElements()}
</body>,
)
chunkExtractor.getLinkTags
Get "prefetch" and "preload" links as a string of <link> tags.
const head = `<head>${chunkExtractor.getLinkTags()}</head>`
chunkExtractor.getLinkElements
Get "prefetch" and "preload" links as an array of React <link> elements.
const head = renderToString(<head>{chunkExtractor.getLinkElements()}</head>)
chunkExtractor.getStyleTags
Get style links as a string of <link> tags.
const head = `<head>${chunkExtractor.getStyleTags()}</head>`
chunkExtractor.getStyleElements
Get style links as an array of React <link> elements.
const head = renderToString(<head>{chunkExtractor.getStyleElements()}</head>)
ChunkExtractorManager
Used to inject a ChunkExtractor in the context of your application.
import { ChunkExtractor, ChunkExtractorManager } from '@loadable/server'
const extractor = new ChunkExtractor()
const app = (
<ChunkExtractorManager extractor={extractor}>
<YourApp />
</ChunkExtractorManager>
)
License
MIT