How to decrease prod bundle size?
Asked Answered
B

16

183

I have a simple app, initialized by angular-cli.

It display some pages relative to 3 routes. I have 3 components. On one of this page I use lodash and Angular 2 HTTP modules to get some data (using RxJS Observables, map and subscribe). I display these elements using a simple *ngFor.

But, despite the fact my app is really simple, I get a huge (in my opinion) bundle package and maps. I don't talk about gzip versions though but size before gzipping. This question is just a general recommendations inquiry.

Some tests results:

ng build

Hash: 8efac7d6208adb8641c1 Time: 10129ms chunk {0} main.bundle.js, main.bundle.map (main) 18.7 kB {3} [initial] [rendered]

chunk {1} styles.bundle.css, styles.bundle.map, styles.bundle.map (styles) 155 kB {4} [initial] [rendered]

chunk {2} scripts.bundle.js, scripts.bundle.map (scripts) 128 kB {4} [initial] [rendered]

chunk {3} vendor.bundle.js, vendor.bundle.map (vendor) 3.96 MB [initial] [rendered]

chunk {4} inline.bundle.js, inline.bundle.map (inline) 0 bytes [entry] [rendered]

Wait: 10Mb vendor bundle package for such a simple app?

ng build --prod

Hash: 09a5f095e33b2980e7cc Time: 23455ms chunk {0} main.6273b0f04a07a1c2ad6c.bundle.js, main.6273b0f04a07a1c2ad6c.bundle.map (main) 18.3 kB {3} [initial] [rendered]

chunk {1} styles.bfdaa4d8a4eb2d0cb019.bundle.css, styles.bfdaa4d8a4eb2d0cb019.bundle.map, styles.bfdaa4d8a4eb2d0cb019.bundle.map (styles) 154 kB {4} [initial] [rendered]

chunk {2} scripts.c5b720a078e5464ec211.bundle.js, scripts.c5b720a078e5464ec211.bundle.map (scripts) 128 kB {4} [initial] [rendered]

chunk {3} vendor.07af2467307e17d85438.bundle.js, vendor.07af2467307e17d85438.bundle.map (vendor) 3.96 MB [initial] [rendered]

chunk {4} inline.a345391d459797f81820.bundle.js, inline.a345391d459797f81820.bundle.map (inline) 0 bytes [entry] [rendered]

Wait again: such a similar vendor bundle size for prod?

ng build --prod --aot

Hash: 517e4425ff872bbe3e5b Time: 22856ms chunk {0} main.95eadabace554e3c2b43.bundle.js, main.95eadabace554e3c2b43.bundle.map (main) 130 kB {3} [initial] [rendered]

chunk {1} styles.e53a388ae1dd2b7f5434.bundle.css, styles.e53a388ae1dd2b7f5434.bundle.map, styles.e53a388ae1dd2b7f5434.bundle.map (styles) 154 kB {4} [initial] [rendered]

chunk {2} scripts.e5c2c90547f3168a7564.bundle.js, scripts.e5c2c90547f3168a7564.bundle.map (scripts) 128 kB {4} [initial] [rendered]

chunk {3} vendor.41a6c1f57136df286f14.bundle.js, vendor.41a6c1f57136df286f14.bundle.map (vendor) 2.75 MB [initial] [rendered]

chunk {4} inline.97c0403c57a46c6a7920.bundle.js, inline.97c0403c57a46c6a7920.bundle.map (inline) 0 bytes [entry] [rendered]

ng build --aot

Hash: 040cc91df4df5ffc3c3f Time: 11011ms chunk {0} main.bundle.js, main.bundle.map (main) 130 kB {3} [initial] [rendered]

chunk {1} styles.bundle.css, styles.bundle.map, styles.bundle.map (styles) 155 kB {4} [initial] [rendered]

chunk {2} scripts.bundle.js, scripts.bundle.map (scripts) 128 kB {4} [initial] [rendered]

