Code splitting

Code Splitting is required if you want to make your "home" bundle as lightweight as possible. A framework must set up accordingly. FuseBox offers great functionality to loading split bundles with just a few lines of code.

Splitting is universal, meaning that you can lazy load them on a server (nodejs) as well.

Basics

Code splitting in FuseBox is very simple, before we dive into examples, these are the basics.

  • 1

    Master Bundle

    Code splitting happens on a master bundle, so techincally you have only 1 bundle that is going to be split. All split bundles will be generated automatically
  • 2

    Configuration is shared

    Split bundles fully depend on a master bundle. They cannot be customised or decorated with additional configuration as they are "branched out"
  • 3

    Piping files

    Files are piped out from a master bundle. All external dependencies will be skipped.
  • 4

    Information on your bundles

    The information on your bundles is stored in a master bundle. FuseBox builds a manifest file, while this file can be accessed directly in the [output](#basic), however your master bundle WILL know how to resolve them by name without having an extra hustle.

FuseBox does as much as possible to automate the process.

Setting it up

Let's imagine a project

File hierarchy
  • Expand all
  • Collapse all
  • node_modules
    • src
      • app.tsx
      • helper.ts
      • routes
        • about
          • AboutHelper.tsx
          • AboutComponent.tsx
        • contact
          • Foo.ts
          • Bar.ts
          • ContactComponent.tsx

    How?

    • 1

      Entry point

      We have an application that is pointed to app.tsx
    • 2

      Isolate routes

      We isolate our routes, meaning that we can't have any require statements / imports in our main application (`app.tsx` or its modules). That would defeat the purpose of code splitting.
    • 3

      Cross reference and Shared Scope

      Split bundle CAN reference other modules e.g `Helper.ts`. The scope is shared and accessible throughout the entire application
    • 4

      Master bundle

      Our master bundle should consume split targets using arithmetic instructions

    Instructions

    Instructions for code splittings need to be set up to consume split targets, otherwise, no plugins will be applied. Split bundles are taken out entirely from a master bundle, therefore the functionality is limited.

    const app = fuse.bundle("app")
        .split("routes/about/**", "about > routes/about/AboutComponent.tsx")
        .split("routes/contact/**", "contact > routes/contact/ContactComponent.tsx")
        .instructions(`> [app.tsx] + [routes/**/**.{ts, tsx}]`)

    Make sure that your application has no references to the split bundles, then modify your instructions to consume them.

    .instructions(`> [app.tsx] + [routes/**/**.{ts, tsx}]`)

    Split

    Once instructions are setup, we can define a config for splitting.

    split("routes/about/**", "about > routes/about/AboutComponent.tsx")

    First Argument: Path matching

    First argument "routes/about/**" is a simplified RegExp. Each time FuseBox processes files it tests the path against the defined RegExp. These paths have no relation to physical paths, and they all belong to homeDir without an opening slash.

    Let's break down the logic:

    • 1

      FuseBox tests `app.ts`

      It does not match any of defined split configs. Simply because `routes\/about/.*?` does not match `app\.ts`
    • 2

      FuseBox tests `helper.ts`

      Same result: `routes\/about/.*?` does not match `helper\.ts`
    • 3

      FuseBox test `routes/about/AboutHelper.tsx`

      Success, now FuseBox knows that this module needs to be stripped from the main bundle and piped out to a different one
    • 4

      Tests continues

      It continues until all files are processed.

    FuseBox caches your modules aggressively, so performance is blazing fast.

    Second Argument: Split instructions

    Now FuseBox knows which files belong no longer to the master bundle, let us read the instructions:

    about > routes/about/AboutComponent.tsx

    about in a split bundle name, it's processed through the output and respects hashing. For example, having output: "dist/$name.js" will result in file dist/about.js. Now for the tricky part: each split bundle may have many files included.

    File hierarchy
    • Expand all
    • Collapse all
    • routes
      • about
        • AboutHelper.tsx
        • AboutComponent.tsx

    FuseBox matched 2 of them - AboutHelper.tsx and AboutComponent.tsx. Which module should be executed once the bundle is lazy loaded? Exactly!

    The second part with > stands for an entry point, which aligns with FuseBox arithmetics. Once you lazy load, your bundle will have the exports of AboutComponent.tsx

    Configuring

    You may need to configure how FuseBox resolves your bundles.

    browser key will help FuseBox to resolve your bundles in browser, and server on server accordingly. dest customises the output: it's consistent with our output configuration:

    const fuse = FuseBox.init({
      homeDir: "src",
      output: "dist/$name.js",
    })
    
    fuse.bundle("app")
        .splitConfig({ browser: "/static/bundles/", server : "dist/static/bundles/", dest: "bundles/" })
        .split("routes/about/**", "about > routes/about/AboutComponent.tsx")
        .split("routes/contact/**", "contact > routes/contact/ContactComponent.tsx")
        .instructions(`> [app.tsx] + [routes/**/**.{ts, tsx}]`)
    
    fuse.run()

    With the above setup, we will have:

    File hierarchy
    • Expand all
    • Collapse all
    • dist
      • bundles
        • about.js
        • contact.js
      • app.js
    • src
      • app.tsx
      • helper.ts
      • routes
        • about
          • AboutHelper.tsx
          • AboutComponent.tsx
        • contact
          • Foo.ts
          • Bar.ts
          • ContactComponent.tsx

    Lazy load

    Finally, we can lazy load from out master bundle. To make your life easier FuseBox offers a great tool for resolving named bundles.

    import { lazyLoad } from "fuse-tools";
    lazyLoad("about").then(module => {
      // routes/about/AboutComponent.tsx has arrived
    })

    See a fully working react application with code splitting

    Example

    git clone https://github.com/fuse-box/fuse-box-examples.git
    npm install
    cd examples/react-code-splitting
    node fuse.js