¿Cómo implemento una aplicación Angular 2 + Typescript + systemjs?

103

Hay un tutorial de inicio rápido en angular.io que usa mecanografiado y systemjs. Ahora que tengo esa miniapp ejecutándose, ¿cómo haría para crear algo desplegable? No pude encontrar ninguna información al respecto.

¿Necesito herramientas adicionales, alguna configuración adicional en System.config?

(Sé que podría usar webpack y crear un solo bundle.js, pero me gustaría usar systemjs como se usa en el tutorial)

¿Alguien podría compartir su proceso de compilación con esta configuración (Angular 2, TypeScript, systemjs)

Brian G. Bell
fuente
Aquí está mi receta para crear una aplicación ng2 para su implementación usando JSPM: stackoverflow.com/a/34616199/3532945
brando
2
respuesta simple ng build -prod stackoverflow.com/a/38421680/5079380
Amr ElAdawy

Respuestas:

66

La clave para entender en este nivel es que usando la siguiente configuración, no puede concatizar archivos JS compilados directamente.

En la configuración del compilador de 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"
  ]
}

En el HTML

System.config({
  packages: {
    app: {
      defaultExtension: 'js',
      format: 'register'
    }
  }
});

De hecho, estos archivos JS contendrán módulos anónimos. Un módulo anónimo es un archivo JS que usa System.registerpero sin el nombre del módulo como primer parámetro. Esto es lo que genera el compilador mecanografiado de forma predeterminada cuando systemjs está configurado como administrador de módulos.

Entonces, para tener todos sus módulos en un solo archivo JS, debe aprovechar la outFilepropiedad dentro de la configuración del compilador de TypeScript.

Puede usar el siguiente trago interno para hacer eso:

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

Esto podría combinarse con algún otro procesamiento:

  • para uglify los archivos compilados de TypeScript
  • para crear un app.jsarchivo
  • para crear un vendor.jsarchivo para bibliotecas de terceros
  • para crear un boot.jsarchivo para importar el módulo que arranca la aplicación. Este archivo debe incluirse al final de la página (cuando toda la página está cargada).
  • para actualizar el index.htmltener en cuenta estos dos archivos

Las siguientes dependencias se utilizan en las tareas de gulp:

  • gulp-concat
  • gulp-html-replace
  • gulp-mecanografiado
  • tragar-uglificar

La siguiente es una muestra para que se pueda adaptar.

  • Crear app.min.jsarchivo

    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'));
    });
  • Crear vendors.min.jsarchivo

    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'));
    });
  • Crear boot.min.jsarchivo

    gulp.task('boot-bundle', function() {
      gulp.src('config.prod.js')
        .pipe(concat('boot.min.js'))
        .pipe(uglify())
        .pipe(gulp.dest('./dist'));
     });

    El config.prod.jssimplemente contiene lo siguiente:

     System.import('boot')
        .then(null, console.error.bind(console));
  • Actualizar el index.htmlarchivo

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

    El index.htmlaspecto es el siguiente:

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

Tenga en cuenta que System.import('boot');debe hacerse al final del cuerpo para esperar a que todos los componentes de su aplicación se registren desde el app.min.jsarchivo.

No describo aquí la forma de manejar la minificación de CSS y HTML.

Thierry Templier
fuente
1
¿Puedes crear un repositorio de github con un ejemplo, por favor?
jdelobel
Seguí tus instrucciones y todo se ve bien con respecto a tragar. Sin embargo, cuando ejecuto la aplicación en el navegador obtengo este error de registro de la consola: "system.src.js: 1625 Uncaught TypeError: múltiples llamadas anónimas System.register en el mismo archivo de módulo". ¿Alguna idea de lo que esto significa y cómo solucionarlo?
AngularM
@AngularM: ¿tienes el parámetro outFile? Esta es la clave de tu error ;-)
Thierry Templier
Lo tengo en mi archivo gulp y tsconfig
AngularM
¿Podrías echar un vistazo al proyecto de github que envié? Vea mi comentario arriba. ¿Ves algunas diferencias con tu código?
Thierry Templier
28

Puede usar el comando de compilación angular2-cli

ng build -prod

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

Las compilaciones creadas con la marca -prod mediante ng build -prodo agrupanng serve -prod todas las dependencias en un solo archivo y utilizan técnicas de agitación de árboles .

Actualizar

Esta respuesta se envió cuando angular2 estaba en rc4

Lo intenté nuevamente en angular-cli beta21 y angular2 ^ 2.1.0 y está funcionando como se esperaba

Esta respuesta requiere inicializar la aplicación con angular-cli que puede usar

ng new myApp

O en uno existente

ng init

Actualización 08/06/2018

Para angular 6 la sintaxis es diferente.

ng build --prod --build-optimizer

Consultar la documentación

Amr ElAdawy
fuente
8
Esto requiere que su aplicación esté estructurada en la estructura obstinada de angular-cli.
Michael Pell
2
@Amr ElAdawy FYI angular-cli se mudó a webpack. Esta pregunta está relacionada con SystemJS. ng build no funciona para mí.
Shahriar Hasan Sayeed
@ShahriarHasanSayeed ¿Te refieres a la vez que envié la respuesta o la vez que la intentaste?
Amr ElAdawy
@AmrElAdawy, ¿puede agregar las versiones para los módulos donde esto realmente funciona? Angular2 ha cambiado bastante desde julio.
ppovoski
2
Es trivial convertir el tutorial Tour of Heroes a la versión cli. Simplemente genere un nuevo proyecto usando cli y luego copie los archivos del tutorial.
Rosdi Kasim
12

