Add code splitting on page boundaries #35
Description
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
useslaxar-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?