添加 SEO 结构化数据

This commit is contained in:
Han 2025-02-20 19:04:58 +08:00
parent 126751ca79
commit 925b144426
4 changed files with 26 additions and 15 deletions

View File

@ -3,8 +3,14 @@ import SITE_CONFIG from "../config";
// 当前页面的 URL 元地址 // 当前页面的 URL 元地址
const canonicalURL = new URL(Astro.url.pathname, Astro.site); const canonicalURL = new URL(Astro.url.pathname, Astro.site);
// 页面内容的元数据 // 页面内容的元数据
const { Title: title, Keywords, Description } = Astro.props; const { Title, Keywords, Description, PageCover } = Astro.props;
const { Site, Title, Subtitle, Author, Cover, DNSOptimization } = SITE_CONFIG; const { Site, Title: SiteName, Subtitle, Author, Cover, DNSOptimization } = SITE_CONFIG;
const WebTitle = Title || SiteName;
const SiteCover = Site + Cover;
const WebCover = PageCover || SiteCover;
// Schema.org 结构化数据JSON-LD
const WebSiteData = { "@context": "https://schema.org", "@type": "WebSite", name: WebTitle, url: canonicalURL, image: { "@type": "ImageObject", url: SiteCover } };
const ArticleData = { "@context": "https://schema.org", "@type": "BlogPosting", mainEntityOfPage: { "@type": "WebPage", "@id": canonicalURL.href }, headline: WebTitle, image: [WebCover], author: { "@type": "Person", name: Author, url: Site }, publisher: { "@type": "Organization", name: SiteName, logo: { "@type": "ImageObject", url: SiteCover } } };
// 基础 样式 // 基础 样式
import "../styles/Base.less"; import "../styles/Base.less";
--- ---
@ -14,12 +20,12 @@ import "../styles/Base.less";
<meta http-equiv="X-UA-Compatible" content="ie=edge" /> <meta http-equiv="X-UA-Compatible" content="ie=edge" />
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<meta name="viewport" content="width=device-width,initial-scale=1,user-scalable=no" /> <meta name="viewport" content="width=device-width,initial-scale=1,user-scalable=no" />
<title>{title ? `${title} | ${Title}` : `${Title} - ${Subtitle}`}</title> <title>{Title ? `${Title} | ${SiteName}` : `${SiteName} - ${Subtitle}`}</title>
<meta name="keywords" content={(Keywords || ["Astro", "分享", "博客"]).join(", ")} /> <meta name="keywords" content={(Keywords || ["Astro", "分享", "博客"]).join(", ")} />
<meta name="description" content={Description} /> <meta name="description" content={Description} />
<meta name="format-detection" content="telephone=no, email=no, date=no, address=no" /> <meta name="format-detection" content="telephone=no, email=no, date=no, address=no" />
<!-- 元数据 --> <!-- 元数据 -->
<meta name="title" content={Title} /> <meta name="title" content={WebTitle} />
<meta name="site" content={Site} /> <meta name="site" content={Site} />
<meta name="author" content={Author} /> <meta name="author" content={Author} />
<meta name="generator" content={Astro.generator} /> <meta name="generator" content={Astro.generator} />
@ -27,18 +33,20 @@ import "../styles/Base.less";
<meta name="robots" content="max-image-preview:large" /> <meta name="robots" content="max-image-preview:large" />
<!-- Open Graph --> <!-- Open Graph -->
<meta property="og:type" content="website" /> <meta property="og:type" content="website" />
<meta property="og:title" content={Title} /> <meta property="og:title" content={WebTitle} />
<meta property="og:url" content={canonicalURL} /> <meta property="og:url" content={canonicalURL} />
<meta property="og:site_name" content={Title} /> <meta property="og:site_name" content={WebTitle} />
<meta property="og:description" content={Description} /> <meta property="og:description" content={Description} />
<meta property="og:locale" content="zh_CN" /> <meta property="og:locale" content="zh_CN" />
<meta property="og:image" content={Cover} /> <meta property="og:image" content={WebCover} />
<!-- Twitter --> <!-- Twitter -->
<meta property="twitter:card" content="summary_large_image" /> <meta property="twitter:card" content="summary_large_image" />
<meta property="twitter:url" content={canonicalURL} /> <meta property="twitter:url" content={canonicalURL} />
<meta property="twitter:title" content={Title} /> <meta property="twitter:title" content={WebTitle} />
<meta property="twitter:description" content={Description} /> <meta property="twitter:description" content={Description} />
<meta property="twitter:image" content={Cover} /> <meta property="twitter:image" content={WebCover} />
<!-- 输出结构化数据 -->
<script type="application/ld+json" set:html={JSON.stringify(PageCover ? ArticleData : WebSiteData)} is:inline />
<!-- Sitemap --> <!-- Sitemap -->
<link rel="sitemap" href="/sitemap-index.xml" /> <link rel="sitemap" href="/sitemap-index.xml" />
<!-- DNS预解析 --> <!-- DNS预解析 -->

