angular - 使用 Angular 通用开发 :ssr works but build:ssr is not working 设置页面元标记

标签 angular seo server-side-rendering meta-tags angular-universal

我在使用快速服务器构建和提供 Angular Universal 应用程序以获得良好的 SEO 方面遇到了挑战。
当我使用带有命令 "dev:ssr": "ng run motif:serve-ssr" 的脚本为其提供服务时并在浏览器上访问它的默认端口 4200并使用 Chrome 的查看页面源代码选项查看源代码,我可以看到正确的动态元标记以及 HTML 源代码,效果非常好。
但是当我使用以下命令构建和提供它时,元标记没有被更新

"build:ssr": "ng build --prod && ng run motif:server:production"

npm run build:ssr && npm run serve:ssr
它构建和呈现没有错误,但是当我在 Chrome 上查看页面源代码时,我只看到为 index.html 设置的元标记文件,但我希望它为每个文章数据设置标签,如页面标题、描述、图像等。
我正在路由器数据解析器服务中设置元标记,它工作正常,因为我可以在运行 dev:ssr 脚本时看到它的工作。
CLI 版本是 9.1.12(v10 和 11v 有 ssr 和 window 对象的问题)。我是否遗漏了什么或做错了什么?
我很感激对此问题的任何建议和解决方案。
更新:
数据解析器的简化版本如下:
import { Injectable } from '@angular/core';
import { AngularFirestore } from '@angular/fire/firestore';
import { DomSanitizer, TransferState } from '@angular/platform-browser';
import { ActivatedRouteSnapshot, Resolve, RouterStateSnapshot } from '@angular/router';
import { SeoSocialShareData, SeoSocialShareService } from 'ngx-seo';
import { Observable, of, Subscription } from 'rxjs';
import { map } from 'rxjs/operators';

@Injectable({ providedIn: 'root' })
export class ArticleDataResolver implements Resolve<any> {
  constructor(
    private seoSocialShareService: SeoSocialShareService,
    private angularFirestore: AngularFirestore,
  ) { }

  resolve(
    route: ActivatedRouteSnapshot,
    state: RouterStateSnapshot
  ): Observable<any> | Promise<any> | any {
    const stateKey = state.url;

    const ref = this.angularFirestore.collection("movementArticles");

    return ref
      .doc(route.params.id)
      .get()
      .pipe(
        map((dataSnap) => {
          const seoData: SeoSocialShareData = {
            title: '...',
            description: '...',
            image: '...',
            author: '...',
            keywords: `...`,
            url: `...`,
            published: '...',
          };

          this.seoSocialShareService.setData(seoData);

          return dataSnap;
        })
      );
  }
}
 

最佳答案

我与您分享这项服务
如果您不需要翻译,可以省略翻译服务
并尝试调用 中的方法构造函数 ngOnInit 你的组件 View

import { Injectable } from '@angular/core';
import { Title, Meta } from '@angular/platform-browser';
import { TranslateService } from '@ngx-translate/core';
import { Observable } from 'rxjs';
import { map } from 'rxjs/operators';

@Injectable({
  providedIn: 'root'
})
export class SEOService {
  constructor(private title: Title, private meta: Meta, private translate: TranslateService) {}

  async getTranslation(word: string) {
    if (word) {
      return await this.translate
        .get(word)
        .toPromise()
        .then(resp => {
          return resp;
        });
    } else {
      return '';
    }
  }

  async updateTitle(title: string) {
    const translation = await this.getTranslation(title);
    this.title.setTitle(translation);
  }

  async updateDescription(desc: string) {
    const translation = await this.getTranslation(desc);
    this.meta.updateTag({ name: 'description', content: translation });
  }

  async updateKeywords(keywords: string) {
    const translation = await this.getTranslation(keywords);
    this.meta.updateTag({ name: 'keywords', content: translation });
  }

  async updateOgUrl(url: string) {
    const translation = await this.getTranslation(url);
    this.meta.updateTag({
      name: 'og:url',
      property: 'og:url',
      content: translation
    });
  }

  async updateOgTitle(ogTitle: string) {
    const translation = await this.getTranslation(ogTitle);
    this.meta.updateTag({
      name: 'og:title',
      property: 'og:title',
      content: translation
    });
  }

  async updateOgDescription(ogDesc: string) {
    const translation = await this.getTranslation(ogDesc);
    this.meta.updateTag({
      name: 'og:description',
      property: 'og:description',
      content: translation
    });
  }

  async updateOgImage(ogImg: string) {
    const translation = await this.getTranslation(ogImg);
    this.meta.updateTag({
      name: 'og:image',
      property: 'og:image',
      content: translation
    });
  }

  async disableFollow() {
    this.meta.addTag({
      name: 'robots',
      property: 'robots',
      content: 'noindex, nofollow'
    });
  }

  async enableFollow() {
    this.meta.removeTag('robots');
  }
}
全局 ngOnInit 示例

import { Component, OnInit } from '@angular/core';
import {
  Router,
  NavigationEnd,
  ActivatedRoute,
  RouterEvent
} from '@angular/router';

export class AppComponent implements OnInit {

  constructor(
    private router: Router,
    private route: ActivatedRoute,
    private seoService: SEOService,
  ) {
   
  }

ngOnInit() {
    this.router.events
      .pipe(
        filter(event => event instanceof NavigationEnd),
        map(() => this.route),
        map(route => {
          while (route.firstChild) {
            route = route.firstChild;
          }
          return route;
        }),
        filter(route => route.outlet === 'primary'),
        mergeMap(route => route.data)
      )
      .subscribe(event => {
        this.seoService.updateTitle(event['title']);
        this.seoService.updateDescription(event['description']);
        this.seoService.updateKeywords(event['keywords']);
        this.seoService.updateOgUrl(event['url']);
        this.seoService.updateOgTitle(event['ogTitle']);
        this.seoService.updateOgDescription(event['ogDesc']);
        this.seoService.updateOgImage(event['ogImage']);
      });
  }
}

希望这可以帮到你 ;)

关于angular - 使用 Angular 通用开发 :ssr works but build:ssr is not working 设置页面元标记,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/66925705/

相关文章:

javascript - 仅当另一个可观察对象在过去两秒内未发出时才从可观察对象发出

.htaccess - SEO 友好的 URL 错误,请帮帮我?

node.js - 为什么通过刷新浏览器上的 localhost 页面,bundle_client 会卡在挂起状态?

reactjs - Gatsby React-helmet 不是服务器端渲染

Javascript - 打印从 API 返回的 Blob 内容

javascript - 如何通过在angular7中的模态组件外部单击来隐藏ngx-simple-modal?

javascript - 如何在 Angular 可见时刷新组件

html - 我可以标明我网站每个页面的语言,以便 Google 不会向用户显示外国内容吗?

seo - Noindex 页面应该出现在 Google 附加链接中

javascript - 服务器端在 React 中使用 Next.js 加载全局组件