Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/jaredLunde/react-broker
💸 SSR-capable async components & code splitting with Webpack 4+ and Babel 7+
https://github.com/jaredLunde/react-broker
async code-splitting lazy lazyload react reactjs server-side-rendering ssr webpack webpack4
Last synced: 3 months ago
JSON representation
💸 SSR-capable async components & code splitting with Webpack 4+ and Babel 7+
- Host: GitHub
- URL: https://github.com/jaredLunde/react-broker
- Owner: jaredLunde
- License: mit
- Created: 2018-09-01T06:16:49.000Z (about 6 years ago)
- Default Branch: master
- Last Pushed: 2023-01-03T17:12:07.000Z (almost 2 years ago)
- Last Synced: 2024-06-28T12:43:49.003Z (5 months ago)
- Topics: async, code-splitting, lazy, lazyload, react, reactjs, server-side-rendering, ssr, webpack, webpack4
- Language: JavaScript
- Homepage:
- Size: 2.06 MB
- Stars: 25
- Watchers: 3
- Forks: 0
- Open Issues: 61
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
react-broker
npm i react-broker
A [lightweight](https://bundlephobia.com/result?p=react-broker) library for lazy components using React 16.8+. It's perfect for
code splitting and has the simplest SSR story you've ever seen out-of-the-box.Critically, this package is only intended to work with Webpack, specifically
Webpack 4 and future versions. There are no plans to implement a design
accommodating Parcel or other bundlers. There is also a hard requirement
for `babel-plugin-macros` (which is shipped with CRA) if you opt to use the macro.## Quick Start
```js
import {BrokerProvider} from 'react-broker'
import lazy from 'react-broker/macro'// Automatically generates dynamic imports for webpack with babel-plugin-macros.
// Just give it the path.
const LazyPage = lazy('../pages/Page', {loading: props => 'Loading...'})////////////////////////////////////////////////////////////////////////////////
// ⬇ BECOMES ⬇ //
///////////////////////////////////////////////////////////////////////////////const LazyPage =
require('react-broker').lazy(
'src/pages/Page',
() => import(/* webpackChunkName: "src/pages/Page" */ '../pages/Page'),
{loading: props => 'Loading...'}
)
function App () {
// Look at me! I'm used like a normal component.
return (
)
}
```### Requirements
- Webpack 4+ (because `chunks`)
- React 16.8+ (because `hooks`)
- Babel (because `babel-plugin-macros`)### Examples
**[Hello world](examples/hello-world)**
**[Hello world w/ Router](examples/hello-world-router)**--------------------------------------------------------------------------------
## API
### `react-broker/macro`
The function that transforms your imports and delegates your async components.```js
import lazy from 'react-broker/macro'
```#### `lazy(component , options )`
**component** `{String}`
A path to a React component you want to lazy load. The component must be in the `default`
export of the file.Paths cannot be passed via an identifier, it has to be a plain string. It is used just like a
regular component.You may also lazy load external library components, but just know that the component in question must be the
`default` export.
```js
// Used like a regular component
const LazyPage = lazy('./pages/Home', {loading: props => 'Loading ${props.id}...'})// ...
```
**options** `{Object}`
- `loading (props, context{retry, error})`
- **props** props passed the component
- **context**
- `retry` is a function which will force a reload of the component
- `error` is any error returned by `Promise.reject`, this is only relevant
if an `error` component isn't also defined in options
- `error (props, context{retry, error})`
- See `loading`
--------------------------------------------------------------------------------### `Broker.Provider`
Manages code-splitting and the resolution of your async components by
keeping track of which chunk names have been loaded and also determining
which `` need to be included from the server-side. `Broker.Provider`
must be defined at the top-level of your lazy loaded components.#### Props
##### chunkCache `{Broker.createChunkCache}`
You only provide a `chunkCache` on the server side. In the client it is not
allowed. The chunk cache is used for tracking which chunks were loaded during
the latest render phase of the app.
`Broker.createChunkCache````js
import * as Broker from 'react-broker'const chunkCache = Broker.createChunkCache()
function App (props) {
return (
)
}
```
--------------------------------------------------------------------------------### `Broker.createChunkCache`
Creates a context for `Broker.Provider` to track chunks in and provides
helper methods to provide access to those chunks.##### `createChunkCache.getChunkNames()`
Returns an `array` of all the Webpack chunk names loaded into the current app.##### `createChunkCache.getChunks(webpackStats)`
Returns a `Set` of all the Webpack chunks loaded into the current app.
- `webpackStats` ``
- The [stats](https://webpack.js.org/configuration/stats/) object created by
Webpack.
##### `createChunkCache.getChunkScripts(webpackStats, options)`
Returns a `string` representation of all the `` tags to include in the
output of your app when using with SSR.
- `webpackStats` `{Object}`
- The [stats](https://webpack.js.org/configuration/stats/) object created by
Webpack.
- `options` `{Object}`
- `preload` `{Bool|Object}`
- If `true`, this will generate `<link rel='preload'>` tags with your scripts.
- If an `object`, the key/value pairs will be added to the `<link rel='preload'>`
tags as attributes. e.g. `{preload: {crossorigin: 'anonymous'}}` generates
`<link rel='preload' as='script' crossorigin='anonymous' href='...'>`
- `async` `{Bool}`
- If `true`, an `async` flag will be added to your `<script>` tags
- **default** `true`
- `defer` `{Bool}`
- If `true`, a `defer` flag will be added to your `<script>` tags and `async`
will be omitted
- **default** `false`#### See the [SSR section](#serverrenderjs) for an example
--------------------------------------------------------------------------------
### `Broker.lazy`
This is the function created by `react-broker/macro`.To skip the macro you could do something like this with the Webpack code-splitting
API:
```js
import {lazy} from 'react-broker'
const Component = lazy(
'uniqueChunkName',
() => import(/* webpackChunkName: "uniqueChunkName" */'./path/to/component'),
{loading: props => 'Loading...'}
)
```#### `Lazy.load()`
Preloads the component.
```js
const LazyPage = lazy('./pages/Home')
// ...
<Link onMouseEnter={LazyPage.load}>
Home
</Link>
```--------------------------------------------------------------------------------
### `Broker.load(...components <String>)`
Preloads one or several `Lazy` components.```js
import * as Broker from 'react-broker'
import lazy from 'react-broker/macro'const LazyA = lazy('./A')
const LazyB = lazy('./B')Broker.load(LazyA, LazyB).then(/*...*/)
```--------------------------------------------------------------------------------
### `Broker.loadAll(`
#### ` App: React.Element,`
#### ` renderer: ReactDOM.renderToStaticMarkup|renderToString`
### `)`
Tracks all of the chunks used in your app during the server side render and
optionally renders your app to a string- `App` `{React.Element}`
- Your React application
- `renderer` `{ReactDOM.renderToStaticMarkup|ReactDOM.renderToString}`
- **default** `ReactDOM.renderToStaticMarkup`
- The renderer used for determining the chunks used in your app. To avoid,
extra renders, you could change this to `ReactDOM.renderToString.
#### See the [SSR section](#serverrenderjs) for an example--------------------------------------------------------------------------------
### `Broker.loadInitial(chunkCache: Broker.createChunkCache)`
Populates your chunk cache with the async components present in your application.
This requires that `Broker.getChunkScripts` was used on the server side. The primary
use case for this function is elimination loading components and flashes when
initially rendering your app in the browser.#### See the [SSR section](#clientrenderjs) for an example
-------------------------------------------------------------------------------
## Server-side Rendering
#### client/render.js
```js
import React from 'react'
import ReactDOM from 'react-dom'
import * as Broker from 'react-broker'
import App from '../App'const app = (
<Broker.Provider>
<App/>
</Broker.Provider>
)Broker.loadInitial().then(
() => ReactDOM.hydrate(app, document.getElementById('⚛️'))
)
```#### server/render.js
```js
import React from 'react'
import ReactDOMServer from 'react-dom/server'
import express from 'express'
import * as Broker from 'react-broker'
import App from '../App'export default function createRenderer({
// These are the Webpack compilation stats returned by Webpack post-run.
// https://webpack.js.org/api/stats/
clientStats
}) {
app = express()app.get('*', /* Note 'async' here */ async (req, res, next) => {
res.set('Content-Type', 'text/html')
// keeps track of lazy chunks used by the current page
const chunkCache = Broker.createChunkCache()
// chunkCache is passed to Broker.Provider as a prop
const app = (
<Broker.Provider chunkCache={chunkCache}>
<App/>
</Broker.Provider>
)
// Preloads the async components and renders the app to a string
const page = await Broker.loadAll(app, ReactDOMServer.renderToString)
// You could also do this if you have other requirements in addition to preloading with
// react-broker
// await Broker.loadAll(app, ReactDOMServer.renderToStaticMarkup)
// const page = await ReactDOMServer.renderToString(app)
// Generates <script> and <preload> tags for this page
const chunks = chunkCache.getChunkScripts(clientStats, {preload: true})res.send(`
<html>
<head>
${chunks.preload}
</head>
<body>
<div id='⚛️'>
${app}
</div>
${chunks.scripts}
</body>
</html>
`)
})return app
}
```