在Angular SSR中,标题和元标签无法呈现在服务器上。

8
我使用Angular 6开发了一个网站。默认情况下,Angular不太友好于SEO,因此为了解决这个问题,我实现了Angular-Universal或Angular SSR(服务器端渲染)的方式。我更新了代码,并比较了之前和现在的页面源代码,我可以看到我的应用程序位于<app-root></app-root>标签内,在此之前只有“loading…”。

我使用@angular/platform-browser中的MetaServiceTitleService来更新Facebook和Twitter的所需<meta>标签以及<title>标签。
问题是,当我在本地系统上运行节点服务器时,查看源代码会显示已呈现的meta标签,但是当我在AWS VM上运行相同的节点服务器代码时,我没有得到已呈现的meta标签,但其他应用程序代码可用。
更新:添加meta标签的函数。
updateMetaTags(egElement: Elements[]) {
    this.url = 'https://example.com/eg/' + this.id;
    const title = egElement[1].innerHTML;
    this.tweetText = 'Check the latest blog on \"' + title + '\"';
    this.meta.addTags([
      { property: 'og:url', content: this.url },
      { property: 'og:type', content: 'website' },
      { property: 'og:title', content: title },
      { property: 'og:description', content: 'Author: ' + egElement[2].innerHTML },
      { property: 'og:image', content: this.egElement[3].img }
    ]);
  }

我在ngOnInit()中调用了这个函数。在我的本地机器上可以正确呈现,但在服务器上无法呈现。

egElementid是从后端服务调用返回的,且meta服务已被导入并注入到构造函数中。


你能展示生成元标签的代码吗? - Poul Kruijt
@PierreDuc 我已经更新了问题,请查看。 - inthevortex
调用ngOnInit时有任何条件吗?您能提供到该updateMetaTags方法被调用的代码吗? - Sepanyol
@dcballs 这个XHR获取页面的数据。如果你能深入了解,我们可以在聊天中继续讨论吗? - inthevortex
1
@inthevortex问题解决了吗? - Krsna Kishore
显示剩余7条评论
2个回答

4
如果您正在使用自定义的XHR调用,例如没有使用Angular HttpClient,则SSR不会等待API调用响应(如果使用第三方库检索API数据也可能发生这种情况)。从您的网站看,除了页面布局/页眉/页脚之外,没有进行服务器端渲染。我猜测这与SSR中未检索到API数据有关。也许您可以在问题中更新一些相关信息?有一个经过充分测试和维护的名为“ngx-meta”的库,该库是通用的(SSR)兼容。您可以查看他们的实现和演示,或尝试使用他们的库 https://github.com/fulls1z3/ngx-meta

1
我正在使用Angular HttpClient。 - inthevortex
该服务正在从后端API获取数据。是的,我正在呈现页面,并希望呈现与之相关的元标记。但是这些元标记没有被呈现出来。 - inthevortex
2
API 可以使用任何后端。您可能需要添加路由解析器,并在解析器中应用元标记。这意味着服务器端渲染将等待标记添加完成后才会继续和完成渲染过程。这可能值得一试。我关闭了 JS 并尝试访问您的网站,发现页面主体未能渲染,因此引起了我的注意(虽然您可能已经知道了)。 - Drenai
SSR(服务器端渲染)由于其中一个包抛出的错误而遭遇失败,我正在尝试绕过此问题。我一定会尝试这种方法并且通知您。 - inthevortex
有更新吗?我在ngOnInit中的可观察订阅中设置了元标记,但在使用Angular SSR部署时它们没有被设置。 - raberana
我也在使用ngx-meta,但它不支持SSR。如果您有任何建议,请告诉我。 - ROKIKOKI

2
最初的回答:嗨,我也遇到了这个错误,所以请确保在您的 server.ts 文件中导入 import 'reflect-metadata'; 以反射所有元数据到 index.html。您可以查看我的 server.ts 配置文件。
import 'zone.js/dist/zone-node';
import 'reflect-metadata';

import { enableProdMode } from '@angular/core';
// Express Engine
import { ngExpressEngine } from '@nguniversal/express-engine';
// Import module map for lazy loading
import { provideModuleMap } from '@nguniversal/module-map-ngfactory-loader';

import * as express from 'express';
import { join } from 'path';
import { readFileSync } from 'fs';

// Faster server renders w/ Prod mode (dev mode never needed)
enableProdMode();

// Express server
const app = express();

const PORT = process.env.PORT || 4000;
const DIST_FOLDER = join(process.cwd(), 'dist/browser');

const template = readFileSync(join(DIST_FOLDER, 'index.html')).toString();

const domino = require('domino');
const win = domino.createWindow(template);
global['localStorage'] = win.localStorage;
global['window'] = win;
global['document'] = win.document;
global['Document'] = win.document;
global['DOMTokenList'] = win.DOMTokenList;
global['Node'] = win.Node;
global['Text'] = win.Text;
global['HTMLElement'] = win.HTMLElement;
global['navigator'] = win.navigator;
global['MutationObserver'] = getMockMutationObserver();

function getMockMutationObserver() {
  return class {
    observe(node, options) {}

    disconnect() {}

    takeRecords() {
      return [];
    }
  };
}

// * NOTE :: leave this as require() since this file is built Dynamically from webpack
const { AppServerModuleNgFactory, LAZY_MODULE_MAP } = require('./dist/server/main');

// Our Universal express-engine (found @ https://github.com/angular/universal/tree/master/modules/express-engine)
app.engine(
  'html',
  ngExpressEngine({
    bootstrap: AppServerModuleNgFactory,
    providers: [provideModuleMap(LAZY_MODULE_MAP)],
  }),
);

app.set('view engine', 'html');
app.set('views', DIST_FOLDER);

// Example Express Rest API endpoints
// app.get('/api/**', (req, res) => { });
// Serve static files from /browser
app.get(
  '*.*',
  express.static(DIST_FOLDER, {
    maxAge: '1y',
  }),
);

// All regular routes use the Universal engine
app.get('*', (req, res) => {
  res.render('index', { req });
});

// Start up the Node server
app.listen(PORT, () => {
  console.log(`Node Express server listening on http://localhost:${PORT}`);
});

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接