steveblue / angular2-rollup

CLI for bundling Angular with Rollup and Closure Compiler
121 stars 18 forks source link

Unused FormsModule is not removed from the bundle after treeshaking #15

Closed dmitrysteblyuk closed 7 years ago

dmitrysteblyuk commented 7 years ago

I modified app.module a bit by removing routing and all the modules except BrowserModule. Then I run ngr build --prod and the size of the output bundle is 414kB.

If I include FormsModule back in AppModule imports, the bundle is bigger by ~100kB which is about the size of the whole FormsModule minified. Despite FormsModule is never used in the app. So why it's not getting treeshaked?

steveblue commented 7 years ago

Thats a good question. If you import a module, it will still be imported by ngc. If you are not using a Module, don't import it. You can see this in ngfactory/tmp/app/shared/app.module.ngfactory.ts. You can find references to FormsModule still in bundle.js.

The bundle can be gzipped and you should see a drastic size reduction.

ngr --build prod
tar -zcvf build/bundle.tar.gz build/bundle.js

When I omit FormsModule heres what I get on the master branch right now.

bundle.js - 353 KB bundle.tar.gz - 90 KB

With FormsModule

bundle.js - 418KB bundle.tar.gz - 102 KB

In the near future, I plan on writing a build that should be even smaller on load with Splittable but there are limitations there while it should support lazy loading. If anyone wants to contribute this build that would be awesome!

You could also build ngc => ClosureCompiler, omitting Rollup to produce a very small bundle. Right now this is not very possible and somewhat impractical for third party libraries from what I understand although @mlavel got it to work. The core team and Contributors are working on it. It is most likely essential for the core team to figure out how to get this build optimal for teams working at Google.

Optimizations for the production build will most likely be better over time, Angular 4 already was a great move in that direction.

@mlaval demos all the ways Angular prod build can be optimized .

dmitrysteblyuk commented 7 years ago

So what's the point of treeshaking then if not to remove from the bundle unused components and directives of imported modules? If I import a module but don't include it in NgModule imports then I can use tslint (unused-variables) to find imports I don't use and treeshaking would be absolutely useless.

steveblue commented 7 years ago

I was curious to benchmark the prod build post Angular 4 and it is quite impressive

Here are the results

steveblue commented 7 years ago

@dmitrysteblyuk It boils down to how the Modules are exported and how well the tool can interpret the code. Rollup isn't the only tool treeshaking the bundle. The core team made a new setting in the compilerOptions for ngc called annotateForClosureCompiler. This should allow ClosureCompiler to interpret ngfactory and treeshake the codebase further.

Consider this:

If you import FormsModule but don't inject it via imports, it will not be compiled by ngc.

import { NgModule }      from '@angular/core';
import { HttpModule }    from '@angular/http';
import { CommonModule }  from '@angular/common';
import { FormsModule }   from '@angular/forms';
import { BrowserModule } from '@angular/platform-browser';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { AppComponent }  from './app.component';
import { routing }       from './app.routes';
import { HomeModule } from './shared/components/home/home.module';

@NgModule({

    imports: [ BrowserModule,
               BrowserAnimationsModule,
               HttpModule,
               CommonModule,
               HomeModule,
               routing],
    declarations: [ AppComponent ],
    bootstrap:    [ AppComponent ]
})

export class AppModule {}
dmitrysteblyuk commented 7 years ago

My bundle looks less optimistic but after gzipping it's about 157kB.

1

It was just my understanding that rollup can indeed treeshake unused code, and I found it weird that it doesn't apply to angular 2 modules. And then the question is why to use rollup at all if treeshaking has no effect. The case when I do not inject imported modules can be easily (and preferably) handled by lint.

steveblue commented 7 years ago

Rollup isn't the only tool that is treeshaking. ngc, Rollup, and ClosureCompiler are all tree shaking and optimizing the bundle.

dmitrysteblyuk commented 7 years ago

Ok, thanks. I'll try ClosureCompiler. By the way, do you know by any chance if it can handle lazy loaded modules?

steveblue commented 7 years ago

Well 157KB isn't bad. When an application scales its probably more beneficial to lazyload or prerender.

steveblue commented 7 years ago

@mlaval proved it can be done https://mlaval.github.io/optimize-angular-app/

dmitrysteblyuk commented 7 years ago

Thanks for the link! ClosureCompilre produces the smallest bundle indeed. Prerender is also super fast.