使用Angular Universal输出JSON-LD,谷歌JSON-LD如何使用Angular Universal添加

2019-09-17
0评论
/
1198阅读
爱搜啊

前言

本文和使用Angular Universal生成JSON-LD的指南面向希望生成表示Linked Data对象的JSON并在服务器上生成的HTML中正确注入它的开发人员。我们将重用和解释一些TransferState键值缓存服务用于将服务器状态传输到客户端应用程序的技术。

使用Angular Universal输出JSON-LD

首先什么是JSON-LD?

JSON-LD是一种轻量级的关联数据格式。人和机器很容易读写。它基于已经成功的JSON格式,并提供了一种帮助JSON数据在Web规模上进行互操作的方法。

关联数据使人们能够在Web上发布和使用信息。它是一种跨网站创建基于标准的机器可读数据网络的方法。它允许应用程序从一个关联数据开始,并遵循嵌入在Web上不同站点上托管的其他链接数据的嵌入式链接。

JSON-LD和关联数据的 这两个定义仅来自JSON-LD的网站。本文没有深入介绍JSON-LD的完整规范,也没有教您如何正确生成链接数据结构。JSON-ld网站的工具,例如游乐场,为您提供验证链接数据结构所需的一切。

一个基本的例子

您在下面看到的是注入HTML的JSON-LD数据对象的典型示例。它LocalBusiness 通过遵循其定义并提供识别实体的属性来描述a 。例如,给出了地址,地理位置,开放时间等。正如您所看到的,地址具有自己的类型PostalAddress。大多数类型都可以指定外部链接,这就是数据在同一网站或资源甚至其他网站上交织的方式。

<script type="application/ld+json">{

 "@context": "http://schema.org",

 "@type": "LocalBusiness",

 "address": {

   "@type": "PostalAddress",

   "addressLocality": "Manhattan",

   "addressRegion": "NY",

   "postalCode":"10036",

   "streetAddress": "400 Broadway"

 },

 "description": "This is your business description.",

 "name": "Craig's Car Repair",

 "telephone": "555-111-2345",

 "openingHours": "Mo,Tu,We,Th,Fr 09:00-17:00",

 "geo": {

   "@type": "GeoCoordinates",

   "latitude": "20.75",

   "longitude": "13.98"

 },

 "sameAs" : [ "http://www.facebook.com/example",

   "http://www.twitter.com/example",

   "http://plus.google.com/example"]}</script>

你为什么要关心JSON-LD?

你应该关心JSON-LD有几个原因:

它比Microdata更容易生成,更结构化,更易于阅读

它链接或允许您的数据与万维网上的其他实体/数据主题链接

一般来说; 它通过为您在页面上显示的内容提供上下文并使该上下文具有机器可读性,从而使搜索引擎和 Web上的其他爬虫更容易解析,从而为HTML标记提供更多语义价值。

生成JSON链接数据结构

在博客或不太复杂的网站的简单情况下,生成您的JSON关联数据可以使用您用于为社交共享和SEO设置元标记的相同基础数据来完成。您最关心的问题是建立正确的结构。您的结构外观完全取决于您的用例。作为一个基本的例子,我们将使用这个网站,更具体地说是关于页面。

使用路由器,我们首先定义与我们的路线相关的正确的SEO数据。就像我们之前做的那样,设置正确的社交共享元标记。

RouterModule.forChild([{

   path: '',

   component: AboutComponent,

   data: {

     seo: {

       title: `About Sam - ${environment.seo.title}`,

       description: `I'm a 30 year old software engineer living in Belgium.`,

       shareImg: '/assets/share/about.png',

     }

   }}])

使用服务,我们可以订阅路由更改以提取此数据并将数据传递给服务以解析JSON-LD对象。

