Code Splitting

If you are bundling an application with a lot of routes, users of your application may be downloading a lot of unnecessary content just to render the initial page. Using code splitting, you can reduce the initial download size for your application by splitting code that is conditionally loaded into a separate bundle that is only downloaded when it is needed.

Note: This guide assumes that you are using Webpack 2+ to bundle your application.

An app without code splitting#

Let's start out by describing our application's routes without code splitting. We will import each route's component from the files where they are defined.

import Home from './components/Home';
import Contact from './components/Contact';
import ContactMethod from './components/ContactMethod';

const routes = [
  {
    name: 'Home',
    path: '',
    response: () => {
      return {
        body: Home
      };
    }
  },
  {
    name: 'Contact',
    path: 'contact',
    response: () => {
      return {
        body: Contact
      };
    },
    children: [
      {
        name: 'Contact Method',
        path: ':method',
        response: () => {
          return {
            body: ContactMethod
          };
        }
      }
    ]
  }
];

import() in on.initial()#

Instead of having static imports, we will use the import() function to import our modules. We will import our components using the on.initial() property of routes. This function will only be called the first time that its route matches, so we don't have to worry about making extra requests to our server.

on.initial() should be a function that returns a Promise; import(), conveniently, returns a Promise. Then, in our response() function, instead of referencing values imported at the top of the file, we can reference the result of the initial function using resolved.initial.

import() resolves with a module object, so we will just assume that the component are default imports and reference them as resolved.initial.default. Alternatively, we could just resolve the default value in our on.initial() function.

const routes = [
  {
    name: 'Home',
    path: '',
    response: ({ resolved }) => {
      return {
        body: resolved.initial.default
      };
    },
    on: {
      initial: () => import('./components/Home'),
    }
  },
  {
    name: 'Contact',
    path: 'contact',
    response: ({ resolved }) => {
      return {
        body: resolved.initial.default
      };
    },
    on: {
      initial: () => import('./components/Contact'),
    },
    children: [
      {
        name: 'Contact Method',
        path: ':method',
        response: ({ resolved }) => {
          return {
            body: resolved.initial.default
          };
        },
        on: {
          // we can resolve module.default in initial
          // instead of in response
          initial: () => import('./components/ContactMethod')
            .then(module => module.default)
        }
      }
    ]
  }
];