18

We are currently trying to optimize a complex angular application (performance & bundle size).

We found that we have partially unused components, but we are not 100% sure about them. Anyway... The question we are currently asking is how exactly does tree shaking work in Angular?

Question 1) If there are entries inside the declarations, imports or providers array of a module, but the module is not used anywhere, are they also removed with tree shaking or are they included in the final bundle?

Example:

@NgModule({
  imports: [
    FirstModule,
    SecondModule  // Is not used anymore (probably) but imported here
  ],
  declarations: [SampleComponent] // Is not used anymore (probably) but imported here
})
export class SampleModule {
}

Info: The routes to those modules/components are already deleted.

Question 2) Is it sufficient to only remove these components from the routing modules so that the tree shaking process is successful?

Question 3) What should be in place for the treeshaking process to work optimally?

Used Angular Version 8.2

3
  • Depends on whether you're using Ivy. Are you using Ivy?
    – kvetis
    Commented Feb 20, 2020 at 13:38
  • 1
    No, we are currently using Angular verson 8.2.x
    – Codehan25
    Commented Feb 20, 2020 at 13:40
  • @kvetis Have the same question but we are using Ivy, will it remove unused in the templates and controllers but included in declarations component? Commented Feb 2, 2021 at 16:15

2 Answers 2

16

About how tree-shaking works in Angular

Tree-shaking is done primarily in the minification stage and is not specific to Angular. terser is the minifier they use in the Angular CLI which is a fork of Uglify that supports ES6 syntax.

terser reads your code and if it is not referenced elsewhere, and it can be certain that removing some code will have no side-effects, then it will remove that code.

terser can't be certain whether or not classes with decorators have side-effects just in being defined (decorators are function calls). In Angular's production build, they have a webpack plugin called "build-optimizer" that will go to places their decorators are used and add a special comment /*@__PURE__*/ next to the compiled javascript (from Typescript) which terser recognizes and will remove the decorated class (if it's not referenced elsewhere).

About removing components from the NgModule

The build process just starts from your main.ts file and crawls all the imports for all the files to include in your build. If, from the main.ts file, your component file is never imported by any of the files the crawling process finds, it will never be included. That's not tree-shaking, it's just not included in the build process at all.

If your module file is the only place the component file is imported, then removing that import will be sufficient not to include the component in your build.

About things you can do

You can make some Angular specific changes to ensure you only build the code you use.

  1. Use the {providedIn: root} decoration option for Injectable and remove any places that injectable service, guard, whatever from any Angular decoration's providers: [].
  2. Use the Ivy renderer which will mean less of the framework is bundled (if you're not using it).

Additionally, at the call site (not the definition) you may be able to mark any functions without side effects with the /@__PURE__/ comment so that even if they are called somewhere, if the return value could removed via some other optimization the function itself could also be removed. I haven't tested this, but in theory it should work.

I don't know how all the various build stages transform your code and if they move or remove comments before terser can see them.

You can test it out using this playground tool: https://terser-playground.surge.sh/

Try it with this example:

(function() {
  const a = 1;
  const b = 2;
  const c = /*@__PURE__*/ test();
  console.log(a + b);
}());

function test() {
  return 3
}
1
7

You can use webpack-bundle-analyzer which will help you visualize the size and the different used components in your final output file. This way you can be sure if a module is used or not and then you can safely delete it.

4
  • But how do you use it with Angular? Do you need to run ng eject and wire your build process yourself?
    – Kevin Beal
    Commented Aug 13, 2020 at 21:23
  • 3
    @KevinBeal npm install -D webpack-bundle-analyzer; to do a build with analyze data: add --stats-json to your ng build command. To view the generated analysis: webpack-bundle-analyzer <path-to-stats-json>
    – Spock
    Commented Aug 29, 2020 at 15:13
  • It's definitely a great tool to analyze your bundle, but it isn't useful for answering questions like "why is that module not included?" for example Commented May 26, 2022 at 13:30
  • Source Map Explorer will be much easier to use with Angular. npmjs.com/package/source-map-explorer Commented Jul 19, 2023 at 4:17

Not the answer you're looking for? Browse other questions tagged or ask your own question.