Come posso distribuire effettivamente un'app Angular 2 + Typescript + systemjs?


103

C'è un tutorial rapido su angular.io che usa dattiloscritto e systemjs. Ora che ho quel miniapp in esecuzione, come dovrei creare qualcosa di distribuibile? Non sono riuscito a trovare alcuna informazione al riguardo.

Ho bisogno di strumenti aggiuntivi, impostazioni aggiuntive in System.config?

(So ​​che potrei usare webpack e creare un singolo bundle.js, ma mi piacerebbe usare systemjs così come è usato nel tutorial)

Qualcuno potrebbe condividere il proprio processo di compilazione con questa configurazione (Angular 2, TypeScript, systemjs)


Ecco la mia ricetta per la costruzione di un app NG2 per la distribuzione utilizzando JSPM: stackoverflow.com/a/34616199/3532945
Brando

2
risposta semplice ng accumulo -prod stackoverflow.com/a/38421680/5079380
Amr ElAdawy

Risposte:


66

La cosa fondamentale da capire a questo livello è che utilizzando la seguente configurazione, non è possibile concatenare direttamente i file JS compilati.

Nella configurazione del compilatore 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"
  ]
}

Nell'HTML

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

In effetti, questi file JS conterranno moduli anonimi. Un modulo anonimo è un file JS che utilizza System.registerma senza il nome del modulo come primo parametro. Questo è ciò che il compilatore di dattiloscritti genera di default quando systemjs è configurato come gestore di moduli.

Quindi, per avere tutti i moduli in un singolo file JS, è necessario sfruttare la outFileproprietà all'interno della configurazione del compilatore TypeScript.

Puoi usare quanto segue all'interno di gulp per farlo:

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

Questo potrebbe essere combinato con qualche altra elaborazione:

  • per peggiorare le cose i file TypeScript compilati
  • per creare un app.jsfile
  • per creare un vendor.jsfile per librerie di terze parti
  • per creare un boot.jsfile per importare il modulo che avvia l'applicazione. Questo file deve essere incluso alla fine della pagina (quando tutta la pagina è caricata).
  • per aggiornare il index.htmlper tenere conto di questi due file

Le seguenti dipendenze vengono utilizzate nelle attività gulp:

  • gulp-concat
  • gulp-html-replace
  • gulp-dattiloscritto
  • gulp-uglify

Quello che segue è un esempio in modo che possa essere adattato.

  • Crea app.min.jsfile

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

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

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

    Il config.prod.jscontiene semplicemente quanto segue:

     System.import('boot')
        .then(null, console.error.bind(console));
    
  • Aggiorna il index.htmlfile

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

    Le index.htmlè simile al seguente:

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

Si noti che System.import('boot');deve essere eseguito alla fine del corpo per attendere che tutti i componenti dell'app vengano registrati dal app.min.jsfile.

Non descrivo qui il modo di gestire CSS e HTML minification.


1
puoi creare un repository GitHub con un esempio per favore?
jdelobel

Ho seguito le tue istruzioni e tutto sembra a posto per quanto riguarda il gulp. Tuttavia, quando eseguo l'app nel browser ottengo questo errore di log della console: "system.src.js: 1625 Uncaught TypeError: più chiamate anonime System.register nello stesso file del modulo." Qualche idea su cosa significhi e su come risolverlo?
AngularM

@AngularM: hai il parametro outFile? Questa è la chiave del tuo errore ;-)
Thierry Templier

Ce l'ho nel mio file gulp e tsconfig
AngularM

Potresti dare un'occhiata al progetto GitHub che ho inviato? Vedi il mio commento sopra. Vedi delle differenze con il tuo codice?
Thierry Templier

28

È possibile utilizzare il comando build angular2-cli

ng build -prod

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

Le build create con il flag -prod tramite ng build -prodo ng serve -prodraggruppano tutte le dipendenze in un unico file e fanno uso di tecniche di scuotimento degli alberi .

Aggiornare

Questa risposta è stata inviata quando angular2 era in rc4

L'avevo provato di nuovo su angular-cli beta21 e angular2 ^ 2.1.0 e funziona come previsto

Questa risposta richiede l'inizializzazione dell'app con angular-cli che puoi utilizzare

ng new myApp

O su uno esistente

ng init

Aggiornamento 08/06/2018

Per l'angolo 6 la sintassi è diversa.

ng build --prod --build-optimizer

Controlla la documentazione


8
Ciò richiede che la tua app sia strutturata nella struttura supponente di angular-cli.
Michael Pell,

2
@Amr ElAdawy FYI angular-cli è passato a webpack. Questa domanda è correlata a SystemJS. ng build non funziona per me.
Shahriar Hasan Sayeed

@ShahriarHasanSayeed Ti riferisci al momento in cui ho inviato la risposta o all'ora in cui l'hai provata?
Amr ElAdawy

@AmrElAdawy, puoi aggiungere le versioni per i moduli in cui funziona effettivamente. Angular2 è cambiato parecchio da luglio.
ppovoski

