Cómo agrupar una aplicación angular para producción

353

¿Cuál es el mejor método para agrupar Angular (versión 2, 4, 6, ...) para la producción en un servidor web en vivo?

Incluya la versión angular en las respuestas para que podamos realizar un mejor seguimiento cuando se mueva a versiones posteriores.

Pat M
fuente
Por ahora (rc1). Aquí hay algunas soluciones stackoverflow.com/questions/37324511/how-to-bundle-angular2-rc1-with-systemjs
Pat M
rc3 ahora ofrece un paquete de versiones de archivos que reduce el número de solicitudes de más de 300 a aproximadamente 40.
Pat M
2
Oye. También odio los WebPack y los pasos de construcción en general. Una especie de exageración por solo tratar de crear un sitio web simple. Así hice esto: github.com/schungx/angular2-bundle
Stephen Chung
Gracias Stephen Esta sería una solución simple para los vendedores. Esperando que esto pueda ser ofrecido y actualizado oficialmente. ¿Supongo que usas algo como Gulp para los archivos del proyecto?
Pat M

Respuestas:

362

2.x, 4.x, 5.x, 6.x, 7.x, 8.x, 9.x (TypeScript) con CLI angular

Configuración de una sola vez

  • npm install -g @angular/cli
  • ng new projectFolder crea una nueva aplicación

Paso de agrupación

  • ng build --prod(ejecutar en la línea de comando cuando el directorio es projectFolder)

    prodpaquete de banderas para producción (consulte la documentación angular para la lista de opciones incluidas con la bandera de producción).

  • Comprimir usando Brotli comprimir los recursos usando el siguiente comando

    for i in dist/*; do brotli $i; done

los paquetes se generan por defecto en projectFolder / dist (/ $ projectFolder para 6)

Salida

Tamaños con angular 9.0.0 con CLI 9.0.1y opción CSS sin enrutamiento angular

  • dist/main-[es-version].[hash].js Su aplicación incluida [tamaño ES5: 158 KB para la nueva aplicación CLI angular vacía, 40 KB comprimida].
  • dist/polyfill-[es-version].[hash].bundle.js las dependencias de polyfill (@angular, RxJS ...) agrupadas [tamaño ES5: 127 KB para la nueva aplicación CLI angular vacía, 37 KB comprimida].
  • dist/index.html punto de entrada de su solicitud.
  • dist/runtime-[es-version].[hash].bundle.js cargador de paquetes web
  • dist/style.[hash].bundle.css las definiciones de estilo
  • dist/assets recursos copiados de la configuración de activos de CLI angular

Despliegue

Puede obtener una vista previa de su aplicación utilizando el ng serve --prodcomando que inicia un servidor HTTP local de modo que se pueda acceder a la aplicación con archivos de producción utilizando http: // localhost: 4200 .

Para un uso de producción, debe implementar todos los archivos de la distcarpeta en el servidor HTTP que elija.

Nicolas Henneaux
fuente
Recibí el error al ejecutar npm install -g angular-cli @ webpack: npm ERR! Incluya el siguiente archivo con cualquier solicitud de soporte: .... \ npm-debug.log. ¿Sabes lo que está pasando?
Chong Wang el
2
@chrismarx produce solo un paquete que incluye todos los componentes con sus html y estilos.
Nicolas Henneaux
44
Tenía una aplicación y quería usar este método, así que lanzo ng init desde la carpeta del proyecto. He hecho el resto de los pasos, pero cuando implemento mis aplicaciones parece estar vacío. Lo único que aparece es una "aplicación funciona". mensaje, ¿hay algún lugar donde deba establecer dónde llevar los archivos de mi aplicación?
mautrok
2
ng-init se ha eliminado de cli angular. github.com/angular/angular-cli/issues/5176
Pat M
2
Finalmente marqué esto como la respuesta aceptada. Aunque otras soluciones pueden funcionar también e incluso proporcionar cierta flexibilidad adicional (publiqué una sobre el uso de Webpack sin CLI). El uso de CLI angular es definitivamente el que da menos dolores de cabeza. Terminé usando Angular CLI y adaptando mi proyecto para poder usar AoT más fácilmente.
Pat M
57

2.0.1 Final utilizando Gulp (TypeScript - Target: ES5)


Configuración de una sola vez

  • npm install (ejecutar en cmd cuando direcory es projectFolder)

Pasos de empaquetado

  • npm run bundle (ejecutar en cmd cuando direcory es projectFolder)

    los paquetes se generan para projectFolder / bundles /

Salida

  • bundles/dependencies.bundle.js[ tamaño: ~ 1 MB (lo más pequeño posible)]
    • contiene rxjs y dependencias angulares, no todo el framework
  • bundles/app.bundle.js[ tamaño: depende de su proyecto , el mío es ~ 0.5 MB ]
    • contiene tu proyecto

Estructura de archivo

  • projectFolder / app / (todos los componentes, directivas, plantillas, etc.)
  • projectFolder / gulpfile.js

var gulp = require('gulp'),
  tsc = require('gulp-typescript'),
  Builder = require('systemjs-builder'),
  inlineNg2Template = require('gulp-inline-ng2-template');

gulp.task('bundle', ['bundle-app', 'bundle-dependencies'], function(){});

gulp.task('inline-templates', function () {
  return gulp.src('app/**/*.ts')
    .pipe(inlineNg2Template({ useRelativePaths: true, indent: 0, removeLineBreaks: true}))
    .pipe(tsc({
      "target": "ES5",
      "module": "system",
      "moduleResolution": "node",
      "sourceMap": true,
      "emitDecoratorMetadata": true,
      "experimentalDecorators": true,
      "removeComments": true,
      "noImplicitAny": false
    }))
    .pipe(gulp.dest('dist/app'));
});

