Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Angular Tree Shaking: How exactly does it work?

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 that 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

like image 614
Codehan25 Avatar asked Feb 20 '20 13:02

Codehan25


2 Answers

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
}
like image 149
Kevin Beal Avatar answered Oct 03 '22 02:10

Kevin Beal


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 would be sure if a module is used or not and then you can safely delete it.

like image 30
Fahd Lihidheb Avatar answered Oct 03 '22 00:10

Fahd Lihidheb