components: Tree-shaking for production build is not working properly

Bug, feature request, or proposal:

The production bundle size is not shrinking

What is the expected behavior?

Base on the new package structure the production bundle (ng build --prod) should not have included all the Material Components Module

What is the current behavior?

Base on the change log now we should create our own Custom MaterialModule, in my case I have this one:

import { NgModule } from '@angular/core';
import {
    MdCardModule,
    MdChipsModule,
    MdToolbarModule,
    MdButtonModule,
    MdSidenavModule,
    MdRippleModule,
    MdIconModule,
    MdListModule,
    MdProgressSpinnerModule,
} from '@angular/material';

@NgModule({
    imports: [
        MdCardModule,
        MdChipsModule,
        MdToolbarModule,
        MdButtonModule,
        MdSidenavModule,
        MdRippleModule,
        MdIconModule,
        MdListModule,
        MdProgressSpinnerModule,
    ],
    declarations: [

    ],
    providers: [

    ],
    exports: [
        MdCardModule,
        MdChipsModule,
        MdToolbarModule,
        MdButtonModule,
        MdSidenavModule,
        MdRippleModule,
        MdIconModule,
        MdListModule,
        MdProgressSpinnerModule,
    ]
})
export class CustomMaterialModule { }

that is imported in the AppModule.

Even if I remove all the module and import only, for example, MdButtonModule, the size of material library in the production vendor.js still 308Kb

Which versions of Angular, Material, OS, browsers are affected?

@angular/cli: 1.0.0 node: 7.9.0 os: linux x64 @angular/animations: 4.0.1 @angular/common: 4.0.1 @angular/compiler: 4.0.1 @angular/core: 4.0.1 @angular/forms: 4.0.1 @angular/http: 4.0.1 @angular/material: 2.0.0-beta.3 @angular/platform-browser: 4.0.1 @angular/platform-browser-dynamic: 4.0.1 @angular/router: 4.0.1 @angular/cli: 1.0.0 @angular/compiler-cli: 4.0.1

Is there anything else we should know?

This is the vendor sourcemap generated by source-map-explorer image

About this issue

  • Original URL
  • State: closed
  • Created 7 years ago
  • Reactions: 31
  • Comments: 51 (25 by maintainers)

Most upvoted comments

Yep, just spent a while talking about this with the rest of the Angular core team. The gist is that the new packing structure doesn’t work out of the box with how webpack tries to eliminate dead code. We’re working on a solution and will publish a beta.4 as soon as we have a good approach.

@CardzMania just avoid importing modules from @angular/material:

Before:

import {MatButtonModule} from '@angular/material';

After:

import {MatButtonModule} from '@angular/material/button';

@jelbourn Any Update on tree-shaking?

@IgorMinar and @kara are actively working on it.

We’re working on it (experiment 1, experiment 2), but getting it to “just work” with existing tooling is somewhat tricky and we’re still working out the approach.

@angular-devkit/build-optimizer (formerly ngo+purify) support was added to @angular/cli@1.3.0-rc.0. You can read more about it in https://github.com/angular/angular-cli/pull/6520.

This should allow a lot of unused material classes to be dropped. Please let me know how it worked out for you - this is still experimental and it’s very important to work out the kinks before 1.3.0 final.

@julianobrasil to use the build optimizer you need to add the --build-optimizer flag (e.g. ng build --prod --build-optimizer).

We’ve seen much better results by not having the vendor bundle, so we default to it.

The combination of webpack 3 scope hoisting, build optimizer pure comments and UglifyJS allow a lot of code to be removed if it is in the same bundle. But when separating bundles, UglifyJS can no longer identify code to drop as well due to the indirection from the webpack loader.

So yes, you can re-add the vendor bundle by doing --vendor-chunk=true. But I advise you to compare the total size with and without it to make sure you’re not delivering unnecessary code.

@fknop @karolmie1 for me it is just got increased:

image was: 741.2k kb 60.03 gziped now 850.19 kb 69.11 kb gziped And I am just importing my custom module https://github.com/angular/material2/issues/4137#issuecomment-298827979

I’ve seen HUGE improvement using 1.3.0-rc.5! Having all of the material components and angular itself I previously had a “base” bundle which could be up to 2MB. After using ng build --prod --aot --build-optimizer the compiled code is around ~400kb which is absolutely awesome! @filipesilva job well done! 🥇 👍

Hash: 03b835ca4ff8a748a075
Version: webpack 3.4.1
Time: 56462ms
                              Asset       Size  Chunks                    Chunk Names
                    js/polyfills.js    62.4 kB       0  [emitted]         polyfills
                         js/main.js     337 kB       1  [emitted]  [big]  main
                       js/inline.js    1.36 kB       3  [emitted]         inline
css/styles.03b835ca4ff8a748a075.css    33.4 kB       2  [emitted]         styles
                        favicon.ico    5.43 kB          [emitted]         
                       ./index.html  516 bytes          [emitted]         
                ../../manifest.json  204 bytes          [emitted]         

905KB to 273KB is indeed a very big reduction 😄

Let me know if there’s any runtime problems with the app, we’re still ironing out bugs.

Why not just split into separate submodules files? Instead

import { 
  MdAutocompleteModule
  MdButtonModule,
} from } from '@angular/material';

We could have:

import { MdAutocompleteModule } from '@angular/material/MdAutocompleteModule';
import { MdButtonModule } from '@angular/material/MdButtonModule'

And user will automatically import only what is needed. You can still re-export everything under @angular/material.

Now I have a situation that my prod bundle is 1.5mb and large part is because of Material. I will try with aot, but the app is already barely usable in IE11. We go live on Monday and It looks like I will need to rewrite this to Bootstrap.

This is issue has been closed last year because we switched to the Angular package format that should have fixed this. If you are still seeing an issue in regards to tree-shaking, please open a new issue and maybe also use source-map-explorer, so that we can see what’s going on in your application.

Great, that worked! Had a hiccup along the way and noting here for others, just changing modules is not enough. Do remember to change all imports also:

Before: import { MatDialogRef } from '@angular/material';

After import { MatDialogRef } from '@angular/material/dialog';

Search for from '@angular/material' and just get rid of it!

Ohhh, thanks for the quick reply, let me try that. Must have missed it in the comments 😦

@julianobrasil let’s move this onto the CLI issue tracker now, this part isn’t about Material any more.

Can you open a new issue there with these logs, and perhaps show me the contents of ./src/app/model/service/dao/usuario.service.ts? If you can provide a repro it would be even better.

I’ll try it in a real project I’m working on. At the end of the day I’ll comment about the tests over here.