填写这份《一分钟调查》,帮我们(开发组)做得更好!去填写Home

部署

Deployment

当你准备把 Angular 应用部署到远程服务器上时,有很多可选的部署方式。

When you are ready to deploy your Angular application to a remote server, you have various options for deployment.

最简单的部署选项

Simple deployment options

在完整部署应用之前,你可以先临时用一种技术来测试流程、构建配置和部署行为。

Before fully deploying your application, you can test the process, build configuration, and deployed behavior by using one of these interim techniques.

从磁盘构建和提供服务

Building and serving from disk

在开发过程中,你通常会使用 ng serve 命令来借助 webpack-dev-server 在本地内存中构建、监控和提供服务。但是,当你打算部署它时,就必须使用 ng build 命令来构建应用并在其它地方部署这些构建成果。

During development, you typically use the ng serve command to build, watch, and serve the application from local memory, using webpack-dev-server. When you are ready to deploy, however, you must use the ng build command to build the application and deploy the build artifacts elsewhere.

ng buildng serve 在构建项目之前都会清除输出文件夹,但只有 ng build 命令会把生成的构建成果写入输出输出文件夹中。

Both ng build and ng serve clear the output folder before they build the project, but only the ng build command writes the generated build artifacts to the output folder.

默认情况下,输出目录是 dist/project-name/。要输出到其它文件夹,就要修改 angular.json 中的 outputPath

The output folder is dist/project-name/ by default. To output to a different folder, change the outputPath in angular.json.

当开发临近收尾时,让本地 Web 服务器使用输出文件夹中的内容提供服务可以让你更好地了解当应用部署到远程服务器时的行为。你需要用两个终端才能体验到实时刷新的特性。

As you near the end of the development process, serving the contents of your output folder from a local web server can give you a better idea of how your application will behave when it is deployed to a remote server. You will need two terminals to get the live-reload experience.

  • 在第一个终端上,在监控(watch)模式下执行 ng build 命令把该应用编译进 dist 文件夹。

    On the first terminal, run the ng build command in watch mode to compile the application to the dist folder.

          
          ng build --watch
        

    ng serve 命令一样,当源文件发生变化时,就会重新生成输出文件。

    Like the ng serve command, this regenerates output files when source files change.

  • 在第二个终端上,安装一个 Web 服务器(比如 lite-server ),然后使用输出文件夹中的内容运行它。例如:

    On the second terminal, install a web server (such as lite-server), and run it against the output folder. For example:

          
          lite-server --baseDir="dist/project-name"
        

    每当输出了新文件时,服务器就会自动刷新你的浏览器。

    The server will automatically reload your browser when new files are output.

该方法只能用于开发和测试,在部署应用时,它不受支持,也不是安全的方式。

This method is for development and testing only, and is not a supported or secure way of deploying an application.

使用 CLI 进行自动部署

Automatic deployment with the CLI

Angular CLI 命令 ng deploy(在版本 8.3.0 中引入)执行与你的项目关联的 deploy CLI 构建器。有许多第三方构建器实现了到不同平台的部署功能。你可以通过运行 ng add [package name] 把它们中的任何一个添加到项目中。

The Angular CLI command ng deploy (introduced in version 8.3.0) executes the deploy CLI builder associated with your project. A number of third-party builders implement deployment capabilities to different platforms. You can add any of them to your project by running ng add [package name].

添加具有部署功能的程序包时,它将为所选项目自动更新自动更新工作区配置(angular.json 文件)中的 deploy 部分。然后,你就可以使用 ng deploy 命令来部署该项目了。

When you add a package with deployment capability, it'll automatically update your workspace configuration (angular.json file) with a deploy section for the selected project. You can then use the ng deploy command to deploy that project.

例如,以下命令将项目自动部署到 Firebase。

For example, the following command automatically deploys a project to Firebase.

      
      ng add @angular/fire
ng deploy
    

该命令是交互式的。在这种情况下,你必须拥有或创建 Firebase 帐户,并使用该帐户进行身份验证。该命令提示你选择要部署的 Firebase 项目。

The command is interactive. In this case, you must have or create a Firebase account, and authenticate using that account. The command prompts you to select a Firebase project for deployment

该命令会构建你的应用,并将生产环境的资产文件上传到 Firebase。

The command builds your application and uploads the production assets to Firebase.

在下表中,你可以找到实现了到不同平台部署功能的软件包列表。每个软件包的 deploy 命令可能需要不同的命令行选项。你可以通过以下与包名称相关的链接来阅读更多内容:

In the table below, you can find a list of packages which implement deployment functionality to different platforms. The deploy command for each package may require different command line options. You can read more by following the links associated with the package names below:

部署到

Deployment to

NPM 包

Package

Firebase hosting@angular/fire
Azure@azure/ng-deploy
Now@zeit/ng-deploy
Netlify@netlify-builder/deploy
GitHub pagesangular-cli-ghpages
NPMngx-deploy-npm
Amazon Cloud S3@jefiozie/ngx-aws-deploy

如果要部署到自己管理的服务器上,或者缺少针对你喜欢的云平台的构建器,则可以创建支持你使用 ng deploy 命令的构建器,或者通读本指南以了解如何手动部署应用程序。

If you're deploying to a self-managed server or there's no builder for your favorite cloud platform, you can either create a builder that allows you to use the ng deploy command, or read through this guide to learn how to manually deploy your application.

最简化的部署方式

Basic deployment to a remote server

最简化的部署方式就是为开发环境构建,并把其输出复制到 Web 服务器上。

For the simplest deployment, create a production build and copy the output directory to a web server.

  1. 使用开发环境进行构建

    Start with the production build:

          
          ng build
        
  2. 把输出目录(默认为 dist/)下的每个文件都复制到到服务器上的某个目录下。

    Copy everything within the output folder (dist/project-name/ by default) to a folder on the server.

  3. 配置服务器,让缺失的文件都重定向到 index.html 上。 欲知详情,参阅稍后的服务端重定向部分。

    Configure the server to redirect requests for missing files to index.html. Learn more about server-side redirects below.

这是对应用进行生产环境部署的最简方式。

This is the simplest production-ready deployment of your application.

部署到 GitHub Pages

Deploy to GitHub Pages

要将 Angular 应用程序部署到 GitHub Pages,请遵循以下步骤:

To deploy your Angular application to GitHub Pages, complete the following steps:

  1. 为你的项目创建一个 GitHub Pages 仓库

    Create a GitHub repository for your project.

  2. 通过添加指定你在上一步中创建的 GitHub 存储库的远端地址,来在本地项目中配置 git。创建存储库时,GitHub 已提供了这些命令,以便你可以在命令提示符下复制和粘贴它们。尽管 GitHub 会为你填上某些特定于项目的设置,但这些命令应该类似于以下形式:

    Configure git in your local project by adding a remote that specifies the GitHub repository you created in previous step. GitHub provides these commands when you create the repository so that you can copy and paste them at your command prompt. The commands should be similar to the following, though GitHub fills in your project-specific settings for you:

          
          git remote add origin https://github.com/your-username/your-project-name.git
    git branch -M main
    git push -u origin main
        

    当你从 GitHub 粘贴这些命令时,它们会自动运行。

    When you paste these commands from GitHub, they run automatically.

  3. 创建并签出一个名为 gh-pagesgit 分支。

    Create and check out a git branch named gh-pages.

          
          git checkout -b gh-pages
        
  4. 借助 Angular CLI 命令 ng build和以下选项,使用 Github 项目名称构建应用。这里的 your_project_name 是你在步骤 1 中为 GitHub 存储库提供的项目的名称。

    Build your project using the Github project name, with the Angular CLI command ng buildand the following options, where your_project_name is the name of the project that you gave the GitHub repository in step 1.

    确保在项目名称的两边都包含有斜杠,如 /your_project_name/ 的斜杠。

    Be sure to include the slashes on either side of your project name as in /your_project_name/.

          
          ng build --output-path docs --base-href /your_project_name/
        
  5. 当构建完成时,把 docs/index.html 复制为 docs/404.html

    When the build is complete, make a copy of docs/index.html and name it docs/404.html.

  6. 提交你的更改,并推送。

    Commit your changes and push.

  7. 在 GitHub 的项目页中,把该项目配置为从 docs 目录下发布

    On the GitHub project page, go to Settings and scroll down to the GitHub Pages section to configure the site to publish from the docs folder.

  8. 单击保存。

    Click Save.

  9. 单击 GitHub Pages 区顶部的 “GitHub Pages” 链接,以查看已部署的应用程序。链接的格式为 https://<user_name>.github.io/<project_name>/

    Click on the GitHub Pages link at the top of the GitHub Pages section to see your deployed application. The format of the link is https://<user_name>.github.io/<project_name>/.

参阅 angular-cli-ghpages,这个包用到了全部这些特性,还提供了一些额外功能。

Check out angular-cli-ghpages, a full featured package that does all this for you and has extra functionality.

服务端配置

Server configuration

这一节涵盖了你可能对服务器或准备部署到服务器的文件要做的那些修改。

This section covers changes you may have to make to the server or to files deployed on the server.

带路由的应用必须以 index.html 作为后备页面

Routed apps must fallback to index.html

Angular 应用很适合用简单的静态 HTML 服务器提供服务。 你不需要服务端引擎来动态合成应用页面,因为 Angular 会在客户端完成这件事。

Angular applications are perfect candidates for serving with a simple static HTML server. You don't need a server-side engine to dynamically compose application pages because Angular does that on the client-side.

如果该应用使用 Angular 路由器,你就必须配置服务器,让它对不存在的文件返回应用的宿主页(index.html)。

If the application uses the Angular router, you must configure the server to return the application's host page (index.html) when asked for a file that it does not have.

带路由的应用应该支持“深链接”。 所谓深链接就是指一个 URL,它用于指定到应用内某个组件的路径。 比如,http://www.mysite.com/heroes/42 就是一个到英雄详情页面的深链接,用于显示 id: 42 的英雄。

A routed application should support "deep links". A deep link is a URL that specifies a path to a component inside the application. For example, http://www.mysite.com/heroes/42 is a deep link to the hero detail page that displays the hero with id: 42.

当用户从运行中的客户端应用导航到这个 URL 时,这没问题。 Angular 路由器会拦截这个 URL,并且把它路由到正确的页面。

There is no issue when the user navigates to that URL from within a running client. The Angular router interprets the URL and routes to that page and hero.

但是,当从邮件中点击链接或在浏览器地址栏中输入它或仅仅在英雄详情页刷新下浏览器时,所有这些操作都是由浏览器本身处理的,在应用的控制范围之外。 浏览器会直接向服务器请求那个 URL,路由器没机会插手。

But clicking a link in an email, entering it in the browser address bar, or merely refreshing the browser while on the hero detail page — all of these actions are handled by the browser itself, outside the running application. The browser makes a direct request to the server for that URL, bypassing the router.

静态服务器会在收到对 http://www.mysite.com/ 的请求时返回 index.html,但是会拒绝对 http://www.mysite.com/heroes/42 的请求, 并返回一个 404 - Not Found 错误,除非,它被配置成了返回 index.html

A static server routinely returns index.html when it receives a request for http://www.mysite.com/. But it rejects http://www.mysite.com/heroes/42 and returns a 404 - Not Found error unless it is configured to return index.html instead.

后备页面配置范例

Fallback configuration examples

没有一种配置可以适用于所有服务器。 后面这些部分会描述对常见服务器的配置方式。 这个列表虽然不够详尽,但可以为你提供一个良好的起点。

There is no single configuration that works for every server. The following sections describe configurations for some of the most popular servers. The list is by no means exhaustive, but should provide you with a good starting point.

  • Apache:在 .htaccess 文件中添加一个重写规则, 代码如下(出处):

    Apache: add a rewrite rule to the .htaccess file as shown (https://ngmilk.rocks/2015/03/09/angularjs-html5-mode-or-pretty-urls-on-apache-using-htaccess/):

          
          RewriteEngine On
    # If an existing asset or directory is requested go to it as it is
    RewriteCond %{DOCUMENT_ROOT}%{REQUEST_URI} -f [OR]
    RewriteCond %{DOCUMENT_ROOT}%{REQUEST_URI} -d
    RewriteRule ^ - [L]
    # If the requested resource doesn't exist, use index.html RewriteRule ^ /index.html
  • NGinx:使用 try_files 指向 index.html,详细描述见Web 应用的前端控制器模式

    Nginx: use try_files, as described in Front Controller Pattern Web Apps, modified to serve index.html:

          
          try_files $uri $uri/ /index.html;
        
  • Ruby:使用 sinatra 和用来配置服务器的基础 Ruby 文件 server.rb 创建一个 Ruby 服务器:

    Ruby: create a Ruby server using (sinatra) with a basic Ruby file that configures the server server.rb:

          
          require 'sinatra'
    
    # Folder structure
    
    # .
    
    # -- server.rb
    
    # -- public
    
    #    |-- project-name
    #        |-- index.html
    
    get '/' do
        folderDir = settings.public_folder + '/project-name'  # ng build output folder
        send_file File.join(folderDir, 'index.html')
    end
        
  • IIS:往 web.config 中添加一条重写规则,类似于这里

    IIS: add a rewrite rule to web.config, similar to the one shown here:

          
          <system.webServer>
      <rewrite>
        <rules>
          <rule name="Angular Routes" stopProcessing="true">
            <match url=".*" />
            <conditions logicalGrouping="MatchAll">
              <add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true" />
              <add input="{REQUEST_FILENAME}" matchType="IsDirectory" negate="true" />
            </conditions>
            <action type="Rewrite" url="/index.html" />
          </rule>
        </rules>
      </rewrite>
    </system.webServer>
        
  • GitHub 页面服务:你没办法直接配置 Github 的页面服务,但可以添加一个 404 页,只要把 index.html 复制到 404.html 就可以了。 它仍然会给出一个 404 响应,但是浏览器将会正确处理该页,并正常加载该应用。 使用在主分支的 docs/ 下启动服务创建一个 .nojekyll 文件也是一个好办法。

    GitHub Pages: you can't directly configure the GitHub Pages server, but you can add a 404 page. Copy index.html into 404.html. It will still be served as the 404 response, but the browser will process that page and load the application properly. It's also a good idea to serve from docs/ on master and to create a .nojekyll file

  • Firebase 托管服务:添加一条重写规则

    Firebase hosting: add a rewrite rule.

          
          "rewrites": [ {
      "source": "**",
      "destination": "/index.html"
    } ]
        

请求来自另一个服务器的服务(CORS)

Requesting services from a different server (CORS)

Angular 开发者在向与该应用的宿主服务器不同域的服务器发起请求时,可能会遇到一种跨域资源共享(CORS)错误。 浏览器会阻止该请求,除非得到那台服务器的明确许可。

Angular developers may encounter a cross-origin resource sharing error when making a service request (typically a data service request) to a server other than the application's own host server. Browsers forbid such requests unless the server permits them explicitly.

客户端应用对这种错误无能为力。 服务器必须配置成可以接受来自该应用的请求。 要了解如何对特定的服务器开启 CORS,参阅enable-cors.org

There isn't anything the client application can do about these errors. The server must be configured to accept the application's requests. Read about how to enable CORS for specific servers at enable-cors.org.

为生产环境优化

Production optimizations

production 配置项指定如下优化特性。

The production configuration engages the following build optimization features.

  • 预先(AOT)编译:预编译 Angular 的组件模板。

    Ahead-of-Time (AOT) Compilation: pre-compiles Angular component templates.

  • 生产模式:部署到启用了生产模式的生产环境。

    Production mode: deploys the production environment which enables production mode.

  • 打包:把你的多个应用于库文件拼接到少量包(bundle)中。

    Bundling: concatenates your many application and library files into a few bundles.

  • 最小化:删除多余的空格、注释和可选令牌。

    Minification: removes excess whitespace, comments, and optional tokens.

  • 混淆/丑化:重写代码,使用简短的、不容易理解的变量名和函数名。

    Uglification: rewrites code to use short, cryptic variable and function names.

  • 消除死代码:删除未引用过的模块和很多未用到的代码。

    Dead code elimination: removes unreferenced modules and much unused code.

要了解关于 CLI 构建选项及其作用的更多知识,参阅 ng build

See ng buildfor more about CLI build options and what they do.

启用生产模式

Enable runtime production mode

除了构建期优化之外,Angular 还支持运行期生产模式。Angular 应用默认运行在开发模式下,你可以在浏览器的控制台中看到如下信息:

In addition to build optimizations, Angular also has a runtime production mode. Angular applications run in development mode by default, as you can see by the following message on the browser console:

      
      Angular is running in development mode. Call enableProdMode() to enable production mode.

Angular 正运行在开发模式下。调用 enableProdMode()以启用生产模式。
    

生产模式通过禁用仅供开发用的安全检查和调试工具(例如,expression-changed-after-checked 检测)来提高应用程序性能。使用生产配置构建应用程序时会自动启用 Angular 的运行时生产模式。

Production mode improves application performance by disabling development-only safety checks and debugging utilities, such as the expression-changed-after-checked detection. Building your application with the production configuration automatically enables Angular's runtime production mode.

惰性加载

Lazy loading

通过只加载应用启动时绝对必须的那些模块,你可以极大缩短应用启动的时间。

You can dramatically reduce launch time by only loading the application modules that absolutely must be present when the application starts.

可以配置 Angular 的路由器,来推迟所有其它模块(及其相关代码)的加载时机,方法有一直等到应用启动完毕,或者当用到时才按需惰性加载

Configure the Angular Router to defer loading of all other modules (and their associated code), either by waiting until the app has launched or by lazy loading them on demand.

不要急性(eagerly)导入来自惰性加载模块中的任何东西
Don't eagerly import something from a lazy-loaded module

如果要惰性加载某个模块,就要小心别在应用启动时要急性加载的模块(比如根模块 AppModule)中导入它。 如果那么做,该模块就会立刻加载起来。

If you mean to lazy-load a module, be careful not to import it in a file that's eagerly loaded when the application starts (such as the root AppModule). If you do that, the module will be loaded immediately.

配置打包方式时必须考虑惰性加载。 因为默认情况下惰性加载的模块没有在 JavaScript 中导入过,因此打包器默认会排除它们。 打包器不认识路由器配置,也就不能为惰性加载的模块创建独立的包。 你必须手动创建这些包。

The bundling configuration must take lazy loading into consideration. Because lazy-loaded modules aren't imported in JavaScript, bundlers exclude them by default. Bundlers don't know about the router configuration and can't create separate bundles for lazy-loaded modules. You would have to create these bundles manually.

CLI 会运行 Angular Ahead-of-Time Webpack 插件,它会自动识别出惰性加载的 NgModules,并为它们创建独立的包。

The CLI runs the Angular Ahead-of-Time Webpack Plugin which automatically recognizes lazy-loaded NgModules and creates separate bundles for them.

测量性能

Measure performance

如果你对哪些东西拖慢了应用有更加清晰、精确的了解,就可以更好地决定优化什么以及如何优化。 慢的原因可能和你所想的不一样。 你可能花费了大量的时间和金钱来优化一些实际上无关紧要的东西,甚至可能让应用变得更慢。 你应该测量应用在运行环境中的实际行为,这才是最重要的。

You can make better decisions about what to optimize and how when you have a clear and accurate understanding of what's making the application slow. The cause may not be what you think it is. You can waste a lot of time and money optimizing something that has no tangible benefit or even makes the application slower. You should measure the application's actual behavior when running in the environments that are important to you.

Chrome DevTools 的网络和性能页是你开始学习如何测量性能的好地方。

The Chrome DevTools Network Performance page is a good place to start learning about measuring performance.

WebPageTest工具是另一个不错的选择,它还能帮你验证这次部署是否成功。

The WebPageTest tool is another good choice that can also help verify that your deployment was successful.

检查发布包

Inspect the bundles

source-map-explorer 工具可以帮你在生产环境构建之后探查 JavaScript 包。

The source-map-explorer tool is a great way to inspect the generated JavaScript bundles after a production build.

安装 source-map-explorer

Install source-map-explorer:

      
      npm install source-map-explorer --save-dev
    

为生产环境构建应用,包括源码映射表(source map)

Build your application for production including the source maps

      
      ng build --source-map
    

dist/ 目录下列出生成的包。

List the generated bundles in the dist/project-name/ folder.

      
      ls dist/project-name/*.js
    

运行浏览器来生成其中一个包的图形化表示。 下面的例子展示了 main 包的图表。

Run the explorer to generate a graphical representation of one of the bundles. The following example displays the graph for the main bundle.

      
      node_modules/.bin/source-map-explorer dist/project-name/main*
    

source-map-explorer 会分析与包一起生成的 source map,并画出所有依赖的地图,精确展示哪些类包含在哪个包中。

The source-map-explorer analyzes the source map generated with the bundle and draws a map of all dependencies, showing exactly which classes are included in the bundle.

下面是范例应用 cli-quickstartmain 包的输出。

Here's the output for the main bundle of an example application called cli-quickstart.

base 标签

The base tag

HTML 的 <base href="..."/> 标签指定了用于解析静态文件(如图片、脚本和样式表)相对地址的基地址。 比如,对于 <base href="/my/app/">,浏览器就会把 some/place/foo.jpg 这样的 URL 解析成到 my/app/some/place/foo.jpg 的请求。 在导航期间,Angular 路由器使用 base href 作为到组件模板文件和模块文件的基地址。

The HTML <base href="..."/> specifies a base path for resolving relative URLs to assets such as images, scripts, and style sheets. For example, given the <base href="/my/app/">, the browser resolves a URL such as some/place/foo.jpg into a server request for my/app/some/place/foo.jpg. During navigation, the Angular router uses the base href as the base path to component, template, and module files.

另一种方式参阅 APP_BASE_HREF

See also the APP_BASE_HREF alternative.

在开发期间,你通常会在存有 index.html 的目录下启动开发服务器。 那就是根目录,你要在 index.html 的顶部附近添加 <base href="/">,因为 / 就是该应用的根路径。

In development, you typically start the server in the folder that holds index.html. That's the root folder and you'd add <base href="/"> near the top of index.html because / is the root of the application.

但是在共享或生产服务器上,你可能会在子目录下启动服务器。 比如,当前应用的加载地址可能类似于 http://www.mysite.com/my/app/,这里的子目录就是 my/app/。所以你就要往服务端版本的 index.html 中添加 <base href="/my/app/">

But on the shared or production server, you might serve the application from a subfolder. For example, when the URL to load the application is something like http://www.mysite.com/my/app/, the subfolder is my/app/ and you should add <base href="/my/app/"> to the server version of the index.html.

这里如果不配置 base 标签,应用就会失败,并在浏览器的控制台中为缺失的文件显示一个 404 - Not Found 错误。看看它试图从哪里去查找那些文件,并据此调整 base 标签。

When the base tag is mis-configured, the application fails to load and the browser console displays 404 - Not Found errors for the missing files. Look at where it tried to find those files and adjust the base tag appropriately.

差异化加载

Differential Loading

在构建 Web 应用时,你肯定想确保你的应用与大多数浏览器兼容。JavaScript 在不断发展,新功能不断推出,不是所有浏览器都能以同样的进度实现这些新功能。

When building web applications, you want to make sure your application is compatible with the majority of browsers. Even as JavaScript continues to evolve, with new features being introduced, not all browsers are updated with support for these new features at the same pace.

你在开发过程中使用 TypeScript 编写的代码会被编译并打包成 ES2015,这种 JavaScript 语法兼容大多数浏览器。 所有现代浏览器都支持 ES2015 和更新的版本,但是大多数情况下,你仍然要让用户能从不支持它的浏览器中访问你的应用。 当以老式浏览器为目标时,腻子脚本(polyfills)可以提供一些老式浏览器中不存在的功能,从而抹平这种差距。

The code you write in development using TypeScript is compiled and bundled into ES2015, the JavaScript syntax that is compatible with most browsers. All modern browsers support ES2015 and beyond, but in most cases, you still have to account for users accessing your application from a browser that doesn't. When targeting older browsers, polyfills can bridge the gap by providing functionality that doesn't exist in the older versions of JavaScript supported by those browsers.

为了最大限度地提高兼容性,你可以发布一个包含所有已编译代码的发布包(bundle),以及所有可能会用到的腻子脚本。用户如果在支持大量最新 JavaScript 特性的现代浏览器中使用此应用,就不应该为这些他们用不到的包带来的额外体积付出代价。差异化加载可以帮你解决这个问题。Angular CLI 8 及更高版本就支持它。

To maximize compatibility, you could ship a single bundle that includes all your compiled code, plus any polyfills that may be needed. Users with modern browsers, however, shouldn't have to pay the price of increased bundle size that comes with polyfills they don't need. Differential loading, which is supported in Angular CLI version 8 and higher, can help solve this problem.

差异化加载是一种策略,它能让你的应用支持多种浏览器,但是只加载当前浏览器必须用到的代码。 当启用了差异化加载时,CLI 会构建出两个单独的包,作为你要发布的应用的一部分。

Differential loading is a strategy that allows your web application to support multiple browsers, but only load the necessary code that the browser needs. When differential loading is enabled the CLI builds two separate bundles as part of your deployed application.

  • 第一个包是使用现代的 ES2015 语法,它能发挥现代浏览器内置支持的优势,发布更少的腻子脚本,因此打包尺寸更小。

    The first bundle contains modern ES2015 syntax. This bundle takes advantage of built-in support in modern browsers, ships fewer polyfills, and results in a smaller bundle size.

  • 第二个包使用老式的 ES5 语法,包含所有必要的腻子脚本。第二个包的尺寸更大,但是支持老式浏览器。

    The second bundle contains code in the old ES5 syntax, along with all necessary polyfills. This second bundle is larger, but supports older browsers.

差异化构建

Differential builds

使用 Angular CLI 构建过程进行部署时,可以选择如何以及何时支持差异化加载。ng build CLI 命令会查询浏览器配置和配置的构建目标,以确定是否需要支持旧版浏览器,以及该构建是否应产生用于差异化加载的必要捆绑包。

When you deploy using the Angular CLI build process, you can choose how and when to support differential loading. The ng build CLI command queries the browser configuration and the configured build target to determine if support for legacy browsers is required, and whether the build should produce the necessary bundles used for differential loading.

会根据下列配置确定你的要求。

The following configurations determine your requirements.

  • 浏览器列表

    Browserslist

    browserslist 配置文件包含在应用的项目结构中,它提供了本应用打算支持的最低浏览器版本。关于完整的配置选项,请参阅 Browserslist 规范

    The Browserslist configuration file is included in your application project structure and provides the minimum browsers your application supports. See the Browserslist spec for complete configuration options.

  • TypeScript 配置

    TypeScript configuration

    在 TypeScript 配置文件中,compilerOptions 区的 target 选项会决定编译后代码的 ECMAScript 目标版本。现代浏览器原生支持 ES2015,而 ES5 则更常用于支持老式浏览器。

    In the TypeScript configuration file, the "target" option in the compilerOptions section determines the ECMAScript target version that the code is compiled to. Modern browsers support ES2015 natively, while ES5 is more commonly used to support legacy browsers.

当前仅在将 es2015 用作编译目标时才支持差异化加载。当目标高于 es2015 时,构建过程将发出警告。

Differential loading is currently only supported when using es2015 as a compilation target. When used with targets higher than es2015, the build process emits a warning.

对于开发版本,由 ng build 生成的输出更简单且易于调试,从而减小你对编译代码的 sourcemaps 的依赖。

For a development build, the output produced by ng build is simpler and easier to debug, allowing you to rely less on sourcemaps of compiled code.

对于生产版本,你的配置将决定创建哪些捆绑软件来部署你的应用程序。必要时,还会在构建过程中修改 index.html 文件,以包括启用差异化加载的脚本标签,如以下范例所示。

For a production build, your configuration determines which bundles are created for deployment of your application. When needed, the index.html file is also modified during the build process to include script tags that enable differential loading, as shown in the following example.

index.html
      
      <body>
  <app-root></app-root>
  <script src="runtime-es2015.js" type="module"></script>
  <script src="runtime-es5.js" nomodule></script>
  <script src="polyfills-es2015.js" type="module"></script>
  <script src="polyfills-es5.js" nomodule></script>
  <script src="styles-es2015.js" type="module"></script>
  <script src="styles-es5.js" nomodule></script>
  <script src="vendor-es2015.js" type="module"></script>
  <script src="vendor-es5.js" nomodule></script>
  <script src="main-es2015.js" type="module"></script>
  <script src="main-es5.js" nomodule></script>
</body>
    

每个 script 标签都有一个 type="module"nomodule 属性。原生支持 ES 模块的浏览器只会加载带有该类型属性的脚本,而忽略那些带有 nomodule 属性的脚本。而老式浏览器只会加载带有 nomodule 属性的脚本,而忽略那些 type 为 module 的脚本标签。

Each script tag has a type="module" or nomodule attribute. Browsers with native support for ES modules only load the scripts with the module type attribute and ignore scripts with the nomodule attribute. Legacy browsers only load the scripts with the nomodule attribute, and ignore the script tags with the module type that load ES modules.

一些旧版浏览器仍会下载两个捆绑包,但只会根据上述属性执行适当的脚本。你可以在此处阅读关于此问题的更多信息

Some legacy browsers still download both bundles, but only execute the appropriate scripts based on the attributes mentioned above. You can read more on the issue here.

配置差异化加载

Configuring differential loading

要想在构建应用时包含差异化加载特性,你必须修改项目中的 Browserslist 和 TypeScript 配置文件。

To include differential loading in your application builds, you must configure the Browserslist and TypeScript configuration files in your application project.

下面的例子展示了新创建的 Angular 应用的 browserlistrctsconfig.json 文件。 在这份配置中,老式浏览器(比如 IE 9-11)都被忽略了,其编译目标是 ES2015。

The following examples show a .browserslistrc and tsconfig.json file for a newly created Angular application. In this configuration, legacy browsers such as IE 9-11 are ignored, and the compilation target is ES2015.

.browserslistrc
      
      # This file is used by the build system to adjust CSS and JS output to support the specified browsers below.

# 构建系统使用此文件来调整 CSS 和 JS 输出,以支持下列指定的浏览器。

# For additional information regarding the format and rule options, please see:

# 有关格式和规则选项的其他信息,请参见:

# https://github.com/browserslist/browserslist#queries

# For the full list of supported browsers by the Angular framework, please see:

# 有关 Angular 框架支持的浏览器的完整列表,请参见:

# https://angular.io/guide/browser-support

# You can see what browsers were selected by your queries by running:

# 你可以通过运行以下命令查看你指定的查询选择了哪些浏览器:

#   npx browserslist

last 1 Chrome version
last 1 Firefox version
last 2 Edge major versions
last 2 Safari major versions
last 2 iOS major versions
Firefox ESR
not IE 11 # Angular supports IE 11 only as an opt-in. To opt-in, remove the 'not' prefix on this line.
    
tsconfig.json
      
      {
  "compileOnSave": false,
  "compilerOptions": {
    "baseUrl": "./",
    "outDir": "./dist/out-tsc",
    "sourceMap": true,
    "declaration": false,
    "module": "esnext",
    "moduleResolution": "node",
    "emitDecoratorMetadata": true,
    "experimentalDecorators": true,
    "importHelpers": true,
    "target": "es2015",
    "typeRoots": [
      "node_modules/@types"
    ],
    "lib": [
      "es2018",
      "dom"
    ]
  }
}
    

要查看浏览器的支持度,以决定哪些设置适合你要支持的浏览器,请参阅“ 浏览器列表兼容性”页面

To see which browsers are supported and determine which settings meet to your browser support requirements, see the Browserslist compatibility page.

Browserslist 配置允许你忽略不支持 ES2015 的浏览器。在这种情况下,将只生成一个版本。

The Browserslist configuration allows you to ignore browsers without ES2015 support. In this case, a single build is produced.

如果你的 Browserslist 配置包括对所有旧版浏览器的支持,则 TypeScript 配置中的构建目标将确定该构建是否将支持差异化加载。

If your Browserslist configuration includes support for any legacy browsers, the build target in the TypeScript configuration determines whether the build will support differential loading.

浏览器列表

Browserslist

ES 目标

ES target

构建结果

Build result

禁用 ES5 支持

ES5 support disabled

es2015

单一构建,不行可 ES5

Single build, ES5 not required

启用 ES5 支持

ES5 support enabled

es5

单一构建,按需附带只供 ES5 使用的腻子脚本

Single build w/conditional polyfills for ES5 only

弃用 ES5 支持

ES5 support enabled

es2015

差异化加载 (按需附带两种构建需要的腻子脚本)

Differential loading (two builds w/conditional polyfills)

旧版浏览器中的本地开发

Local development in older browsers

在 Angular CLI 10 或更高版本生成的应用项目中,默认是不启用差异化加载的。 ng serveng testng e2e 命令只会生成一个 ES2015 版本,该版本无法在不支持该模块的旧版浏览器(例如 IE 11)中运行。

Differential loading is not enabled by default for application projects that were generated with Angular CLI 10 and above. The ng serve, ng test, and ng e2e commands, however, generate a single ES2015 build which cannot run in older browsers that don't support the modules, such as IE 11.

要保持差异化加载的好处,更好的选择是为 ng serveng e2eng test 定义多个配置。

To maintain the benefits of differential loading, however, a better option is to define multiple configurations for ng serve, ng e2e, and ng test.

为 ES5 配置服务

Configuring serve for ES5

要让 ng serve 做到这一点,就要在 tsconfig.app.json 后面创建一个新的文件 tsconfig-es5.app.json,包含以下内容。

To do this for ng serve, create a new file, tsconfig-es5.app.json next to tsconfig.app.json with the following content.

      
      {
 "extends": "./tsconfig.app.json",
 "compilerOptions": {
     "target": "es5"
  }
}
    

angular.json 中,在 buildserve 下添加两个新的配置节,其目标指向新的 TypeScript 配置。

In angular.json add two new configuration sections under the build and serve targets to point to the new TypeScript configuration.

      
      "build": {
  "builder": "@angular-devkit/build-angular:browser",
  "options": {
      ...
  },
  "configurations": {
    "production": {
        ...
    },
    "es5": {
      "tsConfig": "./tsconfig-es5.app.json"
    }
  }
},
"serve": {
  "builder": "@angular-devkit/build-angular:dev-server",
  "options": {
      ...
  },
  "configurations": {
    "production": {
     ...
    },
    "es5": {
      "browserTarget": "<app-name>:build:es5"
    }
  }
},
    

然后,你可以使用此配置运行 ng serve 命令。务必确保将 <app-name>(在 "<app-name>:build:es5" 中)替换为应用程序的实际名称,因为它也会出现在 angular.jsonprojects 中。例如,如果你的应用程序名称为 myAngularApp 则配置要变成 "browserTarget": "myAngularApp:build:es5"

You can then run the ng serve command with this configuration. Make sure to replace <app-name> (in "<app-name>:build:es5") with the actual name of the app, as it appears under projects in angular.json. For example, if your application name is myAngularApp the configuration will become "browserTarget": "myAngularApp:build:es5".

      
      ng serve --configuration es5
    

配置 test 命令

Configuring the test command

创建一个新的文件,在 tsconfig.spec.json 后面 tsconfig-es5.spec.json,包含以下内容。

Create a new file, tsconfig-es5.spec.json next to tsconfig.spec.json with the following content.

      
      {
 "extends": "./tsconfig.spec.json",
 "compilerOptions": {
     "target": "es5"
  }
}
    
      
      "test": {
  "builder": "@angular-devkit/build-angular:karma",
  "options": {
      ...
  },
  "configurations": {
    "es5": {
      "tsConfig": "./tsconfig-es5.spec.json"
    }
  }
},
    

然后,你可以使用此配置运行测试了

You can then run the tests with this configuration

      
      ng test --configuration es5