gulp.task('bundle-app', ['inline-templates'], function() {
  // optional constructor options
  // sets the baseURL and loads the configuration file
  var builder = new Builder('', 'dist-systemjs.config.js');

  return builder
    .bundle('dist/app/**/* - [@angular/**/*.js] - [rxjs/**/*.js]', 'bundles/app.bundle.js', { minify: true})
    .then(function() {
      console.log('Build complete');
    })
    .catch(function(err) {
      console.log('Build error');
      console.log(err);
    });
});

gulp.task('bundle-dependencies', ['inline-templates'], function() {
  // optional constructor options
  // sets the baseURL and loads the configuration file
  var builder = new Builder('', 'dist-systemjs.config.js');

  return builder
    .bundle('dist/app/**/*.js - [dist/app/**/*.js]', 'bundles/dependencies.bundle.js', { minify: true})
    .then(function() {
      console.log('Build complete');
    })
    .catch(function(err) {
      console.log('Build error');
      console.log(err);
    });
});
  • projectFolder / package.json (igual que la guía de inicio rápido , que se muestra devDependencies y npm-scripts necesarios para agrupar)

{
  "name": "angular2-quickstart",
  "version": "1.0.0",
  "scripts": {
    ***
     "gulp": "gulp",
     "rimraf": "rimraf",
     "bundle": "gulp bundle",
     "postbundle": "rimraf dist"
  },
  "license": "ISC",
  "dependencies": {
    ***
  },
  "devDependencies": {
    "rimraf": "^2.5.2",
    "gulp": "^3.9.1",
    "gulp-typescript": "2.13.6",
    "gulp-inline-ng2-template": "2.0.1",
    "systemjs-builder": "^0.15.16"
  }
}
  • projectFolder / systemjs.config.js (igual que la guía de inicio rápido , ya no está disponible allí)

(function(global) {

  // map tells the System loader where to look for things
  var map = {
    'app':                        'app',
    'rxjs':                       'node_modules/rxjs',
    'angular2-in-memory-web-api': 'node_modules/angular2-in-memory-web-api',
    '@angular':                   'node_modules/@angular'
  };

  // packages tells the System loader how to load when no filename and/or no extension
  var packages = {
    'app':                        { main: 'app/boot.js',  defaultExtension: 'js' },
    'rxjs':                       { defaultExtension: 'js' },
    'angular2-in-memory-web-api': { defaultExtension: 'js' }
  };

  var packageNames = [
    '@angular/common',
    '@angular/compiler',
    '@angular/core',
    '@angular/forms',
    '@angular/http',
    '@angular/platform-browser',
    '@angular/platform-browser-dynamic',
    '@angular/router',
    '@angular/router-deprecated',
    '@angular/testing',
    '@angular/upgrade',
  ];

  // add package entries for angular packages in the form '@angular/common': { main: 'index.js', defaultExtension: 'js' }
  packageNames.forEach(function(pkgName) {
    packages[pkgName] = { main: 'index.js', defaultExtension: 'js' };
  });

  var config = {
    map: map,
    packages: packages
  };

  // filterSystemConfig - index.asp's chance to modify config before we register it.
  if (global.filterSystemConfig) { global.filterSystemConfig(config); }

  System.config(config);

})(this);
  • projetcFolder / dist-systemjs.config.js (solo se muestra la diferencia con systemjs.config.json)

