Как на самом деле развернуть приложение Angular 2 + Typescript + systemjs?


103

На angular.io есть руководство по быстрому запуску, в котором используются typescript и systemjs. Теперь, когда у меня запущен этот miniapp, как мне создать что-то развертываемое? Я не мог найти никакой информации об этом.

Нужны ли мне дополнительные инструменты, какие-либо дополнительные настройки в System.config?

(Я знаю, что могу использовать webpack и создать один файл bundle.js, но я бы хотел использовать systemjs, как он используется в учебнике)

Может ли кто-нибудь поделиться своим процессом сборки с этой настройкой (Angular 2, TypeScript, systemjs)


Вот мой рецепт создания приложения ng2 для развертывания с использованием JSPM: stackoverflow.com/a/34616199/3532945
brando,

2
простой ответ ng build -prod stackoverflow.com/a/38421680/5079380
Амр ЭльАдави

Ответы:


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-файле, вам необходимо использовать это outFileсвойство в конфигурации компилятора TypeScript.

Для этого вы можете использовать следующий глоток:

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 используются следующие зависимости:

  • глоток-конкат
  • глоток-HTML-заменить
  • gulp-typescript
  • проглотить

Ниже приведен образец для адаптации.

  • Создать 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 05

Я выполнил ваши инструкции, и все выглядит нормально в отношении глотка. Однако, когда я запускаю приложение в браузере, я получаю эту ошибку журнала консоли: «system.src.js: 1625 Uncaught TypeError: несколько анонимных вызовов System.register в одном файле модуля». Есть идеи, что это значит и как это исправить?
AngularM

@AngularM: у вас есть параметр outFile? Это ключ к вашей ошибке ;-)
Тьерри Темплиер

У меня он есть в файле gulp и tsconfig
AngularM

Не могли бы вы взглянуть на представленный мною проект на github? См. Мой комментарий выше. Вы видите некоторые отличия в вашем коде?
Тьерри Темплиер

28

Вы можете использовать команду сборки angular2-cli

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

Для angular 6 синтаксис другой.

ng build --prod --build-optimizer

Проверить документацию


8
Для этого необходимо, чтобы ваше приложение было структурировано в соответствии с упорядоченной структурой angular-cli.
Майкл Пелл

2
@Amr ElAdawy FYI angular-cli переехал в webpack. Этот вопрос связан с SystemJS. ng build у меня не работает.
Shahriar Hasan Sayeed

@ShahriarHasanSayeed Вы имеете в виду время, когда я отправил ответ, или время, когда вы его пробовали?
Амр ЭльАдави

@AmrElAdawy, можете ли вы добавить версии модулей, в которых это действительно работает? Angular2 немного изменился с июля.
ppovoski

2
Преобразовать руководство Tour of Heroes в версию cli - тривиальная задача. Просто создайте новый проект с помощью cli, а затем скопируйте файлы руководства.
Росди Касим

12

Вы можете создать проект Angular 2 (2.0.0-rc.1) в Typescript, используя SystemJS с Gulp и SystemJS-Builder .

Ниже представлена ​​упрощенная версия сборки, объединения и минимизации Tour of Heroes под управлением 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?
Ян Дрозен

@JanDrozen В том же месте, что и ваш gulpfile, вы можете запустить, gulp <taskname>где «taskname» - это имя задачи, которая вызывает построитель SystemJS, в моем примере выше это так bundle:app. В этой задаче Gulp вы можете использовать модуль npm systemjs-builder, чтобы указать конфигурацию вашей системы и файл Outfile.
Steely

@ steely: Спасибо! Работает как шарм. Ожидайте достижения цели по умолчанию - метод uglify () отсутствует (или мне что-то не хватает). Не могли бы вы объяснить мне эту последнюю неясную часть?
Ян Дрозен

@Steely, пожалуйста, подскажите, как это сделать с новой версией angular2?
micronyks

@ Steely. Можете ли вы предоставить последнюю ссылку (на github) на последние файлы сборки angular2, которые необходимы для запуска приложения angular2 quickstart-app?
micronyks

1

Вот мой шаблон MEA2N для Angular 2: https://github.com/simonxca/mean2-boilerplate

Это простой шаблон, который используется tscдля объединения вещей. (На самом деле использует grunt-ts , который по своей сути является просто tscкомандой.) Нет необходимости в Wekpack и т.д.

Независимо от того, используете ли вы grunt, идея такова:

  • написать приложение в папке ts/(пример: public/ts/)
  • используйте tscдля зеркального отображения структуры каталогов вашей ts/папки в js/папку и просто ссылки на файлы в js/папке в вашем index.html.

Чтобы заставить 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/.

Там. Очень легко понять. Не лучший подход, но хороший для начала.


1

Самый простой способ связать angular rc1 для systemJs, который я нашел, - это использовать 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);
        });
});

Как указано в комментариях, у systemJs в настоящее время есть проблемы при объединении компонентов с использованием moduleId: module.id

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

Текущая рекомендация (angular 2 rc1), похоже, заключается в использовании явных путей, т.е. moduleId: '/app/path/'


Это кажется многообещающим, но не работает, когда я использую в @Componentдекораторе относительные пути к внешним шаблонам . bundle.jsпытается разрешить пути как абсолютные, даже если они относительные, что вызывает ошибку 404 (см. stackoverflow.com/questions/37497635/… ). Как вы с этим справились?
BeetleJuice

вы устанавливаете moduleIdотносительный путь?
Павел

Не уверен, что понимаю. Я имею moduleId: module.idв@Component
Beetlejuice

Это имеет те же недостатки, что и пренебрежение полным путем, templateUrlи сводит на нет цель иметь moduleIdв первую очередь. Я пытаюсь использовать относительные пути в соответствии с рекомендациями ( angular.io/docs/ts/latest/cookbook/… )
BeetleJuice

возможно, вам повезет больше, если вы явно укажете путь самостоятельно, напримерmoduleId: '/app/components/home/'
пол


0

На веб-сайте Angular.io в разделе Advanced / Deployment рекомендуется, чтобы самый простой способ развертывания - «скопировать среду разработки на сервер».

  1. пройдите по разделу: Возможно простейшее развертывание. Окончательные файлы проекта показаны внутри раздела кода. Обратите внимание, что он уже настраивает код для загрузки файлов пакета npm из Интернета (вместо локальной папки npm_modules).

  2. убедитесь, что он запущен на вашем локальном компьютере (запуск npm). Затем в папке проекта скопируйте все, что находится в подпапке '/ src', в корзину S3, которую вы настроили. Вы можете использовать перетаскивание для копирования, во время этого процесса вы получаете возможность выбрать настройку разрешений для файлов, сделав их «доступными для чтения» для «всех».

  3. на вкладке «Свойства» сегмента найдите панель «Хостинг статического веб-сайта», установите флажок «Использовать этот сегмент для размещения веб-сайта» и укажите «index.html» как в индексном документе, так и в документе с ошибкой.

  4. нажмите на конечную точку статического веб-сайта, ваш проект будет запущен!

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.