Chapter 10. Bundling for Production

When deploying a JS application to production, bundling is an important practice performance-wise. By merging resources, mostly JS code, HTML templates, and CSS sheets, into a single file, we can drastically reduce the number of HTTP calls the browser has to make to serve the application.

The CLI always bundles the application it runs, even in a development environment. This makes deploying an application to a server pretty simple; it's only a matter of building it and then copying a bunch of files over.

But then comes the versioning problem. When deploying a new version of our application, if the bundles keep the same names, cached bundles may not get refreshed, causing users to run an outdated version of our application. How do we deal with this?

In this chapter, we'll see how to customize the bundling of our contact-management application. We'll also see how to leverage the CLI's revision feature to version our bundles, so we can benefit from HTTP caching as much as possible. Finally, we'll add a new build task to our project in order to facilitate deployment.

Configuring bundles

By default, a project created using the CLI contains two bundles: a first one named vendor-bundle.js, which contains all external libraries used by the application, and a second named app-bundle.js, which contains the application itself.

The bundles are configured in the aurelia_project/aurelia.json file, under the build section. Here's how it looks in a typical application:

"bundles": [ 
  { 
    "name": "app-bundle.js", 
    "source": [ 
      "[**/*.js]", 
      "**/*.{css,html}" 
    ] 
  }, 
  { 
    "name": "vendor-bundle.js", 
    "prepend": [ 
      "node_modules/bluebird/js/browser/bluebird.core.js", 
      "scripts/require.js" 
    ], 
    "dependencies": [ 
      "aurelia-binding", 
      "aurelia-bootstrapper", 
      "aurelia-dependency-injection", 
      "aurelia-framework", 
      //Omitted snippet... 
    ] 
  } 
] 

Each bundle has a unique name, and must define its content, which can be sourced from the application and external dependencies. Typically, the app-bundle includes all JS, HTML, and CSS from the application's sources, while the vendor-bundle includes external dependencies.

This is generally the best configuration for small to medium applications. The external dependencies, which commonly don't change very often, are grouped in their own bundle, so the users won't have to download those dependencies every time a new version of the application is released. In most cases, they'll only have to download the new app-bundle.

Merging the application in a single bundle

However, if for some reason you want your application to fit in a single bundle, including both the application itself and its dependencies, it is fairly easy to do so. You simply need to define a single bundle, which contains both the application sources and the external dependencies:

Note

The snippets in the following section are excerpts from the chapter-10/samples/app-single-bundle sample from the book's assets.

aurelia_project/aurelia.json

"bundles": [ 
  { 
    "name": "app-bundle.js", 
    "prepend": [ 
      "node_modules/bluebird/js/browser/bluebird.core.js", 
      "scripts/require.js" 
    ], 
    "source": [ 
      "[**/*.js]", 
      "**/*.{css,html}" 
    ], 
    "dependencies": [ 
      "aurelia-binding", 
      "aurelia-bootstrapper", 
      //Omitted snippet... 
    ] 
  } 
] 

With the entry point of an Aurelia application being the aurelia-bootstrapper library, the entry-point bundle must be the one containing the bootstrapper. By default, this is the vendor-bundle. If you change the entry-point bundle here it becomes the app-bundle; you need to change a couple of things.

First, still in aurelia_project/aurelia.json and under build, the loader's configTarget property must be changed for the new entry-point bundle:

aurelia_project/aurelia.json

"loader": { 
  "type": "require", 
  "configTarget": "app-bundle.js", 
  // Omitted snippet... 
}, 

Additionally, the main script tag of index.html must also reference the new entry-point bundle:

index.html

<!-- Omitted snippet... --> 
<body aurelia-app="main"> 
  <script src="scriptsapp-bundle.js" 
          data-main="aurelia-bootstrapper"></script> 
</body> 
<!-- Omitted snippet... --> 

If you run the application at this point, you will see that a single bundle is generated, and that the browser loads only this bundle when launching the application.

Splitting the application into multiple bundles

In some scenarios, having the whole application source in a single app-bundle is suboptimal. We could easily imagine an application built on heavily segregated user stories. Users, depending on their role, only use specific parts of this application.

Such an application could be split into multiple smaller bundles, one for each role-related section. This way, users would not download bundles for sections of the application they never use.

The snippets in the following section are excerpts from the chapter-10/samples/ app-with-home sample from the book's assets.

Let's try this out by moving the contacts feature of our application into its own bundle. To do so, we first need to exclude everything within the contacts directory from the app-bundle:

aurelia_project/aurelia.json

{ 
  "name": "app-bundle.js", 
  "source": { 
    "include": [ 
      "[**/*.js]", 
      "**/*.{css,html}" 
    ], 
    "exclude": [ 
      "**/contacts/**/*" 
    ] 
  } 
} 

The source property supports either an array of glob patterns, or an object with an include and optional exclude properties, both expected to contain an array of glob patterns.

Here, we simply move the previous value of source down to the include property, and add an exclude property matching everything in the contacts directory.

Next, we need to define the new bundle:

aurelia_project/aurelia.json

{ 
  "name": "app-bundle.js", 
  //Omitted snippet... 
}, 
{ 
  "name": "contacts-bundle.js", 
  "source": [ 
    "[**/contacts/**/*.js]", 
    "**/contacts/**/*.{css,html}" 
  ] 
},

This new bundle, named contacts-bundle.js, will include all JS, HTML, and CSS files within the contacts directory.

If you run the application at this point, you should first see that the scripts directory now contains three bundles: app-bundle.js, contacts-bundle.js, and vendor-bundle.js. If you open the application in a browser and check the debug console, you should see that when loading the application, the browser first loads the vendor-bundle, then the app-bundle, and finally, the contacts-bundle.

The contact-bundle is loaded when the main configure function loads the contacts feature during the application startup process. This is one of the limitations of Aurelia's features: it can be difficult to isolate a feature in a distinct bundle. Indeed, a feature's index file, along with all its dependencies, should be bundled in the app-bundle. Bundling it separately is useless, since this other bundle will be loaded upon startup anyway. However, everything else in the feature can be bundled separately.

In our application, even if you make this change, the contacts-bundle would still get loaded when the application starts, because the app component automatically redirects the user to the contacts default route, which is the contacts list.

If you add a home component as the default route in the application and you make sure this home component is included in the app-bundle, you should see that the contacts-bundle is loaded only when you navigate to it.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.222.250.157