Dynamic Components Using TypeScript 2.4

Dynamic import expressions are a new feature in ECMAScript that allows you to asynchronously request a module at any arbitrary point in your program. These modules come back as Promises of the module itself, and can be await-ed in an async function, or can be given a callback with .then.

TypeScript 2.4 added support to dynamic import expressions. With webpack, I have successfully made it working with the AppRun library to load dynamic components.

AppRun is a library I created for building applications using the elm style model-view-update architecture. Using AppRun, applications can be built from model (data model of application state), view (a function to display the state) and update (a set of functions to change the state).

Applications can also be built from components. Components have their own model, view and update. They are like mini applications.

Components are classes exported from ES2015 modules. They are loaded into web page elements by reacting to routing events or user events. It is a perfect scenario of using dynamic import expressions to loads components only when they are needed. In the post, I will demonstrate how to in a demo application.

The Demo Application

The demo application uses Bootstrap starter template, which has three top menus: home, about and contact. Once the menus are clicked, the three corresponding components will be loaded and displayed.

The Components

Without dynamic import expressions, the components are statically imported and bundled into app.js. They are loaded without menu clicks.

import Home from './home';
import About from './about';
import Contact from './contact';
const element = document.getElementById('my-app');
new Home().start(element);
new About().start(element);
new Contact().start(element);
static components

With dynamic import expressions, components are lazily loaded only once the menus are clicked.

import app from 'apprun';
import home from './home';
const element = document.getElementById('my-app');
new home().start(element);
app.on('#about', async () => {
const module = await import('./about');
new module.default().start(element);
});
app.on('#contact', async () => {
const module = await import('./contact');
new module.default().start(element);
})
dynamic components

Notice in the screen recording above, the home component was bundled statically into app.js and is loaded immediately. The about (1.js) and contact(0.js) components are loaded upon menu clicks.

TypeScript Configurations

In order to support the dynamic import expressions, the module setting has to be set to esnext in tsconfig.js. The full tsconfig.js in my case is as below.

{
"compilerOptions": {
"target": "es5",
"jsx": "react",
"reactNamespace": "app",
"lib": ["dom", "es2015", "es5"],
"module": "esnext",
"moduleResolution": "node",
"sourceMap": true
}
}

There is no special treatment needed in webpack. Just use vanilla settings with ts-loader for TypeScript.

module.exports = {
entry: {
‘app’: ‘./main.tsx’,
},
output: {
filename: ‘[name].js’
},
resolve: {
extensions: [‘.webpack.js’, ‘.ts’, ‘.tsx’, ‘.js’]
},
module: {
rules: [
{ test: /.tsx?$/, loader: ‘ts-loader’ }
]
},
devServer: {},
devtool: 'source-map'
}

Debugging

We can enable the source map is enabled in tsconfig.json and webpack.config.js. The dynamic loaded code can be debugged at the TypeScript source level as usual.

Conclusion

The dynamic import expressions are interesting. Although I used it with the AppRun components, it should work with other modules and libraries.

You can try the demo here. Or check out the source code.

Happy coding.