Angular v9 和 Universal:开箱即用的 SSR 和预渲染!
最初于 2020 年 1 月 5 日发布于https://samvloeberghs.be
目标受众
本文和指南旨在帮助所有使用 Angular v9 的用户开始使用服务器端渲染 (SSR) 并预渲染其应用程序。请注意,这些说明仅适用于使用 Angular v9 的新应用或更新的应用。要使 Angular v2-v8 获得相同的效果,需要进行更多自定义设置。
示例项目
对于示例项目,我们使用一个启用了路由的、由 angular-cli 生成的最小项目。此外,还生成了 home( /
) 和 about( ) 路由及其组件。通过使用惰性加载的新闻模块,还配置了/about
额外的动态 overview( /news
) 和 newsdetail( ) 路由。/news/:id
此概述和详细信息页面的数据是我们从静态资产文件夹(/assets/news.json
)加载的简单 JSON 数据对象,如下所示:
// /assets/news.json
[
{
"id": 1,
"title": "Newsitem #1",
"short": "Lorem ipsum dolor sit amet, ...",
},
{
"id": 2,
"title": "Newsitem #2",
"short": "Lorem ipsum dolor sit amet, ..."
}
]
以下动画展示了我们的应用程序如何使用路由和加载动态数据。此基本示例应用程序的完整源代码可在此处找到。
向您的应用程序添加服务器端渲染(SSR)
通常情况下,你的 Angular 应用只会在浏览器加载后立即渲染。在这种情况下,Web 服务器的唯一职责就是提供 Angular 应用的静态文件(构建成功后 dist 文件夹中的所有内容)。
使用 SSR,应用程序的特定路由(例如/about
)将完全在服务器上渲染,就像在浏览器中渲染一样。这使得像 Google 这样的搜索引擎和像 Facebook 这样的社交媒体平台能够索引并显示应用程序页面的预览,因为完整的 HTML 内容从服务器初始加载时即可获取,而无需 JavaScript。
入门
首先,您需要做的就是@nguniversal/express-engine
使用 angular-cli 添加包:
ng add @nguniversal/express-engine@next
# as soon as v9 is released you can drop the "@next"
该ng add
命令通过添加必要的文件和更新angular.json
配置文件来更新您的应用程序。架构部分新增了 3 个配置:server
、serve-ssr
和prerender
。这 3 个配置各有用途,它们共同帮助我们实现所需的结果。让我们来看看这些变化。
// updated angular.json
{
"$schema": "./node_modules/@angular/cli/lib/config/schema.json",
"version": 1,
"newProjectRoot": "projects",
"projects": {
"ng-v9-universal": {
"..": "..",
"architect": {
"..": "..",
"server": {
"builder": "@angular-devkit/build-angular:server",
"options": {
"outputPath": "dist/ng-v9-universal/server",
"main": "server.ts",
"tsConfig": "tsconfig.server.json"
},
"configurations": {
"production": {
"..": ".."
}
}
},
"serve-ssr": {
"builder": "@nguniversal/builders:ssr-dev-server",
"options": {
"browserTarget": "ng-v9-universal:build",
"serverTarget": "ng-v9-universal:server"
},
"configurations": {
"production": {
"browserTarget": "ng-v9-universal:build:production",
"serverTarget": "ng-v9-universal:server:production"
}
}
},
"prerender": {
"builder": "@nguniversal/builders:prerender",
"options": {
"browserTarget": "ng-v9-universal:build:production",
"serverTarget": "ng-v9-universal:server:production",
"routes": []
},
"configurations": {
"production": {}
}
}
}
}
}
"..": ".."
}
server
运行选项
在你的系统(或后端)上运行 Angular 构建需要服务器,因此该server.ts
文件会被添加到项目的根目录。这个 TypeScript 文件包含渲染路由和提供应用程序静态资源所需的所有设置。如第 13-15 行所示,它定义了此服务器的输出路径,以及一个tsConfig
定义如何构建服务器 TypeScript 代码的变量。与构建一样,browser
我们也可以定义特定于生产环境的环境变量。
构建此服务器与构建 Angular 应用程序一样简单。只需在终端中运行以下命令,服务器就会构建到dist/ng-v9-universal/server
。
npm run build:ssr
serve-ssr
运行选项
在开发应用程序时,您可能只需运行ng serve
。此命令将为您设置一个开发服务器,该服务器具有热重载功能以及其他所有功能,可为您提供良好的开发体验。
测试服务器端渲染部分也可以在开发过程中进行。只需以 运行应用程序,ng run ng-v9-universal:serve-ssr
服务器源文件就会与应用程序源文件一起被监视。由于实时重载,更改应用程序或服务器的代码将自动重新启动应用程序。
现在,如果您重新加载任何页面,例如新闻概览(/news
),初始有效负载将包含新闻概览的完整呈现 HTML,包括动态数据。
prerender
运行选项
最后一项新增功能是可以预渲染应用程序的路由。为了实现此功能,预渲染构建器会使用(由@mgechevguess-parser
构建)来猜测静态路由。它会分析应用程序的路由模块,并对找到的路由进行预渲染。
要预渲染应用程序的路由,只需运行:
npm run prerender
渲染更多(动态)路线
如配置文件第 41 行所示angular.json
,可以列出应用程序的其他已知路由。例如,在我们的例子中,我们可以列出/news
、/news/1
和/news/2
路由。这将指示构建器也预渲染这些额外的路由。对于惰性加载的模块来说,这一点尤其重要,因为 guess-parser 不容易猜到这些路由。
为了渲染更动态的路由,例如示例应用中的新闻详情页,我们需要更多逻辑。基本上,你需要找到一种方法让预渲染器知道哪些路由需要预渲染。例如,可以定义一个list-routes.js
脚本,将所有路由列在一个文本文件中。然后,可以将该文件传递给预渲染脚本,如下所示:
npm run prerender --routesFile routes.txt
下面是一个列出所有必需路由的脚本示例。此文本文件中列出的所有路由在预渲染之前都会添加到配置文件routes
数组中已定义的路由中angular.json
。
// scripts/list-routes.js
const fs = require('fs');
const axios = require('axios');
const endOfLine = require('os').EOL;
const newsDataPath = 'http://localhost:4200/assets/news.json';
const routesFile = './routes.txt';
axios.get(newsDataPath).then(res => {
const routes = [];
res.data.forEach(newsitem => {
routes.push('news/' + newsitem.id);
});
fs.writeFileSync(routesFile, routes.join(endOfLine), 'utf8');
}).catch(e => console.log(e));
我们的脚本package.json
现在看起来像这样:
// updated package.json
{
"name": "ng-v9-universal",
"version": "0.0.0",
"scripts": {
"..", "..",
"list-routes": "node ./scripts/list-routes.js",
"prerender": "npm run list-routes && ng run ng-v9-universal:prerender --routesFile routes.txt"
},
"..": ".."
}
重要提示:运行npm run prerender
命令时,请确保应用程序也处于运行状态。静态文件/assets/news.json
需要可用,以便应用程序预渲染所有路由!您只需在另一个终端运行 ng serve 即可完成此操作。
结论
我最初是用 Angular v2 和 Universal 来创建这个博客的,在 2016 年的时候,搭建起来并不容易。现在 Universal 和 Angular v9 的结合极大地提升了开发者的体验,现在只需遵循清晰的步骤即可实现。
现在只需一个命令即可测试您的应用程序及其服务器端渲染版本。只需运行npm run dev:ssr
即可开始!
预渲染静态路由很容易,除非你使用了延迟加载的路由,否则这些路由很难被猜到guess-parser
。你仍然需要一种方法来渲染所有路由。这可以通过将路由列在一个文件中,然后使用选项将其提供给构建器来实现--routesFile
。如何获取路由列表由你决定。
进一步阅读
Angular Universal v9:有什么新功能?
Vikram @vikerman:我最近发布的 Angular 团队推文