var map = {
    'app':                        'dist/app',
  };
  • projectFolder / index.html (producción): el orden de las etiquetas de script es crítico. Colocar la dist-systemjs.config.jsetiqueta después de las etiquetas del paquete aún permitiría que el programa se ejecute, pero el paquete de dependencia se ignorará y las dependencias se cargarán desde la node_modulescarpeta.

<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8"/>
  <meta name="viewport" content="width=device-width, initial-scale=1"/>
  <base href="/"/>
  <title>Angular</title>
  <link rel="stylesheet" type="text/css" href="style.css"/>
</head>
<body>

<my-app>
  loading...
</my-app>

<!-- Polyfill(s) for older browsers -->
<script src="node_modules/core-js/client/shim.min.js"></script>

<script src="node_modules/zone.js/dist/zone.min.js"></script>
<script src="node_modules/reflect-metadata/Reflect.js"></script>
<script src="node_modules/systemjs/dist/system.js"></script>

<script src="dist-systemjs.config.js"></script>
<!-- Project Bundles. Note that these have to be loaded AFTER the systemjs.config script -->
<script src="bundles/dependencies.bundle.js"></script>
<script src="bundles/app.bundle.js"></script>

<script>
    System.import('app/boot').catch(function (err) {
      console.error(err);
    });
</script>
</body>
</html>
  • projectFolder / app / boot.ts es donde está el bootstrap.

Lo mejor que pude hacer todavía :)

Ankit Singh
fuente
2
Hola, el script gulp está creando los paquetes, pero no estoy seguro de qué debería estar en el archivo boot.ts. ¿No están todos los archivos ahora en el paquete? ¿Ejecutamos el paquete?
chrismarx
2
Huh, creo que tengo que intentarlo de nuevo. Intenté cambiar a builder.buildStatic y obtuve errores de rxjs sobre no ser cargado como un módulo commonjs o amd. Voy a intentar su sugerencia otra vez
chrismarx
1
Tampoco tengo claro cómo se utilizan realmente los paquetes en esta configuración. Parece que me encuentro exactamente con los mismos problemas que @chrismarx aquí. Puedo crear los paquetes, pero parece que todavía todo se está cargando desde mi carpeta de aplicaciones copiada y copiada (ubicada en dist / app). Si miro en mi panel de red, puedo ver que mis archivos relacionados con la aplicación se están cargando desde allí (componentes, etc.), en lugar de todo lo relacionado con la aplicación que proviene de app.bundle.js. A_Singh, ¿puedes compartir tus boot.ts? Parece que me falta algo aquí y me encantaría algunas aclaraciones.
jbgarr
1
A_Singh, no veo cómo eso ayuda. Cuando inline-templatesse ejecuta, alinea las plantillas y luego crea una copia de todas las carpetas y archivos de la aplicación en dist/app. Luego, en dist-systemjs.config.jsasignar appa dist/appque es una carpeta que no existirá si se utiliza la distcarpeta como root. ¿No le gustaría ejecutar su aplicación desde la distcarpeta? Y si ese es el caso, no tendría una distcarpeta anidada en la distcarpeta raíz . Debo estar perdiendo algo más aquí. ¿No necesita decirle a systemjs que use sus archivos agrupados y no los archivos habituales que se encuentran en la dist/appcarpeta?
jbgarr
1
Me encuentro con un problema con su solución, el arranque es algo que no existe aquí, y cuando lo reemplazo por "aplicación", tengo un error "el módulo no está definido".
LoïcR
22

