dojo dragon main logo

Creating bundles

A bundle is a portion of code that represents a slice of functionality. Bundles can be loaded asynchronously on demand and in parallel. An application that is appropriately bundled can be significantly more responsive and require fewer kilobytes and less load time than an application that does not use any kind of code splitting. This is especially important when working with large applications where much of the presentation logic isn't needed on the initial load.

Dojo tries to make intelligent choices by using routes and outlets to automatically split code into smaller bundles. In general these bundles should all have code that is related and relevant. This comes for free as part of the build system and doesn't require any additional thought to use. However, for those with specific bundling needs Dojo also allows for bundles to be explicitly defined in the .dojorc configuration file.

By default a Dojo application only creates a single application bundle. However, there are a number of configuration options provided by @dojo/cli-build-app that will help break down an application into smaller portions that can be progressively loaded.

Automatic bundling using routes

By default Dojo will create bundles based on an application's routes. In order to do this several rules must be followed.

  1. src/routes.ts must have a default export containing the routing configuration
  2. Widgets must be the default export of their module
  3. Outlets render function must use inline functions

src/routes.ts

export default [
	{
		path: 'home',
		outlet: 'home',
		defaultRoute: true
	},
	{
		path: 'about',
		outlet: 'about'
	},
	{
		path: 'profile',
		outlet: 'profile'
	}
];

src/App.ts

export default class App extends WidgetBase {
	protected render() {
		return (
			<div classes={[css.root]}>
				<Menu />
				<div>
					<Outlet key="home" id="home" renderer={() => <Home />} />
					<Outlet key="about" id="about" renderer={() => <About />} />
					<Outlet key="profile" id="profile" renderer={() => <Profile username="Dojo User" />} />
				</div>
				w(Menu, {}),
			</div>
		);
	}
}

The output will result in a separate bundle for each of the application's top level routes. In this example, there will be a main application bundle and bundles for src/Home, src/About, and src/Profile.

To see automatic bundling in action create a new application using @dojo/cli-create-app and run npm run build. Dojo will automatically create bundles along the various routes in the sample application.

Manually specifying bundles

Bundles can be manually specified in the .dojorc configuration file, providing a mechanism for declarative code splitting within an application. This can be useful for breaking down an application into smaller bundles when automatic route bundling isn't sufficient.

The bundles feature is part of the build app command. The configuration is comprised of a map of bundle names followed by a list of files or globs to match.

For example, this configuration will bundle About and Profile together in a bundle named additional.[hash].js. Widget modules defined used with w() will be automatically converted to a lazily imported, local registry item in the parent widget.

.dojorc

{
	"build-app": {
		"bundles": {
			"additional": ["src/widgets/About", "src/widgets/Profile"]
		}
	}
}

If we wanted to create nls internationalization modules by locale we could use globs to ensure all files under each language directory are included.

.dojorc

{
	"build-app": {
		"bundles": {
			"fr": ["src/**/nls/fr/**"],
			"de": ["src/**/nls/de/**"]
		}
	}
}

In this case Dojo will create bundles named fr.[hash].js and de.[hash].js. For more information see Working with message bundles from the Internationalization reference guide.

Bundling considerations

Sometimes decisions made by the build tool or manually defined in .dojorc can create duplication of common resources shared by multiple bundles. Some of this is unavoidable. A good general rule of thumb for avoid duplication is to try to ensure that common code is at the outermost edges of an application's dependency tree. In other words, minimize dependencies as much as possible among shared code. If a significant amount of code may be shared among bundles (e.g. common widgets) consider bundling these assets together.