2
È banale convertire il tutorial Tour of Heroes nella versione cli. Basta generare un nuovo progetto utilizzando cli e quindi copiare i file del tutorial.
Rosdi Kasim

12

Puoi costruire un progetto Angular 2 (2.0.0-rc.1) in Typescript usando SystemJS con Gulp e SystemJS-Builder .

Di seguito è riportata una versione semplificata di come creare, raggruppare e minimizzare Tour of Heroes con 2.0.0-rc.1 ( fonte completa , esempio dal 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
});


2
Potresti specificare come eseguire SystemJs e Gulp?
Jan Drozen

@JanDrozen Nella stessa posizione del tuo gulpfile, puoi eseguire gulp <taskname>dove "taskname" è il nome dell'attività che chiama il builder SystemJS, nel mio esempio sopra lo è bundle:app. In quell'attività Gulp puoi usare il modulo npm 'systemjs-builder' per specificare la configurazione del sistema e il file di output.
Steely

@ steely: Grazie! Funziona come un fascino. Aspettatevi il target predefinito: manca il metodo uglify () (o mi manca qualcosa). Puoi spiegarmi per favore quest'ultima parte poco chiara?
Jan Drozen,

@Steely potresti per favore guida come farlo con la versione più recente di angular2?
micronyks

@ Steely. Puoi fornire un collegamento finale (su GitHub) degli ultimi file di build angular2 necessari per eseguire l'app di avvio rapido angular2?
micronyks

1

Ecco il mio boilerplate MEA2N per Angular 2: https://github.com/simonxca/mean2-boilerplate

È un semplice boilerplate che usa tscper mettere insieme le cose. (In realtà usa grunt-ts , che in fondo è solo il tsccomando.) Nessun Wekpack, ecc. Necessario.

Che tu usi o meno grugnito, l'idea è:

  • scrivi la tua app in una cartella chiamata ts/(esempio public/ts/:)
  • utilizzare tscper rispecchiare la struttura della directory della ts/cartella in una js/cartella e fare semplicemente riferimento ai file nella js/cartella nel file index.html.

Per far funzionare grunt-ts (dovrebbe esserci un comando equivalente per plain tsc, Gulp, ecc.), Hai una proprietà nel tuo tsconfig.jsonchiamato "outDir": "../js"e fai riferimento ad esso nel tuo gruntfile.jscon:

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

Quindi esegui grunt ts, che porterà la tua app dentro public/ts/e la speccherà public/js/.

Là. Super facile da capire. Non è l'approccio migliore, ma buono per iniziare.


1

Il modo più semplice che ho trovato per raggruppare angular rc1 per systemJs è usare gulpe 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);
        });
});

Come sottolineato nei commenti, systemJs attualmente ha problemi durante il raggruppamento di componenti utilizzando moduleId: module.id

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

La raccomandazione corrente (angolare 2 rc1) sembra essere quella di utilizzare percorsi espliciti es moduleId: '/app/path/'


Questo sembra promettente ma non riesce quando utilizzo percorsi relativi a modelli esterni nel @Componentdecoratore. bundle.jstenta di risolvere i percorsi come assoluti anche se sono relativi, causando errori 404 (vedere stackoverflow.com/questions/37497635/… ). Come l'hai affrontato?
BeetleJuice

stai impostando moduleIdil percorso relativo?
paul

Non sono sicuro di aver capito. Ho moduleId: module.idin@Component
BeetleJuice

Ciò ha gli stessi inconvenienti di mettere giù il percorso completo templateUrle sconfigge lo scopo di avere moduleIdin primo luogo. Sto cercando di utilizzare percorsi relativi come consigliato ( angular.io/docs/ts/latest/cookbook/… )
BeetleJuice

potresti avere più fortuna impostando esplicitamente il percorso da solo, ad esempiomoduleId: '/app/components/home/'
paul


0

Nel sito Web Angular.io, nella sezione Avanzate / Distribuzione, si consiglia che il modo più semplice per eseguire la distribuzione sia "copiare l'ambiente di sviluppo sul server".

  1. passare attraverso la sezione sotto: Distribuzione più semplice possibile. I file di progetto finali sono mostrati all'interno della sezione del codice. Si noti che già imposta il codice per caricare i file del pacchetto npm dal web (invece che dalla cartella npm_modules locale).

  2. assicurati che sia in esecuzione sul tuo computer locale (avvio npm). Quindi, nella cartella del progetto, copia tutto nella sottocartella "/ src" nel bucket S3 che hai configurato. Puoi usare il drag-and-drop per copiare, durante questo processo, hai la possibilità di selezionare l'impostazione di autorizzazione per i file, assicurati di renderli "leggibili" a "tutti".

  3. nella scheda "Proprietà" del bucket, cerca il pannello "Hosting di siti Web statici", seleziona l'opzione "Utilizza questo bucket per ospitare il sito Web" e specifica "index.html" sia nel documento Indice che nel documento di errore.

  4. fai clic sul sito Web statico Endpoint, il tuo progetto funziona bene!

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.