Angular 2 con Webpack (sin configuración de CLI)

1- El tutorial del equipo de Angular2.

El equipo de Angular2 publicó un tutorial para usar Webpack

Creé y coloqué los archivos del tutorial en un pequeño proyecto semilla de GitHub . Para que pueda probar rápidamente el flujo de trabajo.

Instrucciones :

  • npm install

  • npm start . Para desarrollo. Esto creará una carpeta virtual "dist" que se volverá a cargar en vivo en su dirección de host local.

  • npm ejecutar compilación . Para la producción. "Esto creará una versión física de la carpeta" dist "que se puede enviar a un servidor web. La carpeta dist es de 7.8MB pero solo se requieren 234KB para cargar la página en un navegador web.

2 - Un kit de inicio de Webkit

Este paquete de inicio Webpack ofrece algunas características de prueba más que el tutorial anterior y parece bastante popular.

Pat M
fuente
hola, ¿es posible actualizar el proyecto semilla con angular 2.1.0? El tutorial está usando angular 2.1.0 ahora. Lo seguí y no pude hacerlo funcionar. El error es http 404: no puedo encontrar app.component.html.
heq99
Actualicé a angular 2.1.0 sin problema. Se llama a app.component.html desde app.component.ts (templateUrl: './app.component.html'). Tienes ambos archivos en la misma carpeta de la aplicación?
Pat M
1
La sacudida de árboles, la minificación y el gzipping pueden reducir en gran medida el tamaño cuando va a la producción. Aquí hay una excelente lectura con ejemplo, blog.mgechev.com/2016/06/26/…
Hamzeen Hameem
16

Flujo de trabajo de producción de Angular 2 con SystemJs Builder y Gulp

Angular.io tiene un tutorial de inicio rápido. Copié este tutorial y lo extendí con algunas tareas simples para agrupar todo en la carpeta dist que se puede copiar al servidor y funcionar así. Intenté optimizar todo para que funcione bien en Jenkis CI, por lo que node_modules se puede almacenar en caché y no es necesario copiar.

Código fuente con aplicación de muestra en Github: https://github.com/Anjmao/angular2-production-workflow

Pasos para la produccion
  1. Archivos de texto limpios compilados archivos js y carpeta dist
  2. Compilar archivos mecanografiados dentro de la carpeta de la aplicación
  3. Use el paquete SystemJs para agrupar todo en la carpeta dist con hashes generados para la actualización de la memoria caché del navegador
  4. Use gulp-html-replace para reemplazar los scripts index.html con versiones incluidas y copie a la carpeta dist
  5. Copie todo dentro de la carpeta de activos a la carpeta dist

Nodo : aunque siempre puede crear su propio proceso de compilación, pero le recomiendo utilizar angular-cli, porque tiene todos los flujos de trabajo necesarios y ahora funciona perfectamente. Ya lo estamos usando en producción y no tenemos ningún problema con angular-cli.

Andzej Maciusovic
fuente
Esto es lo que estoy buscando. La aplicación de muestra en github es muy útil. Gracias
Shahriar Hasan Sayeed
14

Angular CLI 1.xx (Funciona con Angular 4.xx, 5.xx)

Esto apoya:

  • Angular 2.xy 4.x
  • Webpack 2.x más reciente
  • Compilador angular de AoT
  • Enrutamiento (normal y perezoso)
  • SCSS
  • Paquete de archivos personalizado (activos)
  • Herramientas de desarrollo adicionales (linter, unidad y configuraciones de prueba de extremo a extremo)

Configuración inicial

ng nuevo nombre-proyecto - enrutamiento

Puedes añadir --style=scss soporte para SASS .scss.

Puedes añadir --ng4 para usar Angular 4 en lugar de Angular 2.

Después de crear el proyecto, la CLI se ejecutará automáticamente npm installpor usted. Si desea utilizar Yarn en su lugar, o simplemente quiere ver el esqueleto del proyecto sin instalar, consulte aquí cómo hacerlo .

Pasos del paquete

Dentro de la carpeta del proyecto:

ng build -prod

En la versión actual, debe especificarlo --aotmanualmente, ya que puede usarse en modo de desarrollo (aunque eso no es práctico debido a la lentitud).