chunk {3} vendor.bundle.js, vendor.bundle.map (vendor) 2.75 MB [initial] [rendered]

chunk {4} inline.bundle.js, inline.bundle.map (inline) 0 bytes [entry] [rendered]

So a few questions for deploying my app on prod:

  • Why are the vendor bundles so huge?
  • Is tree shaking properly used by angular-cli?
  • How to improve this bundle size?
  • Are the .map files required?
  • Are the testing features included in bundles? I don't need them in prod.
  • Generic question: what are the recommanded tools to pack for prod? Maybe angular-cli (using Webpack in the background) is not the best option? Can we do better?

I searched many discussions on Stack Overflow, but I haven't found any generic question.

Burnt answered 2/1, 2017 at 20:0 Comment(3)
To learn more about angular 2 app optimization, check out this: github.com/mgechev/angular-performance-checklist#introductionBurchfield
But I don't think we should care that much, angular-cli will evolve and things will be done better and better. If you need some feature which angular-cli doesn't have, just submit an issue in their repo: github.com/angular/angular-cliBurchfield
while I think @Burchfield is right in some ways, if anyone is trying to deploy Angular2 into production they should care about bundle sizes as this directly affects app performance. The angular performance checklist is a great resource to see what can be improved. the angular team is working towards reducing bundle sizes. Excited to see where it goes!Sword
S
101

Update February 2020

Since this answer got a lot of traction, I thought it would be best to update it with newer Angular optimizations:

  1. As another answerer said, ng build --prod --build-optimizer is a good option for people using less than Angular v5. For newer versions, this is done by default with ng build --prod
  2. Another option is to use module chunking/lazy loading to better split your application into smaller chunks
  3. Ivy rendering engine comes by default in Angular 9, it offers better bundle sizes
  4. Make sure your 3rd party deps are tree shakeable. If you're not using Rxjs v6 yet, you should be.
  5. If all else fails, use a tool like webpack-bundle-analyzer to see what is causing bloat in your modules
  6. Check if you files are gzipped

Some claims that using AOT compilation can reduce the vendor bundle size to 250kb. However, in BlackHoleGalaxy's example, he uses AOT compilation and is still left with a vendor bundle size of 2.75MB with ng build --prod --aot, 10x larger than the supposed 250kb. This is not out of the norm for angular2 applications, even if you are using v4.0. 2.75MB is still too large for anyone who really cares about performance, especially on a mobile device.

There are a few things you can do to help the performance of your application:

  1. AOT & Tree Shaking (angular-cli does this out of the box). With Angular 9 AOT is by default on prod and dev environment.

  2. Using Angular Universal A.K.A. server-side rendering (not in cli)

  3. Web Workers (again, not in cli, but a very requested feature)
    see: https://github.com/angular/angular-cli/issues/2305

  4. Service Workers
    see: https://github.com/angular/angular-cli/issues/4006

You may not need all of these in a single application, but these are some of the options that are currently present for optimizing Angular performance. I believe/hope Google is aware of the out of the box shortcomings in terms of performance and plans to improve this in the future.

Here is a reference that talks more in depth about some of the concepts i mentioned above:

https://medium.com/@areai51/the-4-stages-of-perf-tuning-for-your-angular2-app-922ce5c1b294

Sword answered 12/4, 2017 at 1:44 Comment(7)
My minimal project is 384kB, see github.com/JCornat/min-angular I am sure there is easy way to optimize it, but it is close to 250kB !Maestricht
@JacquesCornat looks good! But your app only has a single component. The question is how to manage bundle sizes or larger applications. Often AOT does not reduce bundle sizes enough, and people are forced to look at other way to optimize. I highly recommend everyone to check out webpack-bundle-analyzer. A very helpful/easy way to see whats causing bloatSword
Unfortunately, utilities such as sw-precache refuse to deal with vendor bundles greater than 2MB.Peroxidase
@JackClancy "The question is how to manage bundle sizes or larger applications". No his question is "how to improve this bundle size?" and he is talking about his minimal app with 3 components. Anyway, talking about big bundles, using the AngularClass/angular-starter configuration, the same as in my repo, my bundle size for big apps went from 8MB (4MB without map files) to 580kB.Maestricht
Check out this blog post on medium about how rxjs 5.5 with lettable operators can decrease the size of your app and it contains a way to collect metrics about the size and where the big chunks are coming from: hackernoon.com/…Whitelaw
Service workers are now supported in angular angular.io/guide/service-worker-introArchiplasm
Thanks, you saved me. --prod reduced the app size from 6Mb to 1.2Mb. Still not perfect, but accepting since it is for desktop use only.Renshaw
H
24

