I recently implemented some big optimizations for the company that I work for and I wanted to share with you how I tackled the topic step by step. The solution uses dynamic import and code splitting.

The application uses reactjs as UI library, but the concept of splitting the code can be applied to every application.

I always try to use the latest technologies and features in order to bring the highest level of quality for my customers. The application that I work on is a metadata driven application. What does that mean? It means that all the components that the application has to render will dictated by some metadata coming from the server. So by default the application doesn’t know what to render.

The application implements a mapping which is basically an object that maps the components to render:

1
2
3
4
5
6
7
8
9
10
11
12
// mapping.js
import ComponentA from 'src/path/to/ComponentA'
import ComponentB from 'src/path/to/ComponentB'

const components = {
ComponentA,
ComponentB
}

export default function getComponent(type) {
return components[type]
}

Inside the application there is a component that will be in charge of getting the component based on the definition of the metadata and it will render it. There is no rocket science behind it so I will skip this step.

Everything was synchronous and the application needed to load internally all the components before getting the first document.

Code splitting

Webpack always had a feature for code splitting called require.ensure.

I wanted to use this feature but, there is more! Webpack introduced a new approach using a function called import() which will be very soon proposed as a standard, probably inside JavaScript ES2018. If you want to have more information about how to you use the function, check this article from 2ality.

This function tells to Webpack

Get this very file specified inside the path that I give you as input and put it inside a different file that will be called ‘chunk’. Inside this chunk, also append all the dependencies that it needs

Webpack has a limitation though, the import is done at compiling time, not at runtime (there is no way at the moment to ‘guess’ which file to load). But that’s fine, I just want to implement the code splitting

1
2
3
4
5
6
7
8
9
// mapping.js

export default function getComponent(type) {
switch (type) {
case 'ComponentA': return () => import(/* webpackChunkName: "ComponentA" */ 'src/path/to/ComponentA')
case 'ComponentB': return () => import(/* webpackChunkName: "ComponentB" */ 'src/path/to/ComponentB')
default: return undefined
}
}

You see that comment named webpackChunkName? Well, that’s a recent feature implement inside version 2.4.0 of webpack. This gives you the chance to give a name to the chunk. This feature could potentially deprecate the require.ensure.

The component that will be responsible of rendering those components will look like this

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
// RenderComponent.js
import getComponent from './mapping.js'

class RenderComponent extends React.Component {

constructor (props) {
super(props)
this.state = {
component: null
}
}

componentDidMount () {
const { metadata } = this.props
this.fetchComponent(metadata)
}

fetchComponent (metadata) {
const resolver = fetchComponent(metadata.type)
if (resolver) {
resolver()
.then(Component => this.setState({ component: <Component /> }))
.catch(err => throw new Error(err))
}
}

render () {
const { component } = this.state
if (!component) {
return null
}
return (
component
)
}
}

In this way, when webpack will compile the code, will create different chunks separated from the main bundle, they will have the name that I specified and will be loaded only if they are needed from the metadata!

Comment and share

  • page 1 of 1

Emanuele Stoppa

Programmer, Gamer and music listener


Senior UI Developer, Contractor


Dublin