Building  Large-Scale Web Applications with Angular
上QQ阅读APP看书,第一时间看更新

Lazy loading of routes

When we roll out our application, we expect that our users will be accessing the Workout Runner every day (and we know that this will be the case for you!). But, we anticipate that they will only occasionally be using the Workout Builder to construct their exercises and workout plans. It would, therefore, be nice if we could avoid the overhead of loading the Workout Builder when our users are just doing their exercises in the Workout Runner. Instead, we would prefer to load Workout Builder only on demand when a user wants to add to or update their exercises and workout plans. This approach is called lazy loading. Lazy loading allows us to employ an asynchronous approach when loading our modules. This means that we can load just what is required to get the application started and then load other modules as we need them.

Under the hood, when we use the Angular CLI to build and serve our application, it uses WebPack's bundling and chunking capabilities to accomplish lazy loading. We'll be discussing these capabilities as we work through how to implement lazy loading in our application. 

So in our Personal Trainer, we want to change the application so that it only loads the Workout Builder on demand. And the Angular router allows us to do just that using lazy loading.

But before we get started implementing lazy loading, let's take a look at our current application and how it is loading our modules. With the developer tools open in the Sources tab, start up the application; when the start page appears in your browser, if you look under the webpack node in the source tree, you will see that all the files in the application have loaded, including both the Workout Runner and Workout Builder files:

So, even though we may just want to use the Workout Runner, we have to load the Workout Builder as well. In a way, this makes sense if you think of our application as a Single Page Application (SPA). In order to avoid round trips to the server, an SPA will typically load all the resources that will be needed to use the application when it is first started up by a user. But in our case, the important point is that we do not need the Workout Builder when the application is first loaded. Instead, we would like to load those resources only when the user decides that they want to add or change a workout or exercise.

So, let's get started with making that happen.

First, modify app.routing-module.ts to add the following route configuration for WorkoutBuilderModule:

const routes: Routes = [
...
{ path: 'builder', loadChildren: './workout-builder/workout-builder.module#WorkoutBuilderModule'},
{ path: '**', redirectTo: '/start' }
];

Notice that the loadChildren property is:

module file path + # + module name 

This configuration provides the information that will be needed to load and instantiate WorkoutBuilderModule

Next go back to workout-builder-routing.module.ts and change the path property to an empty string:

export const Routes: Routes = [ 
    { 
        path: '', 
. . . 
    } 
]; 

We are making this change because we are now setting the path (builder) to the WorkoutBuilderRoutes in the new configuration for them that we added in app.routing-module.ts.

Finally go back to app-module.ts  and remove the WorkoutBuilderModule import in the @NgModule configuration in that file. What this means is that instead of loading the Workout Builder feature when the application first starts, we only load it when a user accesses the route to Workout Builder.

Let's go build and run the application again using ng serve. In the Terminal window, you should see something like the following output:

What's interesting here is the last line that shows a separate file for the workout.builder.module called workout-builder.module.chunk.js.. WebPack has used what is called code splitting to carve out our workout builder module into a separate chunk. This chunk will not be loaded in our application until it is needed (that is when the router navigates to WorkoutBuilderModule).

Now, keeping the Sources tab open in the Chrome developer tools bring up the application in the browser again. When the start page loads, only the files related to the Workout Runner appear and not those related to the Workout Builder, as shown here:

Then, if we clear the Network tab and click on the Create a Workout link, we'll see the workout-builder.module chunk load:

This means that we have achieved encapsulation of our new feature and with asynchronous routing we are able to use lazy loading to load all its components only when needed.

Child and asynchronous routing make it straightforward to implement applications that allow us to have our cake and eat it too. On one hand, we can build SPAs with powerful client-side navigation, while on the other hand we can also encapsulate features in separate child routing components and load them only on demand.

This power and flexibility of the Angular router give us the ability to meet user expectations by closely mapping our application's behavior and responsiveness to the ways they will use the application. In this case, we have leveraged these capabilities to achieve what we set out to do: immediately load Workout Runner so that our users can get to work on their exercises right away, but avoid the overhead of loading Workout Builder and instead only serve it when a user wants to build a workout.

Now that we have the routing configuration in place in the Workout Builder, we will turn our attention to building out the sub-level and left navigation; this will enable us to use this routing. The next sections cover implementing this navigation.