【问题标题】:How to bundle an Angular app for production如何捆绑 Angular 应用程序进行生产
【发布时间】:2016-10-04 12:10:08
【问题描述】:

在实时网络服务器上捆绑 Angular(版本 2、4、6...)以进行生产的最佳方法是什么。

请在答案中包含 Angular 版本,以便我们在它移至更高版本时更好地跟踪。

【问题讨论】:

标签: angular webpack systemjs angular-cli


【解决方案1】:

2 to 11 (TypeScript) 与 Angular CLI

一次性设置

  • npm install -g @angular/cli
  • ng new projectFolder 创建一个新的应用程序

捆绑步骤

  • ng build --prod(目录为projectFolder时在命令行运行)

    标记prod 用于生产的捆绑包(请参阅Angular documentation 以获取生产标记中包含的选项列表)。

  • 使用Brotli compression使用以下命令压缩资源

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

对于 v6+,默认情况下会生成捆绑包为 projectFolder/dist(/$projectFolder**

输出

大小与 Angular 11.2.12 与 CLI 11.2.12and option CSS without Angular routing

  • dist/main-[es-version].[hash].js 捆绑您的应用程序 [ES5 大小:135 KB 用于新的 Angular CLI 应用程序空,38 KB 压缩]。
  • dist/polyfill-[es-version].[hash].bundle.js 捆绑了 polyfill 依赖项(@angular、RxJS...)[ES5 大小:36 KB 用于新的 Angular CLI 应用程序为空,12 KB 压缩]。
  • dist/index.html 应用程序的入口点。
  • dist/runtime-[es-version].[hash].bundle.js webpack 加载器
  • dist/style.[hash].bundle.css 样式定义
  • dist/assets 从 Angular CLI 资产配置中复制的资源

部署

您可以使用启动本地 HTTP 服务器的ng serve --prod 命令获取应用程序的预览,以便可以使用 http://localhost:4200 访问带有生产文件的应用程序。 这对于生产用途来说是不安全的。

对于生产用途,您必须部署所选 HTTP 服务器中 dist 文件夹中的所有文件。

【讨论】:

  • 运行 npm install -g angular-cli@webpack: npm ERR 时出现错误!请在任何支持请求中包含以下文件:....\npm-debug.log。你知道发生了什么吗?
  • @chrismarx 它只生成一个包,包括所有组件及其 html 和样式。
  • 我有一个应用程序,我想使用这种方法,所以我从项目文件夹启动 ng init。我已经完成了其余的步骤,但是当我部署我的应用程序时,它似乎是空的。唯一出现的是“应用程序有效!”消息,是否有一些地方我必须设置我的应用程序文件的位置?
  • ng-init 已从 Angular cli 中删除。 github.com/angular/angular-cli/issues/5176
  • 我终于将此标记为已接受的答案。尽管其他解决方案也可以工作,甚至提供一些额外的灵活性(我发布了一篇关于在没有 CLI 的情况下使用 Webpack)。使用 Angular CLI 无疑是一种减少头痛的方法。我最终使用了 Angular CLI 并调整了我的项目,以便我可以更轻松地使用 AoT。
【解决方案2】:

ng serve 用于为我们的应用程序提供开发服务。生产呢?如果我们查看我们的 package.json 文件,我们可以看到我们可以使用一些脚本:

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

构建脚本使用 Angular CLI 的 ng 构建和 --prod 标志。现在让我们试试吧。我们可以通过以下两种方式之一来实现:

# 使用 npm 脚本

npm run build

#直接使用cli

ng build --prod

这次我们得到了四个文件,而不是五个。 --prod 标志告诉 Angular 让我们的应用程序体积更小。

【讨论】:

    【解决方案3】:

    请在当前项目目录中尝试以下 CLI 命令。它将创建 dist 文件夹包。因此您可以上传 dist 文件夹中的所有文件以进行部署。

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

    【讨论】:

      【解决方案4】:

      只需在一分钟内使用 webpack 3 设置 angular 4,您的开发和生产环境包就可以准备就绪,没有任何问题 只需遵循以下 github 文档

      https://github.com/roshan3133/angular2-webpack-starter

      【讨论】:

        【解决方案5】:

        Angular CLI 1.x.x(适用于 Angular 4.x.x、5.x.x)

        这支持:

        • Angular 2.x 和 4.x
        • 最新的 Webpack 2.x
        • Angular AoT 编译器
        • 路由(普通和惰性)
        • SCSS
        • 自定义文件捆绑(资产)
        • 其他开发工具(linter、单元和端到端测试设置)

        初始设置

        ng 新项目名称 --routing

        您可以添加 --style=scss 以获得 SASS .scss 支持。

        您可以添加 --ng4 以使用 Angular 4 而不是 Angular 2。

        创建项目后,CLI 会自动为您运行npm install。如果你想改用 Yarn,或者只是想看项目骨架而不安装,check how to do it here

        捆绑步骤

        项目文件夹内:

        ng build -prod

        在当前版本你需要手动指定--aot,因为它可以在开发模式下使用(虽然由于速度慢所以不实用)。

        这也为更小的包执行 AoT 编译(没有 Angular 编译器,而是生成编译器输出)。如果您使用 Angular 4,由于生成的代码更小,使用 AoT 的包要小得多。
        您可以在开发模式下使用 AoT 测试您的应用程序(源图,没有缩小)和运行 ng build --aot 的 AoT。

        输出

        默认输出目录是./dist,虽然可以在./angular-cli.json中更改。

        可部署文件

        构建步骤的结果如下:

        (注意:<content-hash> 指的是文件内容的哈希/指纹,这是一种缓存破坏方式,这是可能的,因为 Webpack 自己编写 script 标签)

        • ./dist/assets
          ./src/assets/**原样复制的文件
        • ./dist/index.html
          来自./src/index.html,添加 webpack 脚本后
          源模板文件可在./angular-cli.json 中配置
        • ./dist/inline.js
          小型 webpack 加载器/polyfill
        • ./dist/main.<content-hash>.bundle.js
          包含所有生成/导入的 .js 脚本的主 .js 文件
        • ./dist/styles.<content-hash>.bundle.js
          当你为 CSS 使用 Webpack 加载器时,这是 CLI 方式,它们在这里通过 JS 加载

        在旧版本中,它还创建了用于检查其大小的 gzip 版本和 .map 源映射文件,但由于人们一直要求删除这些文件,因此不再发生这种情况。

        其他文件

        在某些其他情况下,您可能会发现其他不需要的文件/文件夹:

        • ./out-tsc/
          来自./src/tsconfig.jsonoutDir
        • ./out-tsc-e2e/
          来自./e2e/tsconfig.jsonoutDir
        • ./dist/ngfactory/
          来自 AoT 编译器(从 beta 16 开始,如果不分叉 CLI,则无法配置)

        【讨论】:

        • 是否可以将 Angular 库及其依赖项与我的应用程序分开?
        • 不使用 CLI,这是为了让 tree-shaking 起作用。那就是删除所有未在您的应用程序中使用的 Angular EcmaScript 模块。有计划在开发模式下禁用此功能以提高速度(他们将加载的库称为“DLL”),但没有计划在最终结果中分离。如果您在没有 CLI 的情况下滚动自己的 Webpack 东西,这应该是可以实现的。
        • 如何使用 dist 文件夹检查我的应用程序。如何在我的网络服务器中托管?
        • 你只需将它复制到服务器。这是一个简单的静态网站,无论如何都可以提供服务。如果您使用路由,您可能希望将所有调用重定向到 HTML 文件,为此检查服务器配置部分 angular.io/docs/ts/latest/guide/… 上的 Angular 部署文档
        • @Meligy 如果我从产品包中删除 <content-hash> 会怎样。这可能会导致获取最新捆绑包时出现问题?
        【解决方案6】:

        您可以在github 上使用 angular-cli-ghpages

        查看链接以了解如何使用此 cli 进行部署。

        部署的网站通常会存储在github的某个分支中

        gh-pages

        use 可以克隆 git 分支并在你的服务器中像静态网站一样使用它

        【讨论】:

          【解决方案7】:

          2.0.1 Final 使用 Gulp(TypeScript - 目标:ES5)


          一次性设置

          • npm install(目录为projectFolder时在cmd中运行)

          捆绑步骤

          • npm run bundle(目录为projectFolder时在cmd中运行)

            bundle 被生成到 projectFolder / bundles /

          输出

          • bundles/dependencies.bundle.js [ 大小:~ 1 MB(尽可能小)]
            • 包含 rxjs 和 angular 依赖项,而不是整个框架
          • bundles/app.bundle.js [ 大小:取决于您的项目,我的是 ~ 0.5 MB ]
            • 包含您的项目

          文件结构

          • projectFolder / app /(所有组件、指令、模板等)
          • 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(与Quickstart guide 相同,仅显示捆绑所需的 devDependencies 和 npm-scripts)

          {
            "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(与Quickstart guide相同,不再可用)

          (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(只是展示了与 systemjs.config.json 的区别)

          var map = {
              'app':                        'dist/app',
            };
          
          • projectFolder / index.html(生产)- 脚本标签的顺序很关键。将 dist-systemjs.config.js 标记放在 bundle 标记之后仍然允许程序运行,但依赖包将被忽略,并且将从 node_modules 文件夹加载依赖项。

          <!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 是引导程序所在的位置。

          我能做到的最好的:)

          【讨论】:

          • 您好,gulp 脚本正在创建捆绑包,但我不确定 boot.ts 文件中应该包含什么内容?现在不是所有文件都在捆绑包中吗?我们是否执行捆绑包?
          • 呃,我想我需要再试一次。我尝试切换到 builder.buildStatic 并从 rxjs 收到关于未作为 commonjs 或 amd 模块加载的错误。我会再试一次你的建议
          • 我也不清楚这些捆绑包是如何在这个设置中实际使用的?我似乎在这里遇到了与@chrismarx 完全相同的问题。我可以创建捆绑包,但似乎所有内容仍在从我的转译和复制的应用程序文件夹(位于 dist/app)中加载。如果我查看我的网络面板,我可以看到我的应用相关文件实际上是从那里加载的(组件等),而不是来自 app.bundle.js 的所有应用相关文件。 A_Singh,你能分享你的 boot.ts 吗?看来我在这里遗漏了一些东西,希望得到一些澄清。
          • A_Singh,我看不出这有什么帮助。当inline-templates 运行时,它内联模板,然后在dist/app 创建所有应用程序文件夹和文件的副本。然后在dist-systemjs.config.js 中,您将app 映射到dist/app,如果您将dist 文件夹用作根目录,则该文件夹将不存在。您不想从dist 文件夹运行您的应用程序吗?如果是这种情况,您将不会在根 dist 文件夹中嵌套一个 dist 文件夹。我一定在这里遗漏了其他东西。您不需要告诉 systemjs 使用您的捆绑文件,而不是在 dist/app 文件夹中找到的常用文件吗?
          • 我在您的解决方案中遇到了问题,boot 是这里不存在的东西,当我用“app”替换它时,出现错误“模块未定义”。跨度>
          【解决方案8】:

          “最佳”取决于场景。有时您只关心最小的单个捆绑包,但在大型应用程序中您可能不得不考虑延迟加载。在某些时候,将整个应用程序作为单个包提供服务变得不切实际。

          在后一种情况下,Webpack 通常是最好的方式,因为它支持代码拆分。

          对于单个包,我会考虑 Rollup,或者如果你觉得勇敢的话,我会考虑 Closure 编译器 :-)

          我已经创建了我在这里使用过的所有 Angular 捆绑器的示例:http://www.syntaxsuccess.com/viewarticle/angular-production-builds

          代码可以在这里找到:https://github.com/thelgevold/angular-2-samples

          Angular 版本:4.1.x

          【讨论】:

            【解决方案9】:

            带有 Webpack 的 Angular 2(无 CLI 设置)

            1- Angular2 团队的教程

            Angular2 团队发布了tutorial 用于使用 Webpack

            我创建了教程中的文件并将其放置在一个小的GitHub seed project 中。因此您可以快速尝试工作流程。

            说明

            • npm install

            • npm 启动。为了发展。这将创建一个虚拟的“dist”文件夹,该文件夹将在您的本地主机地址实时重新加载。

            • npm 运行构建。用于生产。 “这将创建一个物理的“dist”文件夹版本,而不是可以发送到网络服务器。dist 文件夹为 7.8MB,但在网络浏览器中加载页面实际上只需要 234KB。

            2 - Webkit 入门工具包

            这个Webpack Starter Kit 提供了比上述教程更多的测试功能,看起来很受欢迎。

            【讨论】:

            • 嗨,是否可以使用 Angular 2.1.0 更新种子项目?本教程现在使用 angular 2.1.0。我跟着它,无法让它工作。错误是 http 404 - 找不到 app.component.html。
            • 我更新到 Angular 2.1.0 没有问题。 app.component.html 从 app.component.ts (templateUrl: './app.component.html') 调用。你有两个文件在同一个应用程序文件夹中?
            • Tree-shaking, Minification & Gzipping 可以在你进行生产时大大减小尺寸。这是一个很好的例子,blog.mgechev.com/2016/06/26/…
            【解决方案10】:
                    **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="/">
                </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.
            

            【讨论】:

              【解决方案11】:

              直到今天,我仍然认为 Ahead-of-Time Compilation 食谱是生产捆绑的最佳方法。你可以在这里找到它:https://angular.io/docs/ts/latest/cookbook/aot-compiler.html

              到目前为止,我对 Angular 2 的体验是,AoT 可以创建最小的构建,几乎没有加载时间。最重要的是,这里的问题是关于 - 您只需将几个文件发送到生产环境。

              这似乎是因为 Angular 编译器不会随生产版本一起提供,因为模板是“提前”编译的。看到您的 HTML 模板标记转换为很难逆向工程为原始 HTML 的 javascript 指令也非常酷。

              我制作了一个简单的视频,我在其中演示了 Angular 2 应用在 dev 与 AoT 构建中的下载大小、文件数量等 - 你可以在这里看到:

              https://youtu.be/ZoZDCgQwnmQ

              您可以在此处找到视频中使用的源代码:

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

              【讨论】:

                【解决方案12】:

                使用 SystemJs builder 和 gulp 的 Angular 2 生产工作流程

                Angular.io 有快速入门教程。我复制了本教程并扩展了一些简单的 gulp 任务,将所有内容捆绑到 dist 文件夹,可以复制到服务器并像这样工作。我尝试优化一切以在 Jenkis CI 上正常运行,因此 node_modules 可以被缓存而无需复制。

                Github 上的示例应用源代码:https://github.com/Anjmao/angular2-production-workflow

                生产步骤
                1. 清理 typescripts 编译的 js 文件和 dist 文件夹
                2. 在 app 文件夹中编译 typescript 文件
                3. 使用 SystemJs 捆绑器将所有内容捆绑到 dist 文件夹,并生成用于刷新浏览器缓存的哈希
                4. 使用 gulp-html-replace 将 index.html 脚本替换为捆绑版本并复制到 dist 文件夹
                5. 将 assets 文件夹中的所有内容复制到 dist 文件夹

                Node:虽然您始终可以创建自己的构建过程,但我强烈建议使用 angular-cli,因为它具有所有需要的工作流程并且现在可以完美运行。我们已经在生产中使用它,并且 angular-cli 完全没有任何问题。

                【讨论】:

                • 这就是我要找的。 github 上的示例应用程序非常有用。谢谢
                猜你喜欢
                • 1970-01-01
                • 1970-01-01
                • 2021-01-11
                • 2018-11-23
                • 2019-05-01
                • 1970-01-01
                • 2018-05-05
                • 2017-11-16
                • 1970-01-01
                相关资源
                最近更新 更多