Skip to content

Instantly share code, notes, and snippets.

@kimniche
Last active November 6, 2017 20:39
Show Gist options
  • Save kimniche/ab438248e20ef53e3fc8ac609dfb986b to your computer and use it in GitHub Desktop.
Save kimniche/ab438248e20ef53e3fc8ac609dfb986b to your computer and use it in GitHub Desktop.

Concept Review

Bundles

  • Processed output files emitted by a bundler (Webpack)
  • Contain chunks

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

Routes

  • An object of a path, fluxibleRouteAction, and component
  • path is relative, like /k12/rankings or /colleges/survey

Router Context

https://github.com/ReactTraining/react-router/blob/master/packages/react-router-dom/docs/guides/server-rendering.md

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)

https://github.com/ReactTraining/react-router/blob/master/packages/react-router-dom/docs/guides/server-rendering.md#adding-app-specific-context-information

Deprecations

  • match
  • childRoutes, indexRoute

Big changes

matchPath

https://reacttraining.com/react-router/web/api/matchPath

routes need vertical prefix

// account-routes.js before
/register/about-you/

// account-routes.js now
/account/register/about-you/

introducing: exact

https://github.com/ReactTraining/react-router/blob/master/packages/react-router/docs/api/Route.md#exact-bool

When true, will only match if the path matches the location.pathname exactly.

order now matters in route config files

Catch-all routes should come last since first matching path will be used; eg /account/register/about-you/ needs to come before /account/register/

we can't actually use staticContext

😭 conflicts with fluxible 😭 https://github.com/ReactTraining/react-router/blob/master/packages/react-router/modules/StaticRouter.js#L104-L106

redirect routes (search 301s) don't need component specified

{
    path: `/${vertical}/search/`,
    status: 301,
    url: getBaseVanityUrlByVertical(vertical),
    exact: true,
},

babel plugin dynamic-import-node breaks code-splitting

  • Converts to require.ensure on server, allows use of dynamic imports (import()) on client

Creating async chunks

Click handlers or other user interaction as trigger

// 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.

Non-critical page content

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

Going forward

Be mindful of opportunities to code split

Dynamic imports can help reduce our chunk sizes 🎉

What does this mean for future routes?

  1. New component goes into app/components/routes/AsyncBundles.js AND app/components/routes/SyncBundles.js
  2. Define new route in appropriate route file as per usual
  3. Let webpack and react router take care of the rest!
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment