AngularJS + Webpack = lazyLoad

Introduction

One of the common problems most developers encounter with when writing Single Page Application is creating and loading lazyLoad modules in response to a certain action taken by a user or clicking on URL (in most cases) that would load a set of dependencies such as JavaScript, CSS, HTML etc. In the realities of modern Front-End development that would be a huge JavaScript file. Well, in this article I would like to share my experience and show you how to realize lazyLoad modules on AngularJS and decrease the start time when you first run an application.

Why AngularJS 1.x

You may wonder “Why work with AngularJS 1.x if Angular v5.2 is available?” The question is more than appropriate. However, do not forget that many projects still use AngularJS 1.x and feel pretty good. For them, as well as for many other branches, switching to a new version would be expensive both in man-hours and money-wise. AngularJS 1.x remains in high demand in the market.

Bicycles that already exist

It has become a common thing that in the world of Front-End development there’s a great number of tools/approaches which can be used to solve the same issue and everyone tends to choose the one that fits his needs, skills, and knowledge best. And this isn’t necessarily a bad thing. You should accept this and go on developing. Someone chooses RequireJS, someone opts for curl.js, someone goes for Browserify, someone gives preference to [paste your favorite tool]. In this article I’d like to show you how to realize lazyLoad modules loading using Webpack, UI-Router, and ocLazyLoad. All behind-the-scene magic will be done with the help of ocLazyLoad tool. If we try to create a lazyLoad module using, for example, require.ensure without ocLazyLoad, we will receive the type of error like this one:

The Project Structure

Here goes. Since I cannot provide access to the original code of the project for which I had to realize lazyLoad modules, I’ve created a small application. I tried to make it as simple as possible so that it didn’t look like a star probe vehicle and you could clearly see what comes from what. The main objective was to create a working prototype which would be accessible online (not only sources). A little bit below you’ll find the project structure for the branches require-ensure and system-import. For the branch import-es6 we’ll amend some addition.

Now, before we proceed to the code, I would like to emphasize on two important points which directly affect whether we’ll get a lazyLoad module or not:

  1. In order for the module to become lazyLoad, one shouldn’t determine it as a dependency in relation to other modules.
  2. The module cannot be imported anywhere except for the route for which you’d like to create this lazyLoad module.

Do not worry if that doesn’t sound clear just yet. In practice, you’ll quickly learn what’s what.

require.ensure() + $ocLazyLoad

require.ensure was introduced by the webpack team in the first of its versions. This method allows developers to dynamically create separate files (called “chunks” in webpack terms) containing certain pieces of the code which would further be loaded to the user in response to a certain action. And although the said method at its core isn’t the best one available for fulfilling this task, if you still prefer it, there’s nothing wrong about it. This method will perfectly suit those who want to create lazyLoad modules without spending too much on refactoring. Below, you can find an example of applying require.ensure for loading index.module.js:

For about.module.js the code will be identical, with the only difference that the paths to a module and some other parameters will be slightly different. Below, you can see an example of using require.ensure for loading about.module.js:

As you might have noticed all magic happens in this line: 
$ocLazyLoad.load(module.HOME_ABOUT_MODULE);

There’s also one other way of defining the module which is by using an object: 
$ocLazyLoad.load({ name: "home.module" });

The downside to this method, though, is that we restrict our freedom of actions. Suchwise, if we decide to rename the module in the future, we’ll have to make alterations in several fragments of the code. Also, you may make a typo when writing a new name of the module which is a very common problem. Well, if you ask me, I’d strongly recommend that you do not use this approach ever.

I would like you to pay attention to one very important nuance regarding about.module.js and further loading apps to the user. Take a look at the screenshot below:

Clicking the Home/About link, two files will be loaded simultaneously: index.module.chunk.js and about.module.chunk.js. This happens so because home.about URL is subsidiary of the home URL. You should keep this in mind. Running a little bit ahead, in the last section we will add one more module with a new URL and see that it will load only one file and nothing more.

System.import + $ocLazyLoad

I have long been thinking whether to write about this approach or not and then decide in favor of doing so. System.import is another construction derivative of the webpack command which despite being prohibited still remains one of the implementation options offered. Furthermore, this construction continues working in new webpack versions. What I suspect is that this is so because of the compatibility reasons. If you use this construction in your project, I’ve got bad news for you — it is currently in a deprecated status. OK, let’s move on.

Dynamic imports + $ocLazyLoad

Perhaps you’ve already heard that Chrome 63 and Safari Technology Preview 24 released a few updates and now developers can access dynamic imports. Yes, you’re right, those very dynamic imports that have been offered in the specification. Back in 2016, the webpack team has first applied dynamic imports support. In this section we will add one more module to the root of pages directory, to make sure lazyLoad works correctly. The structure for the branch import-es6 is given below:

If you use neither Babel nor TypeScript in your project then everything will work straight out of the box. But you know as well as I do that in the realities of the modern Front End it is very difficult to write a code using neither of the two tools. Let’s take a closer look at Babel. At first we need to install an additional plugin for babel which understands the syntax of dynamic imports: syntax-dynamic-import. If we don’t do this we will get an error:

Then we need to add .babelrc with the given settings:

Now, here’s the second unpleasant error which you may see below:

Yes, that’s right, ESLint doesn’t understand dynamic imports either. To correct this, what we need to do is install a special parser for ESLint babel-eslint and once we do this, all will work smoothly. Now let’s add .eslintrc with the settings:

The time has come for us to try out dynamic imports in business. Let’s test them using the new module:

As you may have noticed from the code, the webpack command has added a few pleasant perks to dynamic imports. Now we have an opportunity to specify both the name of the final chunk which will create webpack and the load method. To learn more about this option, click here to read. On the video below, we can see dynamic imports in work:

Component vs. Template

The property that we used for loading modules is component, however, if you prefer, you can use template instead. They work almost the same way with the only one difference: if you make a typo in the name of a component or the component will be unavailable for one reason or another, the console will contain an error and you may spend quite a while to find out what the problem is. If you use template, however, this error will not occur.

Useful Links

  1. How to route to AngularJS 1.5 components
  2. Lazy loading in UI-Router
  3. Source code on GitHub
  4. Project on Heroku

Instead of Conclusion

Using lazyLoad modules in the context of AngularJS apps is an excellent way to make your application less resource-intensive, more responsive and better distributed. Times change, requirements for applications grow and so does the amount of code which we deliver to the user. If earlier it was enough to assemble all code in one file, give it to the user and it was done and dusted now it’s become something of a luxury. There’s a tendency now to separate an application depending on the URL and allocate the common code.

That’s all for now. Thanks for your attention and special thanks to those who’ve read this article to the end.

P.S. If you’ve had a similar experience in realizing lazyLoad modules for AngularJS applications — share your comments below.