Apr 2, 2019 · 4 min read #Angular

Reducing Angular Build Sizes

I like to remind the front-end team that we're all "still learning Angular". Truth is, most of us are solid javascript developers, but there are always tricks or nuances we learn about Angular on a weekly basis.

When I first joined FanReact, pulled down my first repo, and ran npm start I didn't think much of having a 6mb build size. Coming from Ruby on Rails and PHP, I thought that the 6mb was the overall project size, not the main JS file required to load before the website was DOM ready.

Since that time nearly 2 years ago, I've compiled a set of lessons I've learned while developing in Angular so that I can save some other naive soul from giant build sizes.

The simplest way to reduce Angular build sizes is using AOT and tree-shaking, but there's nothing new about that. Instead, I'm going to go over the more non-automated ways of reducing build size.

Lesson 1: Beware ViewEncapsulation

Whether you like to scope your SASS to components is up to you (we do selectively), but be careful you don't start importing common SASS files in each component thinking that AOT or tree-shaking will magically clean your CSS for you.

Within the 6mb build project, I found that EVERY single component was importing 2 SASS files "library.scss" and "forms.scss" whether they were using it or not. Since our project had 74 components and the combined size of the 2 SASS imports were 35kb, our build size exploded by 2.59mb.

Obviously the first thing I did was to remove the unnecessary forms.scss import when it wasn't needed. Then instead of importing "library.scss" on every component, I imported it once on my main styles.scss file and gave each class a semi-unique prefix.

.lib-animate {
  -webkit-transition: all .15s linear;
  -moz-transition: all .15s linear;
  -o-transition: all .15s linear;
  transition: all .15s linear;
}

.lib-container {
  max-width: 1280px;
  margin: 0 auto;
  padding: 0 10px;
  @media #{$tablet} {
    padding: 0 20px;
  }
  @media (min-width: 1310px) {
    padding: 0;
  }
}

In general, the only thing you should be importing into a ViewEncapsulated component's CSS are colors and breakpoint variables.

Lesson 2: Don't make a component for every UI variation

Another thing we suffered from was component overload. Even in our more recent projects, I've found the same HTML template used in multiple components, the only difference being CSS, conditionals, and data related changes.

Prior to starting any project, I break down the design comps into the most basic of building blocks and find areas where I can make my CSS work for me rather than making additional components. In my most recent project, there was a component that had a myriad of arrangements and layouts. Instead of making a component per view, I instead switched the container class of the component and relied on 400 lines of CSS.

One component to rule them all
Each of these 5 comps have 4 different views. -- 1 Angular component.

Lesson 3: If you use a function/method more than twice, make it a service

This one seems obvious. But, when you have 5 developers all working simultaneously on a project you'll find that there's plenty of function duplication.

Early on, our biggest mistake was not having a service that would quickly return an extension on a data object. There were many times we needed to reference a user's profile extensions for different components, but always had written some kind of method (34 to be exact) instead of simply creating a service accessible anywhere.

import { Injectable } from '@angular/core';

@Injectable()
export class ExtensionService {

  constructor() { }

  static profileExtension(name, profile) {
    let extension = null;
    for (let i = 0; i < profile.profileExtensions.length; i++) {
      if (profile.profileExtensions[i].name === name) {
        extension = profile.profileExtensions[i];
      }
    }
    return extension;
  }

}

Lesson 4: Spot check your package.json occasionally

After months of development and hundreds of commits from a half-dozen developers, you're bound to find un-used npm packages. Not only that, but you may find there are alternative versions of the package that are significantly smaller than the original. Case in point: Moment.js: nearly half of the package size is language variations whereas the alternative moment-mini does not.

Furthermore, attempt to load packages that are not immediately required on page load via CDN asynchronously. For VypeReplay, our package included a 1.2mb package used for creating video annotations, but since a user would never need this package when first hitting the site, we loaded it after the fact via CDN and saved ourselves a TON of load time.

Lesson 5: Don't go HTML class crazy

Below is a copy-pasted line from a previous project.

<i *ngIf="showArrows" class="icon icon-Arrow_Right lib-arrow lib-flex lib-cross-center lib-main-center lib-p10 lib-pointer" (click)="scroll('right')"></i>

Each character in HTML is 1 byte. Fight for every byte -- There's no reason why this icon has so many classes. And this is just one instance, the project is riddled with class spam. Going back and removing all unneccesary classes saved 32kb on build size. Not much, but every bit counts.


Do you have any other tricks you have used to reduce your build sizes? I'm all ears! Let me know below in the comments.

&More Articles

Dec 6, 2018 · 5 min read

Be "good" at CSS with these six things

Working at an office of developers, it’s a near weekly occurrence that I will hear “I hate CSS” or “I suck at CSS”.

Mar 2, 2019 · 2 min read

Best SCSS Mixins

From mixins and operators, to conditionals and extensions. My favorite SASS and functions.