From 1ba06d3ff3d248a0e56062b33cf08c42a209fd15 Mon Sep 17 00:00:00 2001 From: biss Date: Sun, 19 Jul 2026 12:06:35 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BC=98=E5=8C=96SEO?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 10 +++-- astro.config.mjs | 2 +- package.json | 3 +- public/manifest.webmanifest | 4 +- src/components/SeoHead.astro | 71 ++++++++++++++++++++++++++++++++ src/data/articles.generated.json | 2 +- src/data/site.ts | 10 ++++- src/layouts/BaseLayout.astro | 14 +++++-- src/pages/anniversary.astro | 17 +++++--- src/pages/articles.astro | 5 ++- src/pages/exam.astro | 7 +++- src/pages/exam/result.astro | 6 ++- src/pages/gallery.astro | 2 +- src/pages/gallery/[slug].astro | 6 ++- src/pages/index.astro | 2 +- src/pages/map.astro | 5 ++- src/pages/messages.astro | 2 +- src/pages/offline.astro | 6 ++- src/pages/people.astro | 2 +- src/pages/people/[slug].astro | 6 ++- src/pages/robots.txt.ts | 19 +++++++++ src/pages/search.astro | 6 ++- src/pages/sitemap.xml.ts | 49 ++++++++++++++++++++++ src/pages/timeline.astro | 2 +- 24 files changed, 224 insertions(+), 34 deletions(-) create mode 100644 src/components/SeoHead.astro create mode 100644 src/pages/robots.txt.ts create mode 100644 src/pages/sitemap.xml.ts diff --git a/README.md b/README.md index 86715b1..272a933 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,8 @@ - 留言墙:接入 Twikoo 后可在页面中留言。 - 全站搜索:通过 Typesense 搜索并筛选时间线、照片、文章和同学公开资料。 - PWA:支持添加到主屏幕,并离线访问站点外壳及已浏览的同源内容。 -- 字体加密:生产构建会随机混淆页面中的汉字,并生成按需裁剪的 WOFF2 字体,降低静态内容被直接复制或抓取的风险。 +- SEO:默认生产构建保留可读正文,并生成 canonical、结构化数据、`robots.txt` 与站点地图,便于搜索引擎抓取。 +- 可选字体保护:需要降低静态内容被直接复制的风险时,可单独执行受保护构建。 ## 技术栈 @@ -34,14 +35,15 @@ npm run dev ```bash npm run dev # 启动开发服务器 -npm run build # 构建静态站点到 dist +npm run build # 构建适合搜索引擎抓取的静态站点到 dist +npm run build:protected # 可选:构建带汉字字体混淆的版本 npm run preview # 预览构建结果 npm run search:sync # 将公开内容同步到 Typesense ``` -`npm run build` 会在 Astro 构建完成后加密页面字体,再生成带内容版本号的 Service Worker,并预缓存页面、样式、脚本、加密字体、应用图标和首页主视觉。照片等大体积资源采用访问后缓存,第三方接口始终走网络。 +`npm run build` 会在 Astro 构建完成后生成带内容版本号的 Service Worker,并预缓存页面、样式、脚本、应用图标和首页主视觉。照片等大体积资源采用访问后缓存,第三方接口始终走网络。 -字体加密只作用于 `dist` 中的生产页面,不修改源码,也不会影响本地开发。每次构建默认生成新的随机映射;如需可复现产物,可设置 `FONT_ENCRYPTION_SEED`。`code`、`pre`、表单属性以及脚本动态生成的内容会保留原文,以免影响复制和交互。字体加密只能提高批量采集成本,不能替代访问控制或版权保护。 +`npm run build:protected` 会额外混淆 `dist` 页面中的汉字并生成按需裁剪的 WOFF2 字体。该模式会降低搜索引擎理解正文的能力,不适合需要 SEO 的正式部署。字体混淆不修改源码,也不能替代访问控制或版权保护。 ## Typesense 全站搜索 diff --git a/astro.config.mjs b/astro.config.mjs index fc54891..e14da29 100644 --- a/astro.config.mjs +++ b/astro.config.mjs @@ -2,7 +2,7 @@ import { defineConfig } from "astro/config"; import { fileURLToPath } from "node:url"; export default defineConfig({ - site: "https://example.com", + site: "https://www.2024612.xyz/", vite: { resolve: { alias: { diff --git a/package.json b/package.json index 6388b13..31c2e67 100644 --- a/package.json +++ b/package.json @@ -5,7 +5,8 @@ "type": "module", "scripts": { "dev": "astro dev", - "build": "astro build && node scripts/encrypt-font.mjs && node scripts/build-pwa.mjs", + "build": "astro build && node scripts/build-pwa.mjs", + "build:protected": "astro build && node scripts/encrypt-font.mjs && node scripts/build-pwa.mjs", "preview": "astro preview", "search:key": "node --env-file-if-exists=.env --env-file-if-exists=.env.local scripts/create-typesense-search-key.mjs", "search:sync": "node --env-file-if-exists=.env --env-file-if-exists=.env.local --import tsx scripts/sync-typesense.ts" diff --git a/public/manifest.webmanifest b/public/manifest.webmanifest index 550518b..8349d22 100644 --- a/public/manifest.webmanifest +++ b/public/manifest.webmanifest @@ -1,8 +1,8 @@ { "id": "/", - "name": "2024届612班纪念网站", + "name": "高平一中612班纪念网站", "short_name": "612班", - "description": "保存2024届612班共同走过的校园时光,也记录毕业后的我们。", + "description": "保存高平一中2024届612班共同走过的校园时光,也记录毕业后的我们。", "lang": "zh-CN", "start_url": "/", "scope": "/", diff --git a/src/components/SeoHead.astro b/src/components/SeoHead.astro new file mode 100644 index 0000000..0da19cf --- /dev/null +++ b/src/components/SeoHead.astro @@ -0,0 +1,71 @@ +--- +import { site } from "../data/site"; + +interface Props { + title?: string; + description?: string; + image?: string; + noindex?: boolean; +} + +const { + title = site.name, + description = site.description, + image = site.heroImage, + noindex = false +} = Astro.props; + +const documentTitle = title === site.name || title.includes(site.name) + ? title + : `${title} · ${site.name}`; +const baseUrl = Astro.site ?? new URL(site.url); +const canonicalUrl = new URL(Astro.url.pathname, baseUrl); +const imageUrl = new URL(image, baseUrl); +const robots = noindex + ? "noindex, nofollow" + : "index, follow, max-image-preview:large, max-snippet:-1, max-video-preview:-1"; +const structuredData = { + "@context": "https://schema.org", + "@graph": [ + { + "@type": "WebSite", + "@id": `${new URL("/", baseUrl)}#website`, + url: new URL("/", baseUrl).toString(), + name: site.name, + alternateName: [site.shortName, `${site.cohort}${site.shortName}`], + description: site.description, + inLanguage: "zh-CN" + }, + { + "@type": "WebPage", + "@id": `${canonicalUrl}#webpage`, + url: canonicalUrl.toString(), + name: documentTitle, + description, + isPartOf: { "@id": `${new URL("/", baseUrl)}#website` }, + inLanguage: "zh-CN" + } + ] +}; +--- + +{documentTitle} + + + + + + + + + + + + + + + + + + +