- Processed output files emitted by a bundler (Webpack)
- Contain chunks
- Entry chunk
- contains Webpack bootstrap code (also called the webpack "runtime") needed to run your app
- must go in <script> tag
- Normal chunk
- One per code-split module
- webpack will dynamically load this, so no need to include in <script> tag
- does not include the Webpack runtime
- Chunks may be in multiple bundles
- An object of a
path
,fluxibleRouteAction
, andcomponent
path
is relative, like /k12/rankings or /colleges/survey
Handle redirects and status codes on server based on render
result:
- 301 redirects
- 301 vs 302 redirects
- 404 redirects
Components have access to this in props staticContext
to manipulate as needed
const context = {}
// ...
const markup = ReactDOMServer.renderToString(
<StaticRouter context={context}>
<App/>
</StaticRouter>
)
// ...
redirect(context.status, context.url)
match
childRoutes
,indexRoute
https://reacttraining.com/react-router/web/api/matchPath
// account-routes.js before
/register/about-you/
// account-routes.js now
/account/register/about-you/
When true, will only match if the path matches the location.pathname exactly.
Catch-all routes should come last since first matching path will be used; eg /account/register/about-you/
needs to come before /account/register/
😭 conflicts with fluxible 😭 https://github.com/ReactTraining/react-router/blob/master/packages/react-router/modules/StaticRouter.js#L104-L106
{
path: `/${vertical}/search/`,
status: 301,
url: getBaseVanityUrlByVertical(vertical),
exact: true,
},
- Converts to
require.ensure
on server, allows use of dynamic imports (import()
) on client
// before, SomeComponent.jsx
import editProfileDeactivateClicked from '../../actions/forms/edit-profile-deactivate-clicked-action'
// ...
handleDeactivateClick = async e => {
e.preventDefault()
this.context.executeAction(editProfileDeactivateClicked, {})
}
// after, OK
// (no top-level import)
handleDeactivateClick = async e => {
e.preventDefault()
const editProfileDeactivateClicked = await import('../../actions/forms/edit-profile-deactivate-clicked-action')
this.context.executeAction(editProfileDeactivateClicked, {})
}
The above generates a bundle for the code, entitled 26.js
. But we can do better!
// after, better via named chunk with webpack magic comments
// (no top-level import)
handleDeactivateClick = async e => {
e.preventDefault()
const editProfileDeactivateClicked = await import(/* webpackChunkName: "EditProfileDeactivateAction" */ '../../actions/forms/edit-profile-deactivate-clicked-action')
this.context.executeAction(editProfileDeactivateClicked, {})
}
Note: babel can't parse import
statements like the one above -- dynamic imports are a webpack-specific feature. To allow babel to read them, we use the "syntax-dynamic-import" babel plugin.
Items that are only needed after the critical/ATF (above the fold) page content has loaded, if not needed for SEO purposes, make good candidates for async chunks. For example, consider the SearchShareBar, which loads on search pages after the user scrolls:
// Search.jx before, standard imported component
import SearchShareBar from './share-bar/SearchShareBar'
Here, Search.js = 313kb
// Search.jsx after, dynamic import
import asyncComponent from '../../routes/async-component'
const SearchShareBar = asyncComponent('SearchShareBar', () => import(/* webpackChunkName: "SearchShareBar" */ './share-bar/SearchShareBar'))
Now, we have Search.js = 309 kb and SearchShareBar.js = 3.43 kb
Dynamic imports can help reduce our chunk sizes 🎉
- New component goes into app/components/routes/AsyncBundles.js AND app/components/routes/SyncBundles.js
- Define new route in appropriate route file as per usual
- Let webpack and react router take care of the rest!