Puede construir un proyecto Angular 2 (2.0.0-rc.1) en Typescript usando SystemJS con Gulp y SystemJS-Builder .

A continuación se muestra una versión simplificada de cómo compilar, agrupar y minificar Tour of Heroes con 2.0.0-rc.1 ( fuente completa , ejemplo en vivo ).

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

Acerado
fuente
2
¿Podría especificar cómo ejecutar SystemJs y Gulp?
Jan D Frozen
@JanDrozen En la misma ubicación que el gulpfile, puede ejecutar gulp <taskname>donde "TaskName" es el nombre de la tarea que llama al constructor SystemJS, en mi ejemplo anterior es bundle:app. En esa tarea de Gulp, puede usar el módulo npm 'systemjs-builder' para especificar la configuración y el archivo de salida de su sistema.
Steely
@ acerado: ¡Gracias! Funciona de maravilla. Espere al objetivo predeterminado: falta el método uglify () (o me falta algo). ¿Puede explicarme esta última parte poco clara?
Jan D Frozen
@Steely, ¿podría guiarnos sobre cómo hacerlo con la versión más nueva de angular2?
micronyks
@ Steely. ¿Puede proporcionar un enlace final (en github) de los últimos archivos de compilación angular2 que se requieren para ejecutar la aplicación de inicio rápido angular2?
micronyks
1

Aquí está mi modelo de MEA2N para Angular 2: https://github.com/simonxca/mean2-boilerplate

Es una simple repetición que se usa tscpara armar las cosas. (En realidad usa grunt-ts , que en esencia es solo eltsc comando). No es necesario Wekpack, etc.

Ya sea que uses gruñido o no, la idea es:

  • escriba su aplicación en una carpeta llamada ts/(ejemplo:public/ts/ )
  • use tscpara reflejar la estructura de directorios de su ts/carpeta en una js/carpeta y solo haga referencia a los archivos en la js/carpeta en su index.html.

Para que grunt-ts funcione (debería haber un comando equivalente para tsc simple, Gulp, etc.), tiene una propiedad en su tsconfig.jsonllamado "outDir": "../js"y la referencia en su gruntfile.jscon:

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

A continuación grunt ts, ejecute , que incluirá su aplicación public/ts/y la reflejará public/js/.

Ahí. Muy fácil de entender. No es el mejor enfoque, pero es bueno para empezar.

Simonxca
fuente
1

La forma más fácil que he encontrado de agrupar angular rc1 para systemJs es usar gulpy 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);
        });
});

Como se señaló en los comentarios, systemJs actualmente tiene problemas al empaquetar componentes usando moduleId: module.id

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

La recomendación actual (angular 2 rc1) parece ser utilizar rutas explícitas, es decir moduleId: '/app/path/'

Pablo
fuente
Esto parece prometedor, pero falla cuando utilizo rutas relativas a plantillas externas en el @Componentdecorador. bundle.jsintenta resolver las rutas como absolutas aunque sean relativas, lo que provoca errores 404 (consulte stackoverflow.com/questions/37497635/… ). ¿Cómo has lidiado con eso?
BeetleJuice
¿Estás estableciendo moduleIdel camino relativo?
paul
No estoy seguro si entiendo. Tengo moduleId: module.iden@Component
BeetleJuice
Eso tiene los mismos inconvenientes que poner abajo el camino completo templateUrly frustra el propósito de tener moduleIden primer lugar. Estoy tratando de usar rutas relativas como se recomienda ( angular.io/docs/ts/latest/cookbook/… )
BeetleJuice
puede tener más suerte si establece explícitamente el camino usted mismo, por ejemplomoduleId: '/app/components/home/'
paul
0

En el sitio web Angular.io, en la sección Avanzado / Implementación, se recomienda que la forma más sencilla de implementar sea 'copiar el entorno de desarrollo al servidor'.

  1. consulte la sección de: Implementación más sencilla posible. Los archivos finales del proyecto se muestran dentro de la sección de código. Tenga en cuenta que ya configura el código para cargar archivos de paquetes npm desde la web (en lugar de desde la carpeta local npm_modules).

  2. asegúrese de que se esté ejecutando en su computadora local (npm start). Luego, en la carpeta del proyecto, copie todo lo que está en la subcarpeta '/ src' en el depósito de S3 que ha configurado. Puede usar arrastrar y soltar para copiar, durante ese proceso, tiene la opción de seleccionar la configuración de permisos para los archivos, asegúrese de hacerlos "legibles" para "todos".

  3. en la pestaña 'Propiedades' del depósito, busque el panel 'Alojamiento de sitios web estáticos', marque la opción 'Usar este depósito para alojar el sitio web' y especifique 'index.html' tanto en el documento de índice como en el documento de error.

  4. haga clic en el sitio web estático Endpoint, ¡su proyecto estará funcionando!

Joseph Wu
fuente