Use latest angular cli version and use command ng build --prod --build-optimizer It will definitely reduce the build size for prod env.

This is what the build optimizer does under the hood:

The build optimizer has two main jobs. First, we are able to mark parts of your application as pure,this improves the tree shaking provided by the existing tools, removing additional parts of your application that aren’t needed.

The second thing the build optimizer does is to remove Angular decorators from your application’s runtime code. Decorators are used by the compiler, and aren’t needed at runtime and can be removed. Each of these jobs decrease the size of your JavaScript bundles, and increase the boot speed of your application for your users.

Note : One update for Angular 5 and up, the ng build --prod automatically take care of above process :)

Hygrophilous answered 18/10, 2017 at 7:8 Comment(2)
As for angular 6 right now, the vendor is still 4MB for only installing angular/material and firebaseDocumentary
That is a problem with angular/material, even I have faced that error. Tree shaking not done correctly is probably the root cause. With the new renderer engine IVY probably it would be solved.Hygrophilous
S
19

Lodash can contribute a bug chunk of code to your bundle depending on how you import from it. For example:

// includes the entire package (very large)
import * as _ from 'lodash';

// depending on your buildchain, may still include the entire package
import { flatten } from 'lodash';

// imports only the code needed for `flatten`
import flatten from 'lodash-es/flatten'

Personally I still wanted smaller footprints from my utility functions. E.g. flatten can contribute up to 1.2K to your bundle, after minimization. So I've been building up a collection of simplified lodash functions. My implementation of flatten contributes around 50 bytes. You can check it out here to see if it works for you: https://github.com/simontonsoftware/micro-dash

Steam answered 9/9, 2017 at 19:3 Comment(10)
2/ I tried the "loadash-es" tips from comment above. It reduces my bundle size of 0.08mb.Godrich
That's not much! Do you still have anything else importing lodash in the old way? You won't get any help unless everything is imported the new way.Steam
I think I ve replaced everything. Size wouldn't have change otherwise. I gave another tip below (gzip thing) that really helped us.Godrich
Interesting. How much of your bundle is/was lodash contributing to? (E.g. using source-map-explorer.)Steam
With this import change our unpacked bundle size decreased from 1.0MB to 663kB (213.4kB -> 126kB packed). We had to update every import across the app but it really wasn't difficult or time consuming.Mathis
Hi I tried to remove loadash completely because it's not used as other third libraries but the bundle size is still the same ..Battlefield
Got one question about micro-dash, does it support chaining methods like lodash 'chain' does? If yes, then how can we do it?Bra
@Bra No, it does not. It is only loose functions (which makes it very tree-shakeable)Steam
@EricSimonton Maybe you planning to implement this?Bra
@Bra I'm afraid not. The library is only meant to contain loose helpers that you import individually, so that it stays fully tree shakable.Steam
M
13

Firstly, vendor bundles are huge simply because Angular 2 relies on a lot of libraries. Minimum size for Angular 2 app is around 500KB (250KB in some cases, see bottom post).
Tree shaking is properly used by angular-cli.
Do not include .map files, because used only for debugging. Moreover, if you use hot replacement module, remove it to lighten vendor.

To pack for production, I personnaly use Webpack (and angular-cli relies on it too), because you can really configure everything for optimization or debugging.
If you want to use Webpack, I agree it is a bit tricky a first view, but see tutorials on the net, you won't be disappointed.
Else, use angular-cli, which get the job done really well.