@Injectable({

   providedIn: 'root',})export class RouteHelper {

 constructor(

   private readonly router: Router,

   private readonly activatedRoute: ActivatedRoute,

   private readonly jsonLdService: JsonLdService

 ) {

   this.setupRouting();

 }

 private setupRouting() {

   this.router.events.pipe(

     filter(event => event instanceof NavigationEnd),

     map(() => this.activatedRoute),

     map(route => {

       while (route.firstChild) {

         route = route.firstChild;

       }

       return route;

     }),

     filter(route => route.outlet === 'primary')

   ).subscribe((route: ActivatedRoute) => {

     const seo = route.snapshot.data['seo'];

     // generate your JSON-LD object here

     const jsonLd = {

       name: seo.title,

       url: environment.url + this.router.routerState.snapshot.url,

     };

     this.jsonLdService.setData('Website', jsonLd);

   });

 }}

将JsonLdService上述注入的是,高速缓存和更新,我们需要输出的数据结构简单的服务。此服务的基本实现如下所示,您作为开发人员仍负责正确构建对象。

@Injectable({

 providedIn: 'root',})export class JsonLdService {

 private jsonLd: any = {};

 setData(type: string, rawData: any) {

   this.jsonLd = this.getObject(type, rawData);

 }

 getObject(type: string, rawData?: any) {

   let object = {

     '@context': 'http://schema.org',

     '@type': type,

   };

   if (rawData) {

     object = Object.assign({}, object, rawData);

   }

   return object;

 }

 toJson() {

   return JSON.stringify(this.jsonLd);

 }}

使用上面解释的方法,我们当前在内存中的JSON-LD数据对象将如下所示:

json输出

{

   "@context": "http://schema.org",

   "@type": "Website",

   "name": "About Sam - Sam Vloeberghs - Freelance Webdeveloper & Software Engineer",

   "url": "https://samvloeberghs.be/about"}

这个基本的例子是我们想要实现的。下一步是在静态HTML中输出此对象。

在静态HTML中注入JSON

从Angular源代码中获取灵感

只有在服务器上生成HTML时,才真正需要在DOM中注入JSON-LD数据对象。为了得到这个结果,我BrowserTransferStateModule通过@angular/platform-browser 模块调查了暴露。

Essentialy这个模块,更具体地说,TransferState它提供的服务,做了我们想要实现的同样的事情。它将数据(例如HTTP调用的结果)缓存在对象中,并在应用程序生命周期中将其保存在内存中。除了HTTP调用之外,这正是我们JsonLdService 所做的。

服务器计数器部分,ServerTransferStateModule 通过它公开@angular/platform-server,序列化数据并将其注入DOM,然后在服务器上生成HTML并通过网络将其发送回浏览器。同样,这正是我们想要实现的目标。这是通过为BEFORE_APP_SERIALIZED 令牌提供额外的工厂功能来实现的。在应用程序稳定后,所有附加到其BEFORE_APP_SERIALIZED 上的工厂都会被执行。

export function serializeTransferStateFactory(

 doc: Document, appId: string, transferStore: TransferState) {

 return () => {

   const script = doc.createElement('script');

   script.id = appId + '-state';

   script.setAttribute('type', 'application/json');

   script.textContent = escapeHtml(transferStore.toJson());

   doc.body.appendChild(script);

 };}@NgModule({

 providers: [

   TransferState,

   {

     provide: BEFORE_APP_SERIALIZED,

     useFactory: serializeTransferStateFactory,

     deps: [DOCUMENT, APP_ID, TransferState],

     multi: true,

   }

 ]})export class ServerTransferStateModule {}

当Angular应用程序在浏览器中引导时,该TransferState服务将在静态HTML中获取序列化状态并对其进行反序列化,使其可用作直接缓存。这样我们就可以避免应用程序向服务器发出类似的相同数据请求,服务器端呈现的版本已经对其进行了调用。我们不需要这部分,但很高兴知道。

