我如何实际部署 Angular 2 + Typescript + systemjs 应用程序?
Posted
技术标签:
【中文标题】我如何实际部署 Angular 2 + Typescript + systemjs 应用程序?【英文标题】:How do I actually deploy an Angular 2 + Typescript + systemjs app? 【发布时间】:2016-07-17 01:17:14 【问题描述】:在 angular.io 上有一个使用 typescript 和 systemjs 的快速入门教程。 现在我已经运行了那个小应用程序,我将如何创建可部署的东西?我找不到任何关于它的信息。
我是否需要任何额外的工具,System.config 中的任何其他设置?
(我知道我可以使用 webpack 并创建单个 bundle.js,但我想使用教程中使用的 systemjs)
有人可以用这个设置分享他们的构建过程吗(Angular 2、TypeScript、systemjs)
【问题讨论】:
这是我使用 JSPM 构建用于部署的 ng2 应用程序的秘诀:***.com/a/34616199/3532945 简单回答 ng build -prod ***.com/a/38421680/5079380 【参考方案1】:这一层要理解的关键是,使用下面的配置,不能直接concat编译好的JS文件。
在 TypeScript 编译器配置中:
"compilerOptions":
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"declaration": false,
"stripInternal": true,
"module": "system",
"moduleResolution": "node",
"noEmitOnError": false,
"rootDir": ".",
"inlineSourceMap": true,
"inlineSources": true,
"target": "es5"
,
"exclude": [
"node_modules"
]
在 html 中
System.config(
packages:
app:
defaultExtension: 'js',
format: 'register'
);
事实上,这些 JS 文件将包含匿名模块。匿名模块是一个使用 System.register
但没有模块名称作为第一个参数的 JS 文件。这是在将 systemjs 配置为模块管理器时 typescript 编译器默认生成的内容。
因此,要将所有模块放入一个 JS 文件中,您需要在 TypeScript 编译器配置中利用 outFile
属性。
您可以在 gulp 中使用以下内容:
const gulp = require('gulp');
const ts = require('gulp-typescript');
var tsProject = ts.createProject('tsconfig.json',
typescript: require('typescript'),
outFile: 'app.js'
);
gulp.task('tscompile', function ()
var tsResult = gulp.src('./app/**/*.ts')
.pipe(ts(tsProject));
return tsResult.js.pipe(gulp.dest('./dist'));
);
这可以与其他一些处理相结合:
丑化编译后的 TypeScript 文件 创建app.js
文件
为第三方库创建vendor.js
文件
创建一个boot.js
文件来导入引导应用程序的模块。此文件必须包含在页面末尾(当所有页面都加载完毕时)。
更新index.html
以考虑这两个文件
gulp 任务中使用了以下依赖项:
gulp-concat gulp-html-替换 gulp 打字稿 gulp-丑化以下是一个示例,因此可以进行调整。
创建app.min.js
文件
gulp.task('app-bundle', function ()
var tsProject = ts.createProject('tsconfig.json',
typescript: require('typescript'),
outFile: 'app.js'
);
var tsResult = gulp.src('app/**/*.ts')
.pipe(ts(tsProject));
return tsResult.js.pipe(concat('app.min.js'))
.pipe(uglify())
.pipe(gulp.dest('./dist'));
);
创建vendors.min.js
文件
gulp.task('vendor-bundle', function()
gulp.src([
'node_modules/es6-shim/es6-shim.min.js',
'node_modules/systemjs/dist/system-polyfills.js',
'node_modules/angular2/bundles/angular2-polyfills.js',
'node_modules/systemjs/dist/system.src.js',
'node_modules/rxjs/bundles/Rx.js',
'node_modules/angular2/bundles/angular2.dev.js',
'node_modules/angular2/bundles/http.dev.js'
])
.pipe(concat('vendors.min.js'))
.pipe(uglify())
.pipe(gulp.dest('./dist'));
);
创建boot.min.js
文件
gulp.task('boot-bundle', function()
gulp.src('config.prod.js')
.pipe(concat('boot.min.js'))
.pipe(uglify())
.pipe(gulp.dest('./dist'));
);
config.prod.js
仅包含以下内容:
System.import('boot')
.then(null, console.error.bind(console));
更新index.html
文件
gulp.task('html', function()
gulp.src('index.html')
.pipe(htmlreplace(
'vendor': 'vendors.min.js',
'app': 'app.min.js',
'boot': 'boot.min.js'
))
.pipe(gulp.dest('dist'));
);
index.html
如下所示:
<html>
<head>
<!-- Some CSS -->
<!-- build:vendor -->
<script src="node_modules/es6-shim/es6-shim.min.js"></script>
<script src="node_modules/systemjs/dist/system-polyfills.js"></script>
<script src="node_modules/angular2/bundles/angular2-polyfills.js"></script>
<script src="node_modules/systemjs/dist/system.src.js"></script>
<script src="node_modules/rxjs/bundles/Rx.js"></script>
<script src="node_modules/angular2/bundles/angular2.dev.js"></script>
<script src="node_modules/angular2/bundles/http.dev.js"></script>
<!-- endbuild -->
<!-- build:app -->
<script src="config.js"></script>
<!-- endbuild -->
</head>
<body>
<my-app>Loading...</my-app>
<!-- build:boot -->
<!-- endbuild -->
</body>
</html>
请注意,System.import('boot');
必须在正文末尾完成,以等待您的所有应用组件从 app.min.js
文件中注册。
我不在这里描述处理 CSS 和 HTML 缩小的方法。
【讨论】:
你能用一个例子创建一个github repo吗? 我按照您的指示进行操作,gulp 看起来一切正常。但是,当我在浏览器中运行该应用程序时,我收到此控制台日志错误:“system.src.js:1625 Uncaught TypeError: Multiple anonymous System.register calls in the same module file.”任何想法这意味着什么以及如何解决它? @AngularM:你有 outFile 参数吗?这是您错误的关键;-) 我的 gulp 文件和 tsconfig 中有它 可以看看我提交的github项目吗?请参阅我上面的评论。你发现你的代码有什么不同吗?【参考方案2】:你可以使用angular2-cli构建命令
ng build -prod
https://github.com/angular/angular-cli/wiki/build#bundling
通过
ng build -prod
或ng serve -prod
使用-prod 标志创建的构建将所有依赖项捆绑到一个单个文件,并利用tree-shaking 技术。
更新
这个答案是angular2在rc4时提交的
我在 angular-cli beta21 和 angular2 ^2.1.0 上再次尝试过,它按预期工作
这个答案需要使用 angular-cli 初始化应用程序 你可以使用
ng new myApp
或者在现有的上
ng init
2018 年 8 月 6 日更新
对于 Angular 6,语法不同。
ng build --prod --build-optimizer
查看documentation
【讨论】:
这要求您的应用采用 angular-cli 自以为是的结构。 @Amr ElAdawy 仅供参考 angular-cli 已移至 webpack。这个问题与 SystemJS 有关。 ng build 不适合我。 @ShahriarHasanSayeed 你是指我提交答案的时间还是你尝试的时间? @AmrElAdawy,您能否为实际工作的模块添加版本。自 7 月以来,Angular2 发生了很大变化。 将《英雄之旅》教程转换为 cli 版本很简单。只需使用 cli 生成一个新项目,然后将教程文件复制过来。【参考方案3】:您可以使用带有Gulp 和SystemJS-Builder 的SystemJS 在Typescript 中构建一个Angular 2 (2.0.0-rc.1) 项目。
以下是如何构建、捆绑和缩小运行 2.0.0-rc.1(full source、live example)的《英雄之旅》的简化版本。
gulpfile.js
var gulp = require('gulp');
var sourcemaps = require('gulp-sourcemaps');
var concat = require('gulp-concat');
var typescript = require('gulp-typescript');
var systemjsBuilder = require('systemjs-builder');
// Compile TypeScript app to JS
gulp.task('compile:ts', function ()
return gulp
.src([
"src/**/*.ts",
"typings/*.d.ts"
])
.pipe(sourcemaps.init())
.pipe(typescript(
"module": "system",
"moduleResolution": "node",
"outDir": "app",
"target": "ES5"
))
.pipe(sourcemaps.write('.'))
.pipe(gulp.dest('app'));
);
// Generate systemjs-based bundle (app/app.js)
gulp.task('bundle:app', function()
var builder = new systemjsBuilder('public', './system.config.js');
return builder.buildStatic('app', 'app/app.js');
);
// Copy and bundle dependencies into one file (vendor/vendors.js)
// system.config.js can also bundled for convenience
gulp.task('bundle:vendor', function ()
return gulp.src([
'node_modules/jquery/dist/jquery.min.js',
'node_modules/bootstrap/dist/js/bootstrap.min.js',
'node_modules/es6-shim/es6-shim.min.js',
'node_modules/es6-promise/dist/es6-promise.min.js',
'node_modules/zone.js/dist/zone.js',
'node_modules/reflect-metadata/Reflect.js',
'node_modules/systemjs/dist/system-polyfills.js',
'node_modules/systemjs/dist/system.src.js',
])
.pipe(concat('vendors.js'))
.pipe(gulp.dest('vendor'));
);
// Copy dependencies loaded through SystemJS into dir from node_modules
gulp.task('copy:vendor', function ()
gulp.src(['node_modules/rxjs/**/*'])
.pipe(gulp.dest('public/lib/js/rxjs'));
gulp.src(['node_modules/angular2-in-memory-web-api/**/*'])
.pipe(gulp.dest('public/lib/js/angular2-in-memory-web-api'));
return gulp.src(['node_modules/@angular/**/*'])
.pipe(gulp.dest('public/lib/js/@angular'));
);
gulp.task('vendor', ['bundle:vendor', 'copy:vendor']);
gulp.task('app', ['compile:ts', 'bundle:app']);
// Bundle dependencies and app into one file (app.bundle.js)
gulp.task('bundle', ['vendor', 'app'], function ()
return gulp.src([
'app/app.js',
'vendor/vendors.js'
])
.pipe(concat('app.bundle.js'))
.pipe(uglify())
.pipe(gulp.dest('./app'));
);
gulp.task('default', ['bundle']);
system.config.js
var map =
'app': 'app',
'rxjs': 'vendor/rxjs',
'zonejs': 'vendor/zone.js',
'reflect-metadata': 'vendor/reflect-metadata',
'@angular': 'vendor/@angular'
;
var packages =
'app': main: 'main', defaultExtension: 'js' ,
'rxjs': defaultExtension: 'js' ,
'zonejs': main: 'zone', defaultExtension: 'js' ,
'reflect-metadata': main: 'Reflect', defaultExtension: 'js'
;
var packageNames = [
'@angular/common',
'@angular/compiler',
'@angular/core',
'@angular/http',
'@angular/platform-browser',
'@angular/platform-browser-dynamic',
'@angular/router',
'@angular/router-deprecated',
'@angular/testing',
'@angular/upgrade',
];
packageNames.forEach(function(pkgName)
packages[pkgName] = main: 'index.js', defaultExtension: 'js' ;
);
System.config(
map: map,
packages: packages
);
【讨论】:
能否请您具体说明,如何运行 SystemJs 和 Gulp? @JanDrozen 在与 gulpfile 相同的位置,您可以运行gulp <taskname>
,其中“taskname”是调用 SystemJS 构建器的任务的名称,在我上面的示例中是 bundle:app
。在那个 Gulp 任务中,您可以使用“systemjs-builder”npm 模块来指定您的系统配置和输出文件。
@steely:谢谢!奇迹般有效。期望默认目标 - 缺少 uglify() 方法(或者我缺少某些东西)。你能解释一下最后不清楚的部分吗?
@Steely 请指导如何使用较新版本的 angular2?
@Steely.你能否提供运行 angular2 quickstart-app 所需的最新 angular2 构建文件的最终链接(在 github 上)?【参考方案4】:
这是 Angular 2 的 MEA2N 样板:https://github.com/simonxca/mean2-boilerplate
这是一个简单的样板文件,它使用tsc
将东西放在一起。 (实际上使用grunt-ts,其核心只是tsc
命令。)不需要Wekpack 等。
无论你是否使用 grunt,思路都是:
将您的应用程序写入名为ts/
的文件夹中(例如:public/ts/
)
使用tsc
将ts/
文件夹的目录结构镜像到js/
文件夹中,并仅引用index.html
中js/
文件夹中的文件。
要让 grunt-ts 工作(应该有一个普通 tsc、Gulp 等的等效命令),您的 tsconfig.json
中有一个名为 "outDir": "../js"
的属性,并参考它在你的gruntfile.js
中:
grunt.initConfig(
ts:
source: tsconfig: 'app/ts/tsconfig.json'
,
...
);
然后运行grunt ts
,这会将您的应用程序放入public/ts/
并将其镜像到public/js/
。
那里。超级容易理解。不是最好的方法,但却是一个很好的入门方法。
【讨论】:
【参考方案5】:我发现为 systemJs 捆绑 angular rc1 的最简单方法是使用 gulp
和 systemjs-builder
:
gulp.task('bundle', function ()
var path = require('path');
var Builder = require('systemjs-builder');
var builder = new Builder('/node_modules');
return builder.bundle([
'@angular/**/*.js'
],
'wwwroot/bundle.js',
minify: false, sourceMaps: false )
.then(function ()
console.log('Build complete');
)
.catch(function (err)
console.log('Build error');
console.log(err);
);
);
正如 cmets 中所指出的,systemJs 目前在使用 moduleId: module.id
捆绑组件时存在问题
https://github.com/angular/angular/issues/6131
目前的建议(角度 2 rc1)似乎是使用显式路径,即moduleId: '/app/path/'
【讨论】:
这看起来很有希望,但是当我在@Component
装饰器中使用外部模板的相对路径时它会失败。 bundle.js
尝试将路径解析为绝对路径,即使它们是相对的,也会导致 404 错误(请参阅 ***.com/questions/37497635/…)。你是怎么处理的?
您是否将moduleId
设置为相对路径?
不确定我是否理解。我在@Component
中有moduleId: module.id
这与将完整路径放在templateUrl
下具有相同的缺点,并且首先破坏了拥有moduleId
的目的。我正在尝试按照推荐使用相对路径 (angular.io/docs/ts/latest/cookbook/…)
您可以通过自己明确设置路径来获得更多运气,例如moduleId: '/app/components/home/'
【参考方案6】:
在 Angular.io 网站的 Advanced/Deployment 部分下,建议最简单的部署方式是“将开发环境复制到服务器”。
浏览以下部分:最简单的部署。最终的项目文件显示在代码部分中。请注意,它已经设置了从 web 加载 npm 包文件的代码(而不是从本地 npm_modules 文件夹)。
确保它正在您的本地计算机上运行 (npm start)。然后在项目文件夹下,将“/src”子文件夹下的所有内容复制到您设置的 S3 存储桶中。您可以使用拖放进行复制,在此过程中,您可以选择文件的权限设置,确保它们对“每个人”都“可读”。
在存储桶“属性”选项卡下,查找“静态网站托管”面板,选中“使用此存储桶托管网站”选项,并为索引文档和错误文档指定“index.html”。
点击静态网站Endpoint,你的项目运行良好!
【讨论】:
以上是关于我如何实际部署 Angular 2 + Typescript + systemjs 应用程序?的主要内容,如果未能解决你的问题,请参考以下文章
如何部署我的 Angular 2 + Typescript + Webpack 应用程序
在 Angular 5 中,如何导入没有 @types 的 NPM 模块