View File

@ -1,6 +1,6 @@
--- ---
import { ClientRouter } from "astro:transitions"; import { ClientRouter } from "astro:transitions";
const { title, keywords, description, activeNav } = Astro.props; const { title, keywords, description, pagecover, activeNav } = Astro.props;
// Head 依赖 // Head 依赖
import Head from "../components/Head.astro"; import Head from "../components/Head.astro";
// 顶部 Header // 顶部 Header
@ -16,9 +16,8 @@ import "../styles/Layout.less";
--- ---
<html lang="zh-CN"> <html lang="zh-CN">
<Head Title={title} Keywords={keywords} Description={description}> <Head Title={title} Keywords={keywords} Description={description} PageCover={pagecover} />
<ClientRouter /> <ClientRouter />
</Head>
<body> <body>
<MobileSidebar activeNav={activeNav} /> <MobileSidebar activeNav={activeNav} />

View File

@ -10,6 +10,9 @@ export async function getStaticPaths() {
// 处理文章内容 // 处理文章内容
type Props = CollectionEntry<"blog">; type Props = CollectionEntry<"blog">;
const post: any = Astro.props; const post: any = Astro.props;
// 获取封面图
import getCover from "../../utils/getCover";
const ARTICLE_COVER: string = await getCover(post.data.cover);
// 页面 Info // 页面 Info
import SITE_CONFIG from "../../config"; import SITE_CONFIG from "../../config";
const { Site, Title, Author, Twikoo } = SITE_CONFIG; const { Site, Title, Author, Twikoo } = SITE_CONFIG;
@ -28,7 +31,7 @@ import Comment from "../../components/Comment.astro";
import "../../styles/Article.less"; import "../../styles/Article.less";
--- ---
<Layout title={post.data.title} keywords={post.data.tags} description={description}> <Layout title={post.data.title} keywords={post.data.tags} description={description} pagecover={ARTICLE_COVER}>
<section class="vh-container"> <section class="vh-container">
<article class="vh-animation vh-article-main"> <article class="vh-animation vh-article-main">
<header> <header>

View File

@ -2,6 +2,7 @@ import path from 'path';
import _fs from 'fs'; import _fs from 'fs';
const fs = _fs.promises; const fs = _fs.promises;
import { fileURLToPath } from 'url'; import { fileURLToPath } from 'url';
import SITE_INFO from '../config';
// 获取当前模块的目录路径 // 获取当前模块的目录路径
const __filename = fileURLToPath(import.meta.url); // 当前文件的绝对路径 const __filename = fileURLToPath(import.meta.url); // 当前文件的绝对路径
const __dirname = path.dirname(__filename); // 当前文件所在的目录 const __dirname = path.dirname(__filename); // 当前文件所在的目录
@ -61,7 +62,7 @@ const fileIter = createImageIterator(targetDir);
const getCover = async (filename: string | null | undefined) => { const getCover = async (filename: string | null | undefined) => {
if (filename) return filename; if (filename) return filename;
const { value } = await fileIter.next(); const { value } = await fileIter.next();
return `/assets/images/banner/${value}` return SITE_INFO.Site + `/assets/images/banner/${value}`
} }
export default getCover; export default getCover;