Skip to content
This repository has been archived by the owner on May 22, 2023. It is now read-only.

Add code splitting on page boundaries #35

Open
jpommerening opened this issue Apr 5, 2018 · 3 comments
Open

Add code splitting on page boundaries #35

jpommerening opened this issue Apr 5, 2018 · 3 comments

Comments

@jpommerening
Copy link
Member

jpommerening commented Apr 5, 2018

TL;DR:
This adds the ?split option to laxar-loader to automatically create webpack chunks for each page.


I implemented a prototype the other day to enable webpack's bundle splitting at page boundaries.
For that I created a file that looked like this:

import definition from './application/flows/main.json';
import artifacts from 'laxar-loader/artifacts?theme=my-theme';

artifacts.aliases.flows.main = 0;
artifacts.flows = [ { descriptor: { name: 'main' }, definition } ];

proxy( 'pages', 'my-page', () => import( /* webpackChunkName: "my-page" */ 'laxar-loader/artifacts?page=my-page&theme=my-theme' ) );

function proxy( bucket, ref, fn ) {
   let index;
   Object.defineProperty( artifacts.aliases[ bucket ], ref, {
      get() {
         if( index === undefined ) {
            index = artifacts[ bucket ].length;
            artifacts[ bucket ][ index ] = fn()
               .then( makeSureTheRequestedRefIsPresentInTheBundle )
               .then( mergeTheReceivedBundleIntoArtifacts )
               .then( () => artifacts[ bucket ][ index ] ); // the promise we stored earlier has now been replaced with a plain JS object
         }
         return index;
      }
   } );
}

This is more-or-less what the loader now generates.
The proxy and merge functions have been moved to split-base.js and handle a few more edge cases and are structured in such a way that makes the calls easy to generate, while allowing to "proxy" more than one single ref with a call.

For example:

// we could generate this call:
proxy( artifacts, { pages: [ 'intro', 'home' ], flows: [ 'sub-flow' ] },
  () => import( /* webpackChunkName: "start" */ 'laxar-loader/artifacts?pages[]=intro&pages[]=home&flows[]=sub-flow' ) );

// … and webpack would split out the pages 'intro' and 'home' as well as everything referenced
// from them and from the flow 'sub-flow' into a chunk named 'start'

Usage:

  • Make sure you're using the right laxar-loader version:
    $ test -f "node_modules/laxar-loader/lib/split-base.js" && echo "ready to split!"
  • Make sure laxar-loader uses laxar-tooling@^2.0.3:
    $ grep "version.*2\.0\.[3-9]" "node_modules/laxar-loader/node_modules/laxar-tooling/package.json"
  • Just add ?split to your artifacts-bundle-import:
    import artifacts from 'laxar-loader/artifacts?flow=main&theme=app&split';
    //                                                                ^^^^^

This should work with any webpack version >= 2, but you get extra benefits from webpack 4+ because the new version is super clever when splitting code. It does, for example, identify common dependencies of some (but not all) chunks and splits them into a separate bundle that is imported when needed. To find out more about that, read this blog post.

Open for discussion:

We could customize the splitting strategy. Theoretically we could split the bundle at any artifact boundary. I guess pages make the most sense. Maybe we could allow the user to group pages into a single chunk? Perhaps by adding a property to the page description? Or a configuration option?

/cc @x1B @alex3683 @jonaswjs

@alex3683
Copy link
Member

alex3683 commented Apr 6, 2018

Sweet! From an application point of view a grouping of places (in the flow definition) would make the most sense. For example places of an admin tool could be separated from the public application part. A normal user would only load public assets, while an administrator would additionally load the admin assets when necessary. Pages could theoretically be used in both groups, which would by the way be a situation where webpack >=4 would shine.

@x1B
Copy link
Member

x1B commented Apr 11, 2018

Hot Stuff! Can't wait to try it!

@x1B
Copy link
Member

x1B commented Apr 11, 2018

wrt the artifact boundary: I definitely like per-page. With HTTP/2, smaller chunks will have a much smaller overhead (connection multiplexing rather than TCP slow start). Also, we can intelligently pre-load chunks based on e.g. the "next" relationship in the flow definition.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants