我如何实际部署Angular 2 + Typescript + systemjs应用程序?


103

在angular.io上有一个快速入门教程,它使用打字稿和systemjs。既然我已经运行了该miniapp,我将如何创建可部署的东西?我找不到任何有关它的信息。

我是否需要任何其他工具,System.config中的任何其他设置?

(我知道我可以使用webpack并创建一个bundle.js,但我想使用本教程中使用的systemjs)

有人可以通过此设置(Angular 2,TypeScript,systemjs)共享其构建过程吗?


这是我使用JSPM建设NG2应用程序部署的食谱:stackoverflow.com/a/34616199/3532945
白兰度

2
简单的答案ng build -prod stackoverflow.com/a/38421680/5079380
Amr ElAdawy '16

Answers:


66

在此级别要理解的关键是,使用以下配置,您无法直接连接已编译的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文件将包含匿名模块。匿名模块是使用以下内容的JS文件System.register但不使用模块名称作为第一个参数。这是将systemjs配置为模块管理器时,默认情况下,打字稿编译器生成的内容。

因此,要将所有模块放入一个JS文件中,需要利用outFileTypeScript编译器配置中的属性。

您可以在内部使用以下代码来做到这一点:

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-replace
  • 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缩小的方法。


1
你能用一个例子创建一个github仓库吗?
jdelobel '16

我按照您的指示进行了,一切都很好。但是,当我在浏览器中运行该应用程序时,出现以下控制台日志错误:“ system.src.js:1625 Uncaught TypeError:同一模块文件中有多个匿名System.register调用。” 任何想法这意味着什么以及如何解决?
AngularM '16

@AngularM:您有outFile参数吗?这是您犯错的关键;-)
蒂埃里·坦佩尔

我将其保存在gulp文件和tsconfig中
AngularM '16

您能看看我提交的github项目吗?请参阅上面的评论。您看到代码有什么不同吗?
Thierry Templier,

28

您可以使用angular2-cli build命令

ng build -prod

https://github.com/angular/angular-cli/wiki/build#bundling

通过-prod标志创建的构建通过ng build -prod或将ng serve -prod所有依赖项捆绑到一个文件中,并利用树状摇动技术。

更新资料

这个问题是在angular2在rc4中时提交的

我已经在angular-cli beta21和angular2 ^ 2.1.0上再次尝试过,它按预期工作

要获得此答案,您需要使用可以使用的angular-cli初始化应用

ng new myApp

或在现有的

ng init

更新08/06/2018

对于角度6,语法是不同的。

ng build --prod --build-optimizer

检查文件


8
这就要求您的应用必须以angular-cli的自以为是的结构构建。
Michael Pell

2
@Amr ElAdawy FYI angular-cli移至webpack。这个问题与SystemJS有关。ng build对我不起作用。
Shahriar Hasan Sayeed

@ShahriarHasanSayeed您是指我提交答案的时间还是您尝试它的时间?
Amr ElAdawy '16

@AmrElAdawy,您可以为实际工作的模块添加版本吗?自7月以来,Angular2发生了很大变化。
ppovoski '16

2
将《英雄之旅》教程转换为cli版本并不容易。只需使用cli生成一个新项目,然后复制教程文件即可。
Rosdi Kasim

12

您可以使用带有GulpSystemJS-Builder的 SystemJS在Typescript中构建Angular 2(2.0.0-rc.1)项目。

以下是有关如何构建,捆绑和最小化运行2.0.0-rc.1的“英雄之旅”的简化版本(完整的源代码实时示例)。

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
});


2
您能否指定如何运行SystemJs和Gulp?
2013年

@JanDrozen在与gulpfile相同的位置,可以运行gulp <taskname>“ taskname”是调用SystemJS构建器的任务的名称,在上面的示例中是bundle:app。在该Gulp任务中,您可以使用'systemjs-builder'npm模块来指定系统配置和输出文件。
Steely

// @斯蒂利:谢谢!奇迹般有效。期望使用默认目标-缺少uglify()方法(或者我缺少某些东西)。您能为我解释一下这最后一个不清楚的部分吗?
Jan Drozen

@Steely,请您指导如何使用较新版本的angular2吗?
micronyks '16

@Steely。能否提供运行angular2 quickstart-app所需的最新angular2构建文件的最终链接(在github上)?
micronyks,2013年

1

这是我的Angular 2的MEA2N样板:https : //github.com/simonxca/mean2-boilerplate

这是一个tsc用于将事物组合在一起的简单样板。(实际上使用grunt-ts,其tsc本质只是命令。)不需要Wekpack等。

无论您是否使用咕unt咕,,它的想法是:

  • 在名为ts/(例如:public/ts/
  • 用于tscts/文件夹的目录结构镜像到一个js/文件夹中,并js/index.html

要获得咕噜-TS工作(应该有一个简单的TSC,咕嘟咕嘟等等效的命令),你在你的属性tsconfig.json"outDir": "../js",并在引用它gruntfile.js用:

grunt.initConfig({
  ts: {
    source: {tsconfig: 'app/ts/tsconfig.json'}
  },
  ...
});

然后运行grunt ts,它将带您的应用程序public/ts/并将其镜像到public/js/

那里。超级好理解。不是最好的方法,而是一种很好的入门方法。


1

我发现为systemJs捆绑角rc1的最简单方法是使用gulpsystemjs-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);
        });
});

正如评论中指出的那样,当使用以下方法捆绑组件时,systemJs当前存在问题 moduleId: module.id

https://github.com/angular/angular/issues/6131

当前的建议(角度2 rc1)似乎是使用显式路径,即 moduleId: '/app/path/'


这似乎很有希望,但是当我在@Component装饰器中使用到外部模板的相对路径时,它会失败。 bundle.js尝试将路径解析为绝对路径,即使它们是相对的,也会导致404错误(请参见stackoverflow.com/questions/37497635/…)。您如何处理?
BeetleJuice

您要设定moduleId相对路径吗?
2016年

不确定我是否理解。我moduleId: module.id@Component
BeetleJuice

这具有与完全走下路相同的缺点,templateUrl并且破坏moduleId了最初拥有的目的。我想建议使用相对路径(angular.io/docs/ts/latest/cookbook/...
哗鬼

您可以通过自己明确设置路径来获得更多的运气,例如moduleId: '/app/components/home/'
Paul


0

在Angular.io网站的“高级/部署”部分下,建议最简单的部署方法是“将开发环境复制到服务器”。

  1. 请仔细阅读以下部分:可能的最简单部署。最终的项目文件显示在代码部分中。请注意,它已经设置了从网络(而不是从本地npm_modules文件夹)加载npm软件包文件的代码。

  2. 确保它正在本地计算机上运行(npm start)。然后,在项目文件夹下,将“ / src”子文件夹下的所有内容复制到已设置的S3存储桶中。您可以使用拖放操作进行复制,在此过程中,您可以选择文件的权限设置,并确保将文件“可读”为“所有人”。

  3. 在存储桶的“属性”标签下,找到“静态网站托管”面板,选中“使用此存储桶托管网站”选项,并为索引文档和错误文档指定“ index.html”。

  4. 单击静态网站Endpoint,您的项目运行良好!

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.