Using Ahead-of-time compilation is mandatory to optimize apps, and shrink Angular 2 app to 250KB.

Here is a repo I created (github.com/JCornat/min-angular) to test minimal Angular bundle size, and I obtain 384kB. I am sure there is easy way to optimize it.

Talking about big apps, using the AngularClass/angular-starter configuration, the same as in the repo above, my bundle size for big apps (150+ components) went from 8MB (4MB without map files) to 580kB.

Maestricht answered 2/1, 2017 at 20:31 Comment(2)
What specific angular-starter config is it that helps reduce bundle size? Is it the webpack.config?Goblet
Yes, it is the webpack configuration that reduces the bundle size.Maestricht
J
10

All answers here are outdated. I just built Angular's standard boilerplate app called "Tour of Heroes" using Angular 13 CLI. Initial prod build took 10sec, subsequent finished in 3sec. Bundle size is 244kB before Gzip. The command I used to build was "ng build", no changes to the boilerplate project.

There is a lot of misinformation on this topic. Let's spread facts.

Build folder size, in bytes, by framework, .map files removed, no gzip.

  • upd 2023/01/18: Angular 15.1.0 - 153,803 (no router), 46.05 kB gzipped estimate.
  • upd 2023/01/18: Angular 15.1.0 - 236,373 (router), 64.83 kB gzipped estimate.
  • upd 2022/03/02: Angular 13.2.5 - 171,580 (ng new test-app, scss, no router). JS size 153.58kb, 46,90kb gzipped estimate, as provided by CLI console output.
  • upd 2022/03/02: Angular 13.2.5 - 249,449 (ng new angular-tour-of-heroes, scss, router). JS size 229.49kb, 64.91kb gzipped estimate, as provided by CLI.
  • Angular 13.0.0 - 264,563 (ng new angular-tour-of-heroes, scss, router).
  • React 17.0.2 without Router - 167,074 (npx create-react-app my-app).
  • React 17.0.2 with Router - 172,459 (npx create-react-app my-app, add "react-router-dom": "6.0.2" to package.json, wrap <App /> inside <BrowserRouter>, as described here).

enter image description here

Jeggar answered 6/11, 2021 at 12:41 Comment(4)
can you provide insight for react prod size with router included?Passive
@DeekshithAnand: I updated my answer. It's only +5KB increase.Jeggar
Can you explain what you did exactly? just using gzip? because other answers are outdated and I didn't find good resources for thatBattlefield
@RebaiAhmed: Steps to create the app are provided in my answer above. Use that to create an app. Build it, measure output JS size in a file manager. I used Total Commander. Actual gzipped size can be measured by configuring your webhost to support gzip compression for static assets. You can check in the network tab of Chrome Dev Tools (F12) if your resource arrived gzipped. It will show resource size and transfer size, transfer size is gzipped.Jeggar
G
9

The following solution assumes you are serving your dist/ folder using nodejs. Please use the following app.js in root level

const express = require('express'),http = require('http'),path = require('path'),compression = require('compression');

const app = express();

app.use(express.static(path.join(__dirname, 'dist')));
app.use(compression()) //compressing dist folder 
app.get('*', (req, res) => {
  res.sendFile(path.join(__dirname, 'dist/index.html'));
})

const port = process.env.PORT || '4201';
app.set('port', port);

const server = http.createServer(app);
server.listen(port, () => console.log('Running at port ' + port))

Make sure you install dependencies;

npm install compression --save
npm install express --save;

Now build the app

ng build --prod --build-optimizer

If you want to further compress the build say reduce 300kb(approx) from , then follow the below process;

Create a folder called vendor inside the src folder and inside vendor folder create a file rxjs.ts and paste the below code in it;

export {Subject} from 'rxjs/Subject';
export {Observable} from 'rxjs/Observable';
export {Subscription} from 'rxjs/Subscription';

