E.g. when a React component or a third party package is only needed when the user clicks on a certain button, then there is no need to load in on the first page load. It can be fetched on-demand when the button is pressed. This reduces the amount of data fetched over the wire on first page load, thus improving performance and user experience. This can be particularly helpful in large applications with many third party packages and components.
In this post let's have a look at how to do code splitting in the SharePoint Framework. As an example, I am going to use an SPFx web part created using React but the code splitting approach can be used with other frameworks/libraries as well.
We are going to have a look at two scenarios where code splitting can really help:
1) Loading a React Component on-demand (where we load the DetailsList component from Office UI Fabric)
2) Loading a third party package on-demand (where we load the infamous-for-its-large-size moment js)
So to begin with, here is my render method of a React component created by default by the SPFx yeoman generator:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public render(): React.ReactElement<ILoaderProps> { | |
return ( | |
<div className={styles.loader}> | |
<button onClick={this._loadDocumentsClicked.bind(this)}> | |
Load Documents | |
</button> | |
<button onClick={this._loadMomentClicked.bind(this)}> | |
Load moment js | |
</button> | |
<div className="momentContainer"> | |
{this.state.currentTime} | |
</div> | |
<div className="detailsContainer"> | |
</div> | |
</div> | |
); | |
} |
Load a React Component on-demand:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
private async _loadDocumentsClicked() { | |
const component = await import( | |
/* webpackChunkName: 'documentdetails-component' */ | |
'./DetailsListComponent' //Custom component | |
); | |
const element: React.ReactElement<any> = React.createElement( | |
component.DetailsListDocumentsComponent | |
); | |
const currentElement = ReactDom.findDOMNode(this); | |
const detailsContainerElement = currentElement.getElementsByClassName("detailsContainer")[0]; | |
ReactDom.render(element, detailsContainerElement); | |
} |
Once the import function fetches the DetailsList component class, we create an an instance of the class and use ReactDom to insert the component to the detailsContainer div in our main component.
Load a third party package (moment js) on-demand:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
private async _loadMomentClicked() { | |
const moment = await import( | |
/* webpackChunkName: 'moment-js' */ | |
'moment' | |
); | |
this.setState({ | |
currentTime: moment().calendar() | |
}); | |
} |
And here is the code in action on a modern SharePoint page:
(click to zoom)
What is also important to note is that the bundle will be loaded only if it was not loaded earlier. The import function is smart enough to determine if the bundle is already downloaded and it does not request it again.
Hope you found this useful!
As always, the code for this is available on GitHub: https://github.com/vman/SPFxCodeSplitting
1 comment:
Hello, I encountered an issue when I tried to configure this dynamic loading.
I changed in the tsconfig.json, "module": "esnext", "moduleResolution": "node",
And now, when I run the gulp serve, I got this warning : Warning - [typescript] Your tsconfig.json file specifies a different "target" than expected. Expected: "commonjs". Actual: "esnext". Using "commonjs" instead.
I'm stuck with that!!
Thanks for your help
Post a Comment