Wednesday, August 22, 2018

An even better bundle optimization method for SPFx using webpack dynamic imports


Photo by Rose Elena at Unsplash

In my last post I ventured into how you dynamically can load script resources in your SPFx web part or extension. Typically you might have parts only needed in edit mode, or parts only used in certain view scenarios. By not including everything in the SPFx bundle you will reduce loading time and page bootstrapping of your solution.

For example, if you use a lot of Office UI Fabric components or an external library like moment, then your overall bundle size, or downloaded .js file increase a lot. If those components are not always needed, loading them dynamically at will is a better solution.

And, it’s so easy! SPFx uses web pack to create the bundle, and webpack allows for something called dynamic imports. And this is such a golden nugget – sitting there without anyone knowing :)

Instead of having an import statement at the top of your code like

import ScriptEditor from './components/ScriptEditor';

you load it dynamically in your code when needed like

const editorPopUp = await import(
    /* webpackChunkName: 'scripteditor' */
    './components/ScriptEditor'
);

The crux here is the comments part /* webpackChunkName: ‘scripteditor’ */ which will make sure anything in that tsx/ts file will be wrapped up in it’s own .js file, loaded when this line of code executes.

That means, refactoring your code to load pieces dynamically is very very very easy. I have updated the modern script editor web part to load the editor this way. And the good thing, you don’t get an extra copy of react, so the dynamic piece is smaller compared to my previous solution.

A big thanks to Pat Miller @ MSFT who clued me onto this solution - https://github.com/SharePoint/sp-dev-docs/issues/2388

Note:

If your SPFx project has “module” : “commonjs” instead of “esnext” in tsconfig.js which SPFx v1.5+ projects have, you need to add the following to the top of your file:

declare var System: any;

and then use System.import, instead of just import. If not, everything goes into the same bundle.