And then add the follwing in the tsconfig.json file in your angular-cli application. Then in the compilerOptions , add the following json;

"paths": {
      "rxjs": [
        "./vendor/rxjs.ts"
      ]
    }

This will make your build size way too smaller. In my project I reduced the size from 11mb to 1mb. Hope it helps

Grenade answered 7/3, 2018 at 4:58 Comment(0)
F
6

One thing I wish to share is how imported libraries increase the size of the dist. I had angular2-moment package imported, whereas I could do all the date time formatting I required using the standard DatePipe exported from @angular/common.

With Angular2-Moment "angular2-moment": "^1.6.0",

chunk {0} polyfills.036982dc15bb5fc67cb8.bundle.js (polyfills) 191 kB {4} [initial] [rendered] chunk {1} main.e7496551a26816427b68.bundle.js (main) 2.2 MB {3} [initial] [rendered] chunk {2} styles.056656ed596d26ba0192.bundle.css (styles) 69 bytes {4} [initial] [rendered] chunk {3} vendor.62c2cfe0ca794a5006d1.bundle.js (vendor) 3.84 MB [initial] [rendered] chunk {4} inline.0b9c3de53405d705e757.bundle.js (inline) 0 bytes [entry] [rendered]

After removing Angular2-moment and using DatePipe instead

chunk {0} polyfills.036982dc15bb5fc67cb8.bundle.js (polyfills) 191 kB {4} [initial] [rendered] chunk {1} main.f2b62721788695a4655c.bundle.js (main) 2.2 MB {3} [initial] [rendered] chunk {2} styles.056656ed596d26ba0192.bundle.css (styles) 69 bytes {4} [initial] [rendered] chunk {3} vendor.e1de06303258c58c9d01.bundle.js (vendor) 3.35 MB [initial] [rendered] chunk {4} inline.3ae24861b3637391ba70.bundle.js (inline) 0 bytes [entry] [rendered]

Note the vendor bundle has reduced half a Megabyte!

Point is it is worth checking what angular standard packages can do even if you are already familiar with an external lib.

Feeley answered 9/10, 2017 at 6:20 Comment(0)
G
6

Another way to reduce bundle, is to serve GZIP instead of JS. We went from 2.6mb to 543ko.

https://httpd.apache.org/docs/2.4/mod/mod_deflate.html

Godrich answered 23/4, 2018 at 14:53 Comment(0)
P
6

update: 2022/05/23 with angular 14 (RC1)

g-zip and ng build --prod is two solution that very helpful. That will help you alot. But what if, you did anything, tried your best. and it still not work? using lazyload module. still not work?

