Angular2 加载文件请求过多
Angular2 too many file requests on load
我正在使用 Angular2
制作网站,我遇到了我认为是问题的问题。在我的 angular 页面第一次加载时,SystemJS
发出超过 50000 个请求来检索 angular2/src
目录中的每个 Angular2
文件。总的来说,第一次加载下载超过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>
我的 public 文件夹结构如下:
.
├── img
├── styles
├── app
├── libs
| └── angular2
| └── systemjs
| └── rxjs
| └── jquery
| └── lodash
| └── bootstrap
└── index.html
正在请求的一些 js 文件的几个屏幕截图:
有没有办法避免所有这些请求?
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.
SystemJs 工作流相当新,没有对其进行足够的研究以实现最佳部署。
建议回到 commonjs
+ webpack
。更多:https://basarat.gitbooks.io/typescript/content/docs/quick/browser.html
这是一个例子:https://github.com/AngularClass/angular2-webpack-starter
我认为你的问题与这个有关:
要为生产做好准备(并加快速度),您需要对其进行打包。
我的意思是将所有文件转译为 JavaScript 个文件,并以与 Angular2 相同的方式连接它们。这样,您将在一个 JS 文件中包含多个模块。这样,您将减少将应用程序代码加载到浏览器中的 HTTP 调用次数。
我遇到了完全相同的问题,实际上是在查看此 post 以寻求答案。这是我为解决问题所做的工作。
- 修改您的项目以使用 webpack。按照这个简短的教程:
Angular2 QuickStart SystemJS To Webpack
- 此方法将为您提供一个 javascript 文件,但它非常大(我的项目文件超过 5MB)并且需要缩小。为此,我全局安装了 webpack:
npm install webpack -g
。安装后,从您的应用程序根目录 运行 webpack -p
。这使我的文件大小减少到大约 700KB
从 20 秒和 350 个请求减少到 3 秒和 7 个请求。
我看到你已经有了回应,这当然很好。
但是对于那些想要使用 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.js
和 tsconfig.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 捆绑任务正常工作:
npm install --save-dev github:gulpjs/gulp#4.0 gulp-typescript gulp-sourcemaps path systemjs-builder
此外,请确保您的 tsconfig.json 中有 "module":"commonjs"
。
这是我的 tsconfig.json 用于我的 'ts'
gulp 任务:
{
"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.4mb 在 1.6 秒内还包括一些其他 js 和 css 管理主题附带的一些 html 模板,这些模板在第一次加载时需要,我更喜欢使用外部模板- templateUrl: '',而不是内联的,写在我的 component.ts 文件中。
当然,对于一个拥有数百万用户的应用程序来说,这仍然不够。还应该实现初始加载和缓存系统的服务器端渲染,我实际上设法用 angular universal 做到了这一点,但是在 Angular2 beta 上(花费了大约 200-240 毫秒 加载上述相同管理应用程序的初始渲染需要 1.6 秒 - 我知道:WOW!)。现在它是不兼容的,因为 Angular2 RC 出来了,但我相信那些做通用的人会很快加快速度,特别是因为 ng-conf 即将到来。此外,他们还计划让 Angular 通用 PHP、ASP 和其他一些 - 现在它只适用于 Nodejs。
编辑:
实际上,我刚刚发现在 NG-CONF 上他们说 Angular Universal 已经支持 ASP(但它不支持 Angular2 > beta.15 :)).. .不过还是给他们点时间吧,RC刚出来几天
如果您想坚持使用 SystemJS,可以将您的应用程序与 JSPM 捆绑在一起。到目前为止,我在这方面取得了很好的成功,使用 JSPM 的 bundle-sfx
命令为 Angular 2 个应用程序制作单个 JS 文件。
this Gist, and there's a seed project.
中有一些有用的信息
我用的是AG2 RC版
在将 MrCroft 的解决方案与 systemjs-builder 一起使用时,我遇到了很多问题,例如:
错误 TS2304:找不到名称 'Map'
错误 TS2304:找不到名称 'Promise'...
经过多次尝试,我补充说:
///<reference path="../../typings/index.d.ts" />
进入我的boot.ts,现在我编译了我的包文件。
我找到了一个简单的解决方案,在 mgechev's angular2-seed repository
上使用 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"
}
}
- 构建您的项目。
- 运行: npm 运行 build_prod
它将在 public\dist 目录下创建 bundle.js & bundle.min.js。
- 编辑您的
index.html
文件:
而不是 运行ning System.import('YourMainModule')... ,
添加 <script src="/dist/bundle.min.js"></script>
@FreeBird72 你的回答真棒。
如果你想像我一样使用 SystemJS 进行开发并加速生产服务器。看看这个。
注意:只导入您使用的组件,不要从整个包中导入。
例如:如果你想使用 ng2-bootstrap 中的模态。
import {MODAL_DIRECTIVES} from "ng2-bootstrap/components/modal";
而不是:
import {MODAL_DIRECTIVES} from "ng2-bootstrap/ng2-bootstrap";
这将导入模态组件而不是整个 ng2-bootstrap
然后按照@FreeBird72 的回答
添加这个package.json
{
...
"scripts": {
...
"prod": "npm run tsc && npm run browserify",
"browserify": "browserify -s main dist/main.js > dist/bundle.js && npm run minify",
"minify": "uglifyjs dist/bundle.js --screw-ie8 --compress --mangle --output dist/bundle.min.js",
...
},
"devDependencies": {
...
"browserify": "^13.0.1",
"uglifyjs": "^2.4.10",
...
}
...
}
然后您可以 npm run tsc
在开发服务器上 npm run prod
在生产服务器上
同时从 index.html 中删除 System.import(....
并将其更改为 <script src="/dist/bundle.min.js"></script>
Angular 命令行界面现在支持捆绑(通过 tree-shaking 从导入中去除未使用的代码)、缩小和提前模板编译,这不仅极大地减少了提出的要求,但也使捆绑包非常小。它在底层使用了 WebPack。
使用它进行生产构建非常容易:
ng build --prod --aot
我正在使用 Angular2
制作网站,我遇到了我认为是问题的问题。在我的 angular 页面第一次加载时,SystemJS
发出超过 50000 个请求来检索 angular2/src
目录中的每个 Angular2
文件。总的来说,第一次加载下载超过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>
我的 public 文件夹结构如下:
.
├── img
├── styles
├── app
├── libs
| └── angular2
| └── systemjs
| └── rxjs
| └── jquery
| └── lodash
| └── bootstrap
└── index.html
正在请求的一些 js 文件的几个屏幕截图:
有没有办法避免所有这些请求?
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.
SystemJs 工作流相当新,没有对其进行足够的研究以实现最佳部署。
建议回到 commonjs
+ webpack
。更多:https://basarat.gitbooks.io/typescript/content/docs/quick/browser.html
这是一个例子:https://github.com/AngularClass/angular2-webpack-starter
我认为你的问题与这个有关:
要为生产做好准备(并加快速度),您需要对其进行打包。
我的意思是将所有文件转译为 JavaScript 个文件,并以与 Angular2 相同的方式连接它们。这样,您将在一个 JS 文件中包含多个模块。这样,您将减少将应用程序代码加载到浏览器中的 HTTP 调用次数。
我遇到了完全相同的问题,实际上是在查看此 post 以寻求答案。这是我为解决问题所做的工作。
- 修改您的项目以使用 webpack。按照这个简短的教程: Angular2 QuickStart SystemJS To Webpack
- 此方法将为您提供一个 javascript 文件,但它非常大(我的项目文件超过 5MB)并且需要缩小。为此,我全局安装了 webpack:
npm install webpack -g
。安装后,从您的应用程序根目录 运行webpack -p
。这使我的文件大小减少到大约 700KB
从 20 秒和 350 个请求减少到 3 秒和 7 个请求。
我看到你已经有了回应,这当然很好。 但是对于那些想要使用 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.js
和 tsconfig.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 捆绑任务正常工作:
npm install --save-dev github:gulpjs/gulp#4.0 gulp-typescript gulp-sourcemaps path systemjs-builder
此外,请确保您的 tsconfig.json 中有 "module":"commonjs"
。
这是我的 tsconfig.json 用于我的 'ts'
gulp 任务:
{
"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.4mb 在 1.6 秒内还包括一些其他 js 和 css 管理主题附带的一些 html 模板,这些模板在第一次加载时需要,我更喜欢使用外部模板- templateUrl: '',而不是内联的,写在我的 component.ts 文件中。 当然,对于一个拥有数百万用户的应用程序来说,这仍然不够。还应该实现初始加载和缓存系统的服务器端渲染,我实际上设法用 angular universal 做到了这一点,但是在 Angular2 beta 上(花费了大约 200-240 毫秒 加载上述相同管理应用程序的初始渲染需要 1.6 秒 - 我知道:WOW!)。现在它是不兼容的,因为 Angular2 RC 出来了,但我相信那些做通用的人会很快加快速度,特别是因为 ng-conf 即将到来。此外,他们还计划让 Angular 通用 PHP、ASP 和其他一些 - 现在它只适用于 Nodejs。
编辑: 实际上,我刚刚发现在 NG-CONF 上他们说 Angular Universal 已经支持 ASP(但它不支持 Angular2 > beta.15 :)).. .不过还是给他们点时间吧,RC刚出来几天
如果您想坚持使用 SystemJS,可以将您的应用程序与 JSPM 捆绑在一起。到目前为止,我在这方面取得了很好的成功,使用 JSPM 的 bundle-sfx
命令为 Angular 2 个应用程序制作单个 JS 文件。
this Gist, and there's a seed project.
中有一些有用的信息我用的是AG2 RC版 在将 MrCroft 的解决方案与 systemjs-builder 一起使用时,我遇到了很多问题,例如: 错误 TS2304:找不到名称 'Map' 错误 TS2304:找不到名称 'Promise'...
经过多次尝试,我补充说:
///<reference path="../../typings/index.d.ts" />
进入我的boot.ts,现在我编译了我的包文件。
我找到了一个简单的解决方案,在 mgechev's angular2-seed repository
上使用 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"
}
}
- 构建您的项目。
- 运行: npm 运行 build_prod 它将在 public\dist 目录下创建 bundle.js & bundle.min.js。
- 编辑您的
index.html
文件: 而不是 运行ningSystem.import('YourMainModule')... ,
添加<script src="/dist/bundle.min.js"></script>
@FreeBird72 你的回答真棒。
如果你想像我一样使用 SystemJS 进行开发并加速生产服务器。看看这个。
注意:只导入您使用的组件,不要从整个包中导入。
例如:如果你想使用 ng2-bootstrap 中的模态。
import {MODAL_DIRECTIVES} from "ng2-bootstrap/components/modal";
而不是:
import {MODAL_DIRECTIVES} from "ng2-bootstrap/ng2-bootstrap";
这将导入模态组件而不是整个 ng2-bootstrap
然后按照@FreeBird72 的回答
添加这个package.json
{
...
"scripts": {
...
"prod": "npm run tsc && npm run browserify",
"browserify": "browserify -s main dist/main.js > dist/bundle.js && npm run minify",
"minify": "uglifyjs dist/bundle.js --screw-ie8 --compress --mangle --output dist/bundle.min.js",
...
},
"devDependencies": {
...
"browserify": "^13.0.1",
"uglifyjs": "^2.4.10",
...
}
...
}
然后您可以 npm run tsc
在开发服务器上 npm run prod
在生产服务器上
同时从 index.html 中删除 System.import(....
并将其更改为 <script src="/dist/bundle.min.js"></script>
Angular 命令行界面现在支持捆绑(通过 tree-shaking 从导入中去除未使用的代码)、缩小和提前模板编译,这不仅极大地减少了提出的要求,但也使捆绑包非常小。它在底层使用了 WebPack。
使用它进行生产构建非常容易:
ng build --prod --aot