export function initTransferState(

 doc: Document, appId: string) {

 const script = doc.getElementById(appId + '-state');

 let initialState = {};

 if (script && script.textContent) {

   try {

     initialState = JSON.parse(unescapeHtml(script.textContent));

   } catch (e) {

     console.warn('Exception while restoring TransferState for app ' + appId, e);

   }

 }

 return TransferState.init(initialState);}@NgModule({

 providers: [{

   provide: TransferState,

   useFactory: initTransferState,

   deps: [DOCUMENT, APP_ID]

 }s],})export class BrowserTransferStateModule {}

我们简单的BrowserJsonLdModule和ServerJsonLdModule

遵循我们从中学到的概念和想法ServerTransferStateModule,BrowserTransferStateModule 我们创造了自己的ServerJsonLdModule和BrowserJsonLdModule。唯一的小差别仅限于更改<script>元素的类型并将其注入标记<head> 之前而不是</body>标记之前。我们也不需要从静态HTML中获取任何状态,就像在BrowserTransferStateModule。

import { NgModule } from '@angular/core';import { JsonLdService } from 'ngx-seo/json-ld.service';@NgModule({

 providers: [

   JsonLdService,

 ]})export class BrowserJsonLdModule {}

ngx-seo / json-ld.server.module.ts

import { NgModule } from '@angular/core';import { DOCUMENT } from '@angular/common';import { BEFORE_APP_SERIALIZED } from '@angular/platform-server';import { JsonLdService } from 'ngx-seo/json-ld.service';export function serializeJsonLdFactory(doc: Document, jsonLdService: JsonLdService) {

 const serializeAndInject = function () {

   const script = doc.createElement('script');

   script.setAttribute('type', 'application/ld+json');

   script.textContent = jsonLdService.toJson();

   doc.head.appendChild(script);

 };

 return serializeAndInject;}@NgModule({

 providers: [

   JsonLdService, {

   provide: BEFORE_APP_SERIALIZED,

     useFactory: serializeJsonLdFactory,

     deps: [DOCUMENT, JsonLdService],

     multi: true,

   },

 ],})export class ServerJsonLdModule {}

区分服务器上的执行

为了区分我们的应用程序的执行服务器上对浏览器上,在角通用,我们通常会创建一个新的服务器模块app.server.module.ts旁边app.module.ts,将直接导入AppModule 由出口app.module.ts。此版本的应用程序用于在服务器端配置的HTML呈现器,在大多数情况下用作呈现引擎。

如果我们回到TransferState服务的例子,我们看到 app.server.module.ts正在导入ServerTransferStateModule。这将覆盖JsonLdService导入的提供程序app.module.ts并提供额外的序列化功能,旁边也提供TransferState服务。

@NgModule({

 declarations: [

   AppComponent,

 ],

 imports: [

   BrowserModule.withServerTransition({appId: 'samvloeberghs'}),

   BrowserTransferStateModule,

   BrowserJsonLdModule,

   CommonModule,

   AppRoutingModule

 ],

 exports: [

   AppComponent,

 ],

 bootstrap: [AppComponent],})export class AppModule {}

app.server.module.ts

@NgModule({

 imports: [

   AppModule,

   ServerModule,

   ModuleMapLoaderModule,

   ServerTransferStateModule,

   ServerJsonLdModule,

 ],

 bootstrap: [AppComponent],})export class AppServerModule {}

我们做同样的事情与我们BrowserJsonLdModule和ServerJsonLdModule。必须导入浏览器模块,app.module.ts同时必须导入服务器模块app.server.module.ts。

结论

使用Angular Universal生成JSON-LD非常简单。借用Angular源代码中的概念,我们可以创建自己的JsonLd模块和服务,使我们能够在服务器上静态生成的HTML中输出JSON-LD。这段代码的结果可以在这个网站上找到,只需查看源代码并进行硬刷新。你可能会问为什么要刷新?由于服务工作者将默认缓存index.html 为app shell。


本站附件分享,如果附件失效,可以去找找看

诚通网盘附件百度网盘附件


于2019-09-17发布