最佳实践:Angular 使用动态回退的 SSR 部分预渲染
Best Practice: Angular SSR Partial Pre-rendering with dynamic fallback
背景:使用Angular Universal进行预渲染,但不会渲染所有路由(大部分为查询参数或仅认证页面),所以想根据需要回退到快速渲染器。
快速复制(bash):
npm install -g @angular/cli@next
ng new partial-prerender -s -t --minimal --routing --interactive=false
cd partial-prerender/
ng add @nguniversal/express-engine@'^9.0.0-rc.1'
ng g m child --route child --module app
cat << 'EOF' > src/app/app.component.ts
import { Component } from '@angular/core';
@Component({
template: `<div [routerLink]="['/']">Root</div><div [routerLink]="['child']">Child</div><router-outlet></router-outlet>`,
})
export class AppComponent {}
EOF
npm run prerender
npm run serve:ssr
此快速复制将生成应用程序、通用实施、子页面,并替换应用程序 html 以提供 2 个链接和一个路由器插座,然后 build/pre-render。两条路线都将被预渲染,但这足以讨论这个问题。
问题: 执行动态 SSR,因为 Express 服务器将接收请求而不是提供预呈现的静态文件。 URL 通常在没有指定 /index.html
的情况下被访问。
请注意,可以在 /dist/partial-prerender/browser/index.html
和 .../child/index.html
中找到静态文件。为了进行测试,我已将这些文件的内容替换为垃圾文件,以确保一目了然地确定正在加载哪些文件。
也可以加一个console.log('DYNAMIC');
到server.ts
:
server.get('*', (req, res) => {
console.log('DYNAMIC');
res.render(indexHtml, { req, providers: [{ provide: APP_BASE_HREF, useValue: req.baseUrl }] });
});
当向 localhost:4000
或 localhost:4000/child
发出请求时,将打印 'DYNAMIC' 并生成动态渲染版本,而不是给我损坏的预渲染文件。
向localhost:4000/index.html
或localhost:4000/child/index.html
发出请求时,
server.get('*.*', express.static(distFolder, { maxAge: '1y' }));
获取并提供损坏的文件。
一切都是有道理的,为什么会这样,但我希望能够只点击给定的 url(没有 /index.html
并接收预渲染文件(如果可用),然后回过头来让 SSR 发挥作用。
可能的解决方案:
修改 server.ts
以测试与给定请求路径匹配的文件是否存在 + /index.html
并为它们提供服务,回落到 res.render(...
- 这是最好的方法吗?
- 如果是这样,为什么这不是默认功能?我唯一的猜测是您可以灵活地使用反向代理执行此操作,同时不会增加检查开销。
- 最好的方法是什么?
- 大概 6 年没有大量使用 Express,但感觉
express.static
应该以某种方式比 fs
- 如果
fs
是答案,将预渲染文件缓存在内存中是否有意义?
如果有帮助,我会生成一个 alpine-node 容器并使用 Nginx 入口部署到 K8s。仅提及这一点,因为可能有一个神奇的类似 try-files
的功能,可以完成 'attempt' 文件 + /index.html
从节点容器检索,然后在没有 /index.html
的情况下回退,但似乎极不可能。
您可以像这样在 get 请求中使用 if 语句
const fullPath = join(distFolder, req.originalUrl);
if (existsSync(fullPath)) {
console.log('STATIC Exists');
return res.sendFile(join(distFolder, req.originalUrl));
} else {
//Dynamic
res.render(indexHtml, { req, providers: [{ provide: APP_BASE_HREF, useValue: req.baseUrl }] });
}
npm install @nguniversal/express-engine@11.2.1 --save
已经默认带静态页面了
背景:使用Angular Universal进行预渲染,但不会渲染所有路由(大部分为查询参数或仅认证页面),所以想根据需要回退到快速渲染器。
快速复制(bash):
npm install -g @angular/cli@next
ng new partial-prerender -s -t --minimal --routing --interactive=false
cd partial-prerender/
ng add @nguniversal/express-engine@'^9.0.0-rc.1'
ng g m child --route child --module app
cat << 'EOF' > src/app/app.component.ts
import { Component } from '@angular/core';
@Component({
template: `<div [routerLink]="['/']">Root</div><div [routerLink]="['child']">Child</div><router-outlet></router-outlet>`,
})
export class AppComponent {}
EOF
npm run prerender
npm run serve:ssr
此快速复制将生成应用程序、通用实施、子页面,并替换应用程序 html 以提供 2 个链接和一个路由器插座,然后 build/pre-render。两条路线都将被预渲染,但这足以讨论这个问题。
问题: 执行动态 SSR,因为 Express 服务器将接收请求而不是提供预呈现的静态文件。 URL 通常在没有指定 /index.html
的情况下被访问。
请注意,可以在 /dist/partial-prerender/browser/index.html
和 .../child/index.html
中找到静态文件。为了进行测试,我已将这些文件的内容替换为垃圾文件,以确保一目了然地确定正在加载哪些文件。
也可以加一个console.log('DYNAMIC');
到server.ts
:
server.get('*', (req, res) => {
console.log('DYNAMIC');
res.render(indexHtml, { req, providers: [{ provide: APP_BASE_HREF, useValue: req.baseUrl }] });
});
当向 localhost:4000
或 localhost:4000/child
发出请求时,将打印 'DYNAMIC' 并生成动态渲染版本,而不是给我损坏的预渲染文件。
向localhost:4000/index.html
或localhost:4000/child/index.html
发出请求时,
server.get('*.*', express.static(distFolder, { maxAge: '1y' }));
获取并提供损坏的文件。
一切都是有道理的,为什么会这样,但我希望能够只点击给定的 url(没有 /index.html
并接收预渲染文件(如果可用),然后回过头来让 SSR 发挥作用。
可能的解决方案:
修改 server.ts
以测试与给定请求路径匹配的文件是否存在 + /index.html
并为它们提供服务,回落到 res.render(...
- 这是最好的方法吗?
- 如果是这样,为什么这不是默认功能?我唯一的猜测是您可以灵活地使用反向代理执行此操作,同时不会增加检查开销。
- 最好的方法是什么?
- 大概 6 年没有大量使用 Express,但感觉
express.static
应该以某种方式比fs
- 如果
fs
是答案,将预渲染文件缓存在内存中是否有意义?
- 大概 6 年没有大量使用 Express,但感觉
如果有帮助,我会生成一个 alpine-node 容器并使用 Nginx 入口部署到 K8s。仅提及这一点,因为可能有一个神奇的类似 try-files
的功能,可以完成 'attempt' 文件 + /index.html
从节点容器检索,然后在没有 /index.html
的情况下回退,但似乎极不可能。
您可以像这样在 get 请求中使用 if 语句
const fullPath = join(distFolder, req.originalUrl);
if (existsSync(fullPath)) {
console.log('STATIC Exists');
return res.sendFile(join(distFolder, req.originalUrl));
} else {
//Dynamic
res.render(indexHtml, { req, providers: [{ provide: APP_BASE_HREF, useValue: req.baseUrl }] });
}
npm install @nguniversal/express-engine@11.2.1 --save
已经默认带静态页面了