Angular2 too many file requests on load

20,944

Solution 1

I had the exact same problem, was actually looking at this post for an answer. Here is what I did to solve the problem.

  1. Modify your project to use webpack. Follow this short tutorial: Angular2 QuickStart SystemJS To Webpack
  2. This method will give you a single javascript file however it is quite large (my project file was over 5MB) and needs to be minified. To do this I installed webpack globaly:npm install webpack -g. Once installed, run webpack -p from your apps root directory. This brought my file size down to about 700KB

From 20 seconds and 350 requests down to 3 seconds and 7 requests.

Solution 2

I see you already have a response, which is good of course. BUT for those who want to use systemjs (like I also do), and not go to webpack, you can still bundle the files. However, it does involve using another tool also (I use gulp). So... you would have the folowing systemjs config (not in the html, but in a separate file - let's call it "system.config.js"):

(function(global) {

    // map tells the System loader where to look for things
    var map = {
        'app':                        'dist/app', // this is where your transpiled files live
        'rxjs':                       'node_modules/rxjs',
        'angular2-in-memory-web-api': 'node_modules/angular2-in-memory-web-api', // this is something new since angular2 rc.0, don't know what it does
        '@angular':                   'node_modules/@angular'
    };

    // packages tells the System loader how to load when no filename and/or no extension
    var packages = {
        'app':                        { main: 'boot.js',  defaultExtension: 'js' },
        'rxjs':                       { defaultExtension: 'js' },
        'angular2-in-memory-web-api': { defaultExtension: 'js' }
    };

    var packageNames = [
        '@angular/common',
        '@angular/compiler',
        '@angular/core',
        '@angular/http',
        '@angular/platform-browser',
        '@angular/platform-browser-dynamic',
        //'@angular/router', // I still use "router-deprecated", haven't yet modified my code to use the new router that came with rc.0
        '@angular/router-deprecated',
        '@angular/http',
        '@angular/testing',
        '@angular/upgrade'
    ];

    // add package entries for angular packages in the form '@angular/common': { main: 'index.js', defaultExtension: 'js' }
    packageNames.forEach(function(pkgName) {
        packages[pkgName] = { main: 'index.js', defaultExtension: 'js' };
    });

    var config = {
        map: map,
        packages: packages
    };

    // filterSystemConfig - index.html's chance to modify config before we register it.
    if (global.filterSystemConfig) { global.filterSystemConfig(config); }

    System.config(config);
})(this);

Then, in your gulpfile.js you would build a bundle like this (using the info from system.config.js and tsconfig.json files):

var gulp = require('gulp'),
    path = require('path'),
    Builder = require('systemjs-builder'),
    ts = require('gulp-typescript'),
    sourcemaps  = require('gulp-sourcemaps');

var tsProject = ts.createProject('tsconfig.json');

var appDev = 'dev/app'; // where your ts files are, whatever the folder structure in this folder, it will be recreated in the below 'dist/app' folder
var appProd = 'dist/app';

/** first transpile your ts files */
gulp.task('ts', () => {
    return gulp.src(appDev + '/**/*.ts')
        .pipe(sourcemaps.init({
            loadMaps: true
        }))
        .pipe(ts(tsProject))
        .pipe(sourcemaps.write('.'))
        .pipe(gulp.dest(appProd));
});

/** then bundle */
gulp.task('bundle', function() {
    // optional constructor options
    // sets the baseURL and loads the configuration file
    var builder = new Builder('', 'dist/system.config.js');

    /*
       the parameters of the below buildStatic() method are:
           - your transcompiled application boot file (the one wich would contain the bootstrap(MyApp, [PROVIDERS]) function - in my case 'dist/app/boot.js'
           - the output (file into which it would output the bundled code)
           - options {}
    */
    return builder
        .buildStatic(appProd + '/boot.js', appProd + '/bundle.js', { minify: true, sourceMaps: true})
        .then(function() {
            console.log('Build complete');
        })
        .catch(function(err) {
            console.log('Build error');
            console.log(err);
        });
});

/** this runs the above in order. uses gulp4 */
gulp.task('build', gulp.series(['ts', 'bundle']));

So, when running "gulp build", you will get the "bundle.js" file with everything you need. Sure, you also need a few more packages for this gulp bundle task to work:

npm install --save-dev github:gulpjs/gulp#4.0 gulp-typescript gulp-sourcemaps path systemjs-builder

Also, make sure that in your tsconfig.json you have "module":"commonjs". Here is my tsconfig.json which is used in my 'ts' gulp task:

{
    "compilerOptions": {
        "target": "es5",
        "module": "commonjs",
        "moduleResolution": "node",
        "sourceMap": true,
        "emitDecoratorMetadata": true,
        "experimentalDecorators": true,
        "removeComments": false,
        "noImplicitAny": false
    },
    "exclude": [
        "node_modules",
        "typings/main",
        "typings/main.d.ts"
    ]
}

Then, in your html file you only need to include this:

<!-- Polyfill(s) for older browsers -->
<script src="node_modules/es6-shim/es6-shim.min.js"></script>

<script src="node_modules/zone.js/dist/zone.js"></script>
<script src="node_modules/reflect-metadata/Reflect.js"></script>
<script src="dist/app/bundle.js"></script>

And that's it... I got from 600 requests, 4mb in about 5 seconds... to 20 requests, 1.4mb in 1.6 seconds (local development machine). But these 20 requests ~1.4mb in 1.6 seconds also include some other js and css that the admin theme came with plus a few html templates that get required on the first load, I prefer to use external templates - templateUrl: '', instead of inline ones, written in my component.ts file. Sure, for an app that would have millions of users, this still wouldn't be enough. Also server-side rendering for initial load and cache system should be implemented, I actually managed to do that with angular universal, but on Angular2 beta (took about 200-240 milliseconds to load the initial render of the same admin app that above takes 1.6 seconds - I know: WOW!). Now it's incompatible since Angular2 RC came out, but I'm sure the guys doing universal will get it up to speed soon, specially since ng-conf is coming up. Plus, they're also planing to make Angular Universal for PHP, ASP and a few other - right now it's only for Nodejs.

Edit: Actually, I've just found out that on NG-CONF they said Angular Universal already supports ASP (but it doesn't support Angular2 > beta.15 :)) ... but let's give them some time, RC just came out a few days ago

Solution 3

I think that your question is related to this one:

To have something ready for production (and speed it up), you need to package it.

I mean transpiling all files into JavaScript ones and concat them the same way Angular2 does for example. This way you will have several modules contained into a single JS file. This way you will reduce the number of HTTP calls to load your application code into the browser.

Solution 4

I found a simple solution, using browserify & uglifyjs on mgechev's angular2-seed repository

Here's my version:

pacakge.json:

{
...
  "scripts": {
      "build_prod": "npm run clean && npm run browserify",
      "clean": "del /S/Q public\\dist",
      "browserify": "browserify -s main  public/YourMainModule.js > public/dist/bundle.js && npm run minify",
      "minify": "uglifyjs public/dist/bundle.js --screw-ie8 --compress --mangle --output public/dist/bundle.min.js"
    },
...
  "devDependencies": {
      "browserify": "^13.0.1",    
      "typescript": "^1.9.0-dev.20160625-1.0",
      "typings": "1.0.4",
      "uglifyjs": "^2.4.10"
    }
}
  1. Build your project.
  2. Run: npm run build_prod It'll create bundle.js & bundle.min.js under public\dist directory.
  3. Edit your index.html file: Instead of running System.import('YourMainModule')... , add <script src="/dist/bundle.min.js"></script>

Solution 5

On the first load of my angular page, systemjs is making more than 500 hundred requests to retrieve every angular2 file in angular2/src directory. In total, the first load downloads more than 4mb and it takes more than 14s to start.

The SystemJs workflows are fairly new and don't have enough research in them for best deployment.

Suggest going back to commonjs + webpack. More : https://basarat.gitbooks.io/typescript/content/docs/quick/browser.html

Here is an example : https://github.com/AngularClass/angular2-webpack-starter

Share:
20,944
Marcos Basualdo
Author by

Marcos Basualdo

Updated on July 09, 2022

Comments

  • Marcos Basualdo
    Marcos Basualdo almost 2 years

    I'm making a website using Angular2 and I'm having what i suppose is an issue. On the first load of my angular page, SystemJS is making more than 500 hundred requests to retrieve every Angular2 file in angular2/src directory. In total, the first load downloads more than 4MB and it takes more than 14 seconds to start.

    My index.html does the following scripts includes:

    <script src="libs/angular2/bundles/angular2-polyfills.js"></script>
    <script src="libs/systemjs/dist/system.src.js"></script>
    <script src="libs/rxjs/bundles/Rx.js"></script>
    <script src="libs/angular2/bundles/angular2.min.js"></script>
    <script src="libs/angular2/bundles/http.dev.js"></script>
    <script src="libs/jquery/jquery.js"></script>
    <script src="libs/lodash/lodash.js"></script>
    <script src="libs/bootstrap/js/bootstrap.js"></script>
    

    And my systemJs initialization code looks like this:

        <script>
          System.config({
            defaultJSExtensions: true,
            paths: {
              '*': 'libs/*',
              'app/*': 'app/*'
            },
            packageConfigPaths: ['libs/*/package.json'],
            packages: {
              app: {
                format: 'register',
                defaultExtension: 'js'
              }
            }
          });
          System.import('app/main')
                .then(null, console.error.bind(console));
    
        </script>
    

    My public folder has the following structure:

    .
    ├── img
    ├── styles
    ├── app
    ├── libs
    |   └── angular2
    |   └── systemjs
    |   └── rxjs
    |   └── jquery
    |   └── lodash
    |   └── bootstrap
    └── index.html
    

    A couple of screenshots of some of the js files that are being requested: enter image description here

    enter image description here

    Is there a way to avoid all of those requests?