Here is the answer for you. Currently I'm upgrade my Angular 13 to Angular 14 project using nx and I find out the "barrel side effect" cause your main.js bundle increase. The solution is simple.

  1. change your tsconfig.json build module from "module": "commonjs" to "module": "es2020" or esnext (you should use es2020 explain here https://web.archive.org/web/20220605011837/https://angular.io/guide/migration-update-module-and-target-compiler-options)

enter image description here

  1. add sideEffects: false to package.json and see the result:

enter image description here

Why?

full explain is here https://webpack.js.org/guides/tree-shaking/#conclusion and here https://github.com/angular/angular-cli/issues/16799 and here https://mcmap.net/q/94511/-barrel-file-and-tree-shaking. But I can make it short for you:

you have module "A is small", and "B is Big". you write them in UI library and export both of them in index.ts file. you import A in your project. final bundle still includes B which you dont want.

Why?

because commonjs is not smart. using es2020 is better, they understand which is use and which is not. But they not sure should it be remove or not. you add sideEffects: false to tell them: "clear dead codeis safe". and they did it. Then your bundle is small. this can use in any modern JS framework include React, VueJS

read the issue in angular project about barrel side effect here: https://github.com/angular/angular-cli/issues/16799

p/s: my production angular website is here: https://awread.vn with 100 users daily.

Palmore answered 23/5, 2022 at 2:26 Comment(2)
angular.io link is not foundPrintery
wew, since angular.io dead I change this to web.archive.org/web/20220605011837/https://angular.io/guide/…Palmore
D
3

It works 100%

ng build --prod --aot --build-optimizer --vendor-chunk=true
Deterge answered 23/4, 2020 at 18:43 Comment(0)
C
1

This did reduce the size in my case:

ng build --prod --build-optimizer --optimization.

For Angular 5+ ng-build --prod does this by default. Size after running this command reduced from 1.7MB to 1.2MB, but not enough for my production purpose.

I work on facebook messenger platform and messenger apps need to be lesser than 1MB to run on messenger platform. Been trying to figure out a solution for effective tree shaking but still no luck.

Capricecapricious answered 29/3, 2019 at 12:19 Comment(1)
BE CAREFUL with this solution/comment. For some reason after executing this, a ton of references were deleted and made a disaster on my project.Cottbus
S
1

If you are using Angular 8+ and you want to reduce the size of the bundle you can use Ivy. Ivy comes as the default view engine in Angular 9 Just go to src/tsconfig.app.json and add the angularCompilerOptions parameter, for example:

{
  "extends": ...,
  "compilerOptions":...,
  "exclude": ...,

/* add this one */ 
  "angularCompilerOptions": {
    "enableIvy": true
  }
}
Seguidilla answered 11/7, 2019 at 15:43 Comment(0)
P
1

If you have run ng build --prod - you shouldn't have vendor files at all.

If I run just ng build - I get these files:

enter image description here

The total size of the folder is ~14MB. Waat! :D

But if I run ng build --prod - I get these files:

enter image description here

The total size of the folder is 584K.

One and the same code. I have enabled Ivy in both cases. Angular is 8.2.13.

So - I guess you didn't add --prod to your build command?

Pileup answered 10/12, 2019 at 11:27 Comment(0)
M
1

Taken from the angular docs v9 (https://angular.io/guide/workspace-config#alternate-build-configurations):

By default, a production configuration is defined, and the ng build command has --prod option that builds using this configuration. The production configuration sets defaults that optimize the app in a number of ways, such as bundling files, minimizing excess whitespace, removing comments and dead code, and rewriting code to use short, cryptic names ("minification").

Additionally you can compress all your deployables with @angular-builders/custom-webpack:browser builder where your custom webpack.config.js looks like that:

module.exports = {
  entry: {
  },
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: '[name].[hash].js'
  },
  plugins: [
    new CompressionPlugin({
      deleteOriginalAssets: true,
    })
  ]
};

Afterwards you will have to configure your web server to serve compressed content e.g. with nginx you have to add to your nginx.conf:

server {
    gzip on;
    gzip_types      text/plain application/xml;
    gzip_proxied    no-cache no-store private expired auth;
    gzip_min_length 1000;
    ...
}

In my case the dist folder shrank from 25 to 5 mb after just using the --prod in ng build and then further shrank to 1.5mb after compression.

Motor answered 30/6, 2020 at 12:0 Comment(0)
H
0

I have a angular 5 + spring boot app(application.properties 1.3+) with help of compression(link attached below) was able to reduce the size of main.bundle.ts size from 2.7 MB to 530 KB.

Also by default --aot and --build-optimizer are enabled with --prod mode you need not specify those separately.

https://mcmap.net/q/137653/-using-gzip-compression-with-spring-boot-mvc-javaconfig-with-restful

Hammerhead answered 11/9, 2018 at 6:53 Comment(0)
C
0

Check you have configuration named "production" for ng build --prod, since it is shorthand for ng build --configuration=production No answer solved my problem, because the problem was sitting right in front of the screen. I think this might be quite common... I've internationalized the app with i18n renaming all configurations to e.g. production-en. Then I built with ng build --prod assuming, that the default optimization is used and should be close to optimal, but in fact just ng build has been executed resulting in 7mb bundle instead of 250kb.

Caputto answered 11/6, 2019 at 16:22 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.