Esto también realiza la compilación de AoT para paquetes aún más pequeños (sin compilador angular, en cambio, genera la salida del compilador). Los paquetes son mucho más pequeños con AoT si usa Angular 4 ya que el código generado es más pequeño.
Puede probar su aplicación con AoT en modo de desarrollo (mapas de origen, sin minificación) y AoT ejecutandong build --aot .

Salida

El directorio de salida predeterminado es ./dist, aunque se puede cambiar en./angular-cli.json .

Archivos desplegables

El resultado del paso de compilación es el siguiente:

(Nota: se <content-hash>refiere al hash / huella digital del contenido del archivo que pretende ser una forma de reventar la caché, esto es posible ya que Webpack escribe las scriptetiquetas por sí mismo)

  • ./dist/assets
    Archivos copiados tal cual de ./src/assets/**
  • ./dist/index.html
    Desde ./src/index.html, después de agregarle scripts webpack, el
    archivo de plantilla de origen es configurable./angular-cli.json
  • ./dist/inline.js
    Cargador webpack pequeño / polyfill
  • ./dist/main.<content-hash>.bundle.js
    El archivo .js principal que contiene todos los scripts .js generados / importados
  • ./dist/styles.<content-hash>.bundle.js
    Cuando utiliza cargadores Webpack para CSS, que es la forma de CLI, aquí se cargan a través de JS

En versiones anteriores también creaba versiones comprimidas para verificar su tamaño, y .map archivos de mapas de origen, pero esto ya no sucede ya que la gente seguía pidiendo eliminarlos.

Otros archivos

En otras ocasiones, puede encontrar otros archivos / carpetas no deseados:

  • ./out-tsc/
    Desde ./src/tsconfig.json'soutDir
  • ./out-tsc-e2e/
    Desde ./e2e/tsconfig.json'soutDir
  • ./dist/ngfactory/
    Desde el compilador de AoT (no configurable sin bifurcar la CLI a partir de beta 16)
Meligy
fuente
¿Es posible separar la biblioteca angular y sus dependencias de mi aplicación?
Dominick Piganell
No usar la CLI, que está diseñada para que funcione el movimiento de árboles. Eso es eliminar todos los módulos Angular EcmaScript que no se utilizan en su aplicación. Hay un plan para deshabilitar esto en el modo de desarrollo para la velocidad (llaman a las bibliotecas cargadas como "DLL"), pero no hay planes para separarlas en el resultado final. Debería ser posible si está rodando su propio material Webpack sin la CLI.
Meligy
Cómo verificar mi aplicación usando la carpeta dist. ¿Cómo puedo alojar en mi servidor web?
Raj m
Simplemente cópialo en el servidor. Es un sitio web estático que se puede servir de todos modos. Sin embargo, si usa el enrutamiento, es posible que desee redirigir todas las llamadas al archivo HTML, para eso verifique el documento de implementación angular en la sección de configuración del servidor angular.io/docs/ts/latest/guide/…
Meligy
@Meligy, ¿qué pasa si elimino <content-hash>de los paquetes en prod. Puede causar problemas para obtener el último paquete?
k11k2
5

A día de hoy todavía encuentro el libro de cocina de compilación anticipada como la mejor receta para la producción en paquete. Puede encontrarlo aquí: https://angular.io/docs/ts/latest/cookbook/aot-compiler.html

Mi experiencia con Angular 2 hasta ahora es que AoT crea las compilaciones más pequeñas casi sin tiempo de carga. Y lo más importante, ya que la pregunta aquí es: solo necesita enviar algunos archivos a producción.

Esto parece deberse a que el compilador Angular no se enviará con las compilaciones de producción, ya que las plantillas se compilan "por adelantado". También es genial ver el marcado de su plantilla HTML transformado en instrucciones de JavaScript que serían muy difíciles de aplicar ingeniería inversa en el HTML original.

Hice un video simple donde demuestro el tamaño de descarga, la cantidad de archivos, etc. para una aplicación Angular 2 en desarrollo vs AoT build, que puede ver aquí:

https://youtu.be/ZoZDCgQwnmQ

Encontrará el código fuente utilizado en el video aquí:

https://github.com/fintechneo/angular2-templates

Peter Salomonsen
fuente
3
        **Production build with

         - Angular Rc5
         - Gulp
         - typescripts 
         - systemjs**

        1)con-cat all js files  and css files include on index.html using  "gulp-concat".
          - styles.css (all css concat in this files)
          - shims.js(all js concat in this files)

        2)copy all images and fonts as well as html files  with gulp task to "/dist".

        3)Bundling -minify angular libraries and app components mentioned in systemjs.config.js file.
         Using gulp  'systemjs-builder'

            SystemBuilder = require('systemjs-builder'),
            gulp.task('system-build', ['tsc'], function () {
                var builder = new SystemBuilder();
                return builder.loadConfig('systemjs.config.js')
                    .then(function () {
                        builder.buildStatic('assets', 'dist/app/app_libs_bundle.js')
                    })
                    .then(function () {
                        del('temp')
                    })
            });


    4)Minify bundles  using 'gulp-uglify'

jsMinify = require('gulp-uglify'),

    gulp.task('minify', function () {
        var options = {
            mangle: false
        };
        var js = gulp.src('dist/app/shims.js')
            .pipe(jsMinify())
            .pipe(gulp.dest('dist/app/'));
        var js1 = gulp.src('dist/app/app_libs_bundle.js')
            .pipe(jsMinify(options))
            .pipe(gulp.dest('dist/app/'));
        var css = gulp.src('dist/css/styles.min.css');
        return merge(js,js1, css);
    });

5) In index.html for production 

    <html>
    <head>
        <title>Hello</title>

        <meta name="viewport" content="width=device-width, initial-scale=1">
        <meta charset="utf-8" />

       <link rel="stylesheet" href="app/css/styles.min.css" />   
       <script type="text/javascript" src="app/shims.js"></script>  
       <base href="https://stackoverflow.com/">
    </head>
     <body>
    <my-app>Loading...</my-app>
     <script type="text/javascript" src="app/app_libs_bundle.js"></script> 
    </body>

    </html>

 6) Now just copy your dist folder to '/www' in wamp server node need to copy node_modules in www.
Tushar Tibude
fuente
2

Puede implementar su aplicación angular al githubusar angular-cli-ghpages

mira el enlace para encontrar cómo implementar usando este cli.

el sitio web desplegado se almacenará en alguna sucursal en github general

gh-pages

use puede clonar la rama git y usarla como sitio web estático en su servidor

Sunil Kumar
fuente
1

"Mejor" depende del escenario. Hay momentos en los que solo te importa el paquete individual más pequeño posible, pero en aplicaciones grandes es posible que tengas que considerar la carga diferida. En algún momento, resulta poco práctico servir toda la aplicación como un solo paquete.

En este último caso, Webpack es generalmente la mejor manera, ya que admite la división de código.

Para un solo paquete, consideraría Rollup o el compilador de Closure si te sientes valiente :-)

He creado muestras de todos los paquetes angulares que he usado aquí: http://www.syntaxsuccess.com/viewarticle/angular-production-builds

El código se puede encontrar aquí: https://github.com/thelgevold/angular-2-samples

Versión angular: 4.1.x

TGH
fuente
0

Intente debajo del comando CLI en el directorio actual del proyecto. Creará un paquete de carpetas dist. para que pueda cargar todos los archivos dentro de la carpeta dist para implementaciones.

ng build --prod --aot --base-href.

Nagnath Mungade
fuente
0

ng serve funciona para servir nuestra aplicación con fines de desarrollo. ¿Qué pasa con la producción? Si miramos nuestro archivo package.json, podemos ver que hay scripts que podemos usar:

"scripts": {
  "ng": "ng",
  "start": "ng serve",
  "build": "ng build --prod",
  "test": "ng test",
  "lint": "ng lint",
  "e2e": "ng e2e"
},

El script de compilación utiliza la compilación ng de la CLI angular con el indicador --prod. Probemos eso ahora. Podemos hacerlo de dos maneras:

# usando los scripts npm

npm run build

# usando el cli directamente

ng build --prod

Esta vez nos dan cuatro archivos en lugar de los cinco. El indicador --prod le dice a Angular que reduzca el tamaño de nuestra aplicación.

Yogesh Waghmare
fuente