部署

部署

本章会描述在远程服务器上部署 Angular 应用的工具与技术。

最简化的部署方式

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

  • 使用开发环境进行构建 content_copyng build

这不是生产级部署。它没有优化过,并且对用户来说也不够快。 但是当你向经理、团队成员或其它利益相关者内部分享你的进度和想法时它是足够的。

为生产环境优化

虽然也可以直接用开发环境的设置进行部署,不过你也可以使用 CLI 命令的其它标志生成一个优化过的构建成果。 先来看 --prod

使用 --prod 构建。

content_copyng build --prod

--prod 元标志包括下列优化特性。

剩下的 拷贝部署步骤 和以前的方式是一样的。

你还可以添加 build-optimizer 标志来进一步缩减打包体积。

content_copyng build --prod --build-optimizer

参见 CLI 文档,来了解可用的构建选项及其用途的详细信息。

启用生产模式

Angular 应用默认运行在开发模式下,正如在浏览器控制台中看到的如下信息:

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

切换到生产模式可以通过禁用开发环境下特有的检查(比如双重变更检测周期)来让应用运行得更快。

为生产环境构建(或添加 --environment=prod 标志)可以启用生产模式。 查看 CLI 自动生成的 main.ts 文件来了解它的工作原理。

惰性加载

通过只加载应用启动时必须展示的那些应用模块,你可以显著缩减启动时间。

配置 Angular 路由器可以延迟加载所有其它模块(以及与它们相关的代码),无论是等应用启动, 还是在需要时才惰性加载

不要立即导入惰性加载模块中的任何东西

这是一种常犯的错误。 你本打算惰性加载一个模块,但可能无意中在根模块 AppModule 文件中使用一个 JavaScript 的 import 语句导入了它。 这样一来,该模块就被立即加载了。

关于打包(bundle)方式的配置必须考虑到惰性加载问题。 因为惰性加载模块不能在 JavaScript 中导入(就像刚才说明的),打包器应该默认排除它们。 打包器不知道路由器的配置,并且不会为延迟加载模块创建单独的包。 你不得不手动创建这些包。

CLI 会运行 Angular AOT 编译 Webpack 插件,它会自动识别出那些需要惰性加载的 NgModule,并为它们创建单独的文件包。

性能测量

如果你能对“是什么导致了应用变慢”的问题有一个清晰、准确的理解,那就可以对优化什么、如何优化做出更好地决策了。 真正的原因可能并不是你所想的那样。 你可能花费大量的时间和金钱去优化一些东西,但它却无法产生可感知的效果甚至让应用变得更慢。 你应该在那些最重要的环境中实际运行,来度量应用的实际行为。

Chrome 开发工具的网络性能页是开始学习度量性能的好地方。

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

深入探查文件包(bundle)

source-map-explorer 是在生产环境构建中深入探查所生成的文件包的好工具。

安装 source-map-explorer

content_copynpm install source-map-explorer --save-dev

构建带源码映射的生产版本

content_copyng build --prod --source-map

列出 dist/ 文件夹中生成的文件包。

content_copyls dist/*.bundle.js

运行这个源码映射浏览器,以生成文件包之一的图形化表示。 下面的例子中就是 main 这个文件包的图形。

content_copynode_modules/.bin/source-map-explorer dist/main.*.bundle.js

source-map-explorer 分析了文件包生成的源码映射信息,并画出了所有这些依赖的地图,准确的展示了这个包中包含了哪些类。

下面是《快速起步》一章生成的 main 文件包的输出。

base 标签

HTML 中的<base href="..."/>用于指定一个解析相对路径的基地址,如图片、脚本和样式表。 比如,指定 <base href="/my/app/"> 时,浏览器就会把 some/place/foo.jpg 这样的 URL 解析成到 my/app/some/place/foo.jpg 的服务端请求。 在浏览期间,Angular 路由器会使用base href作为组件、模板和模块文件的基地址。

参见另一种备选方案APP_BASE_HREF

在开发期间,你通常会在 index.html 所在的目录中启动服务器。这个目录就是根目录,因为 / 就是本应用的根,所以你要在 index.html 的顶部添加 <base href="/">。

但是在共享服务器或生产服务器上,你可能得从子目录下启动服务器。 比如,当加载本应用的 URL 是 http://www.mysite.com/my/app/ 时,子目录就是 my/app/,而你就要在服务器版的 index.html 中添加 <basehref="/my/app/">。

当没有配置 base 标签时,加载应用就会失败,浏览器的控制台中会为这些缺失的文件显示一些 404 - Not Found 错误。 看看浏览器试图从哪里找这些文件,然后调整出合适的 base 标签。

构建与服务

你会更喜欢用 ng build 进行部署。

ng build 命令的设计意图是构建该应用,并且把构建成果部署到别处。 而ng serve 命令的设计意图是快速进行本地的迭代式开发。

在开始构建项目之前,ng buildng serve 都会清空输出文件夹ng build 命令会把生成的构建成果写入输出文件夹中,但 ng serve 命令并不会如此。 它会用内存中的构建成果提供服务,以获得更快速的开发体验。

默认的输出文件夹是 dist/。 要输出到其它文件夹中,请修改 angular.json 中的 outputPath

ng serve 命令会构建、监听并使用本地的 CLI 开发服务器作为服务器。

ng build 命令只会生成一次这些输出文件,而不会用它们提供服务。 ng build --watch 命令会在源码变化的时候重新生成输出文件。 当你在开发期间需要不断构建并自动把修改后的版本发布到另一台服务器的时候,这个 --watch 标志会很有用。

参见 CLI 中的 build 主题以了解详情以及其它选项。

服务端配置

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

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

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

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

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

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

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

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

后备页面配置范例

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

开发服务器

  • Lite-Server是"快速上手"仓库中安装的默认开发服务器,它被预先配置为回退到 index.html

生产服务器

  • Apache:在 .htaccess 文件中添加一个重写规则, 代码如下(出处): content_copyRewriteEngine 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

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

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

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