Angular2 加载时文件请求过多

IT技术 javascript typescript angular systemjs
2021-01-29 16:06:52

我正在使用一个网站Angular2,但我遇到了我认为的问题。在我的 angular 页面的第一次加载时,SystemJS发出超过 50000 个请求来检索目录中的每个Angular2文件angular2/src总共,第一次加载下载超过 4MB,启动时间超过 14 秒。

index.html的以下脚本包括:

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

我的 systemJs 初始化代码如下所示:

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

我的公用文件夹具有以下结构:

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

一些正在请求的 js 文件的屏幕截图: 在此处输入图片说明

在此处输入图片说明

有没有办法避免所有这些请求?

6个回答

我遇到了完全相同的问题,实际上是在看这篇文章以寻求答案。这是我为解决问题所做的工作。

  1. 修改您的项目以使用 webpack。按照这个简短的教程: Angular2 QuickStart SystemJS 到 Webpack
  2. 此方法将为您提供一个 javascript 文件,但它非常大(我的项目文件超过 5MB)并且需要缩小。为此,我全局安装了 webpack:npm install webpack -g. 安装后,webpack -p从您的应用程序根目录运行这使我的文件大小减少到大约 700KB

从 20 秒和 350 个请求减少到 3 秒和 7 个请求。

这太简单直接了,这应该有1000个赞!将我的加载时间从 300 多个请求减少到大约 20 个。
2021-04-01 16:06:52
我刚刚在官方文档中找到了这个链接:angular.io/docs/ts/latest/guide/webpack.html也许它更相似,但也许它会帮助某人
2021-04-09 16:06:52
@DavidHerod 在 webpack -p 之后运行项目的命令应该是什么?是“npm start”还是“npm run server:prod”或其他任何东西?
2021-04-10 16:06:52
不要忘记在您的服务器上启用 gzip。应该再减少 50% 的文件大小。
2021-04-13 16:06:52

我看到你已经有了回应,这当然很好。但是对于那些想要使用systemjs(就像我也这样做)而不是去 webpack 的人,你仍然可以捆绑这些文件。但是,它也涉及使用另一个工具(我使用 gulp)。所以......你会有以下 systemjs 配置(不是在 html 中,而是在一个单独的文件中 - 我们称之为“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);

然后,在您的 gulpfile.js 中,您将构建一个这样的包(使用来自system.config.jstsconfig.json文件的信息):

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']));

因此,当运行“gulp build”时,您将获得包含您需要的所有内容的“bundle.js”文件。当然,你还需要一些更多的包来让这个 gulp bundle 任务工作:

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

另外,请确保在您的 tsconfig.json 中有"module":"commonjs". 这是我的 gulp'ts'任务中使用的 tsconfig.json

{
    "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"
    ]
}

然后,在您的 html 文件中,您只需要包含以下内容:

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

就是这样......我从 600 个请求,大约 5 秒内得到 4mb......到 20 个请求,1.6 秒内得到 1.4mb(本地开发机器)。但是这 20 个请求在 1.6 秒内约 1.4mb 还包括管理主题附带的一些其他 js 和 css 以及一些在第一次加载时需要的 html 模板,我更喜欢使用外部模板 - templateUrl: '',而不是内联的,写在我的 component.ts 文件中。当然,对于拥有数百万用户的应用程序来说,这仍然不够。还应该实现初始加载和缓存系统的服务器端渲染,我实际上设法用 angular 通用来做到这一点,但是在 Angular2 beta 上(大约需要200-240 毫秒来加载上面需要 1.6 的相同管理应用程序的初始渲染秒 - 我知道:哇!)。现在它不兼容,因为 Angular2 RC 出来了,但我相信做通​​用的人很快就会加快速度,特别是因为 ng-conf 即将推出。另外,他们还计划为 PHP、ASP 和其他一些工具制作 Angular Universal——现在它只适用于 Nodejs。

编辑: 实际上,我刚刚发现在 NG-CONF 上,他们说 Angular Universal 已经支持 ASP(但它不支持 Angular2 > beta.15 :))……但是让我们给他们一些时间,RC 刚来几天前出来

@webaba 现在使用 angular-cli 更容易(当然,只适用于使用 angular-cli 创建的应用程序)。换行符似乎在注释中不起作用,所以我用管道分隔命令: npm install angular-cli -g | ng 新的我的应用程序 | cd 我的应用程序 | ng serve -prod | 然后只需访问 localhost:4200 并检查 js 文件的 dist 文件夹
2021-03-31 16:06:52
谢谢。然而,我的请求从 671 变为 17 :( 我从我的 index.html 中删除了 system.js、systemjs.config.js 和 system.import 语句。这是正确的吗?现在我得到一个“module必须加载为 AMD 或CommonJS 的错误,我该如何解决?
2021-04-04 16:06:52
感谢您提供这个非常详细的答案,多亏了它,我才能让我的东西正常工作。
2021-04-06 16:06:52
@CurlyFro 我已经编辑了我的答案,现在包括整个过程,而不仅仅是捆绑文件。希望你刚刚忽略了一些东西,你会马上意识到它。这个设置对我有用。我的意思是我仍然拥有它,因为我没有删除 fodler,我刚刚测试了它。但实际上不再使用它了。我现在使用 angular cli(Alpha,不建议尽快启动任何更大的应用程序进行生产)。
2021-04-09 16:06:52
听起来很酷,我的应用程序变得非常庞大,我不确定我是否可以迁移到 angular-cli,我最终手工制作了类似的东西,根据 NODE_ENV 环境变量提供不同的 html 文件。
2021-04-13 16:06:52

我认为你的问题与这个有关:

要为生产做好准备(并加快生产速度),您需要对其进行打包。

我的意思是将所有文件转换为 JavaScript 文件,并以与 Angular2 相同的方式连接它们,例如。这样,您将在一个 JS 文件中包含多个module。通过这种方式,您将减少将应用程序代码加载到浏览器的 HTTP 调用次数。

我找到了一个简单的解决方案,在mgechev 的 angular2-seed 存储库上使用browserify和 uglifyjs

这是我的版本:

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. 构建您的项目。
  2. 运行:npm run build_prod 会在public\dist目录下创建bundle.js & bundle.min.js。
  3. 编辑您的index.html文件:而不是运行System.import('YourMainModule')... , 添加<script src="/dist/bundle.min.js"></script>
在所有答案中,这是唯一一个真正有效的答案,唯一的问题是 a) 缩小脚本失败...即使在删除--mangle...之后,能够通过gulp. ..
2021-03-30 16:06:52
实际上我确实让它与 gulp 一起使用 gulp-exec
2021-04-05 16:06:52
尝试从“缩小”脚本中删除“--mangle”。在我写下答案后,我遇到了“--mangle”的问题
2021-04-06 16:06:52
@FreeBirs72 我试过你的答案,但现在我的 Angular 2 应用程序在删除 System.import 后不会启动。有任何想法吗?
2021-04-13 16:06:52

在我的 angular 页面的第一次加载时,systemjs 发出超过 50000 个请求来检索 angular2/src 目录中的每个 angular2 文件。总的来说,第一次加载下载超过 4mb,启动时间超过 14 秒。

SystemJs 工作流是相当新的,没有足够的研究来实现最佳部署。

建议回到commonjs+ webpack更多:https : //basarat.gitbooks.io/typescript/content/docs/quick/browser.html

这是一个例子:https : //github.com/AngularClass/angular2-webpack-starter