feat(seo): 全站 SEO 优化,添加 canonical/OG/Twitter/结构化数据及自动生成 sitemap 和 robots

This commit is contained in:
2026-06-15 15:53:23 +08:00
parent 6feb246fde
commit 5e9ea45730
6 changed files with 168 additions and 10 deletions

View File

@@ -1,16 +1,67 @@
import { defineConfig } from 'vitepress'
import fs from 'node:fs'
import path from 'node:path'
import type { HeadConfig, SiteConfig, TransformContext } from 'vitepress'
const siteUrl = 'https://seotest.gongxue100.com'
const siteName = '恭学教育集团'
const defaultOgImage = 'https://fenxiang-1304851894.cos.ap-nanjing.myqcloud.com/%E5%B0%81%E9%9D%A2.png'
const defaultKeywords = '天津专升本,专升本培训,恭学教育,天津专升本刷题,专升本网校,天津专升本教材,专升本辅导,天津专升本报名,专升本真题,天津专升本课程'
/**
* 将 Markdown 相对路径转换为站点 URL 路径
* - index.md -> /
* - books.md -> /books
* - knowledge/index.md -> /knowledge/
* - knowledge/sample-article.md -> /knowledge/sample-article
*/
function relativePathToUrl(relativePath: string): string {
let route = relativePath.replace(/\.md$/, '')
route = route.replace(/(^|\/)index$/, '')
if (!route.startsWith('/')) {
route = `/${route}`
}
return route || '/'
}
function formatDate(date: Date): string {
return date.toISOString().split('T')[0]
}
function getPageLastmod(relativePath: string): string | undefined {
const filePath = path.resolve('./docs', relativePath)
try {
const content = fs.readFileSync(filePath, 'utf-8')
const fmMatch = content.match(/^---\s*\n([\s\S]*?)\n---/)
if (fmMatch) {
const dateMatch = fmMatch[1].match(/^publishedAt:\s*(.+)$/m) || fmMatch[1].match(/^date:\s*(.+)$/m)
if (dateMatch) {
const d = new Date(dateMatch[1].trim())
if (!isNaN(d.getTime())) {
return formatDate(d)
}
}
}
const stat = fs.statSync(filePath)
return formatDate(stat.mtime)
} catch {
return undefined
}
}
export default defineConfig({
cleanUrls: 'with-subfolders',
title: '恭学教育集团',
titleTemplate: false,
description: '恭学教育集团是天津专升本培训领军品牌专注天津专升本7年已服务21万+学员。提供专业刷题系统、在线网校、权威教材和一对一升学规划真题覆盖率100%,助你轻松升本成功!',
lang: 'zh-CN',
head: [
['link', { rel: 'icon', href: '/logo.png' }],
['meta', { name: 'keywords', content: '天津专升本,专升本培训,恭学教育,天津专升本刷题,专升本网校,天津专升本教材,专升本辅导,天津专升本报名,专升本真题,天津专升本课程' }],
['meta', { property: 'og:image', content: defaultOgImage }],
['link', { rel: 'apple-touch-icon', href: '/logo.png' }],
['meta', { name: 'author', content: '恭学教育集团' }],
['meta', { property: 'og:site_name', content: siteName }],
['meta', { property: 'og:locale', content: 'zh_CN' }],
['meta', { name: 'twitter:card', content: 'summary_large_image' }],
],
themeConfig: {
logo: '/logo.png',
@@ -41,13 +92,115 @@ export default defineConfig({
},
],
},
transformHead({ pageData }) {
const keywords = pageData.frontmatter.keywords
if (keywords && Array.isArray(keywords)) {
return [
['meta', { name: 'keywords', content: keywords.join(',') }],
]
transformHead({ pageData, siteData }: TransformContext) {
const isNotFound = pageData.relativePath === '404.md'
const isArticle =
pageData.relativePath.startsWith('knowledge/') &&
pageData.relativePath !== 'knowledge/index.md' &&
pageData.frontmatter.publishedAt
const url = `${siteUrl}${relativePathToUrl(pageData.relativePath)}`
const title = pageData.title || siteName
const description = pageData.description || siteData.description || ''
const keywords = Array.isArray(pageData.frontmatter.keywords)
? pageData.frontmatter.keywords.join(',')
: defaultKeywords
const ogImage = pageData.frontmatter.coverImage || defaultOgImage
const tags: HeadConfig[] = [
['link', { rel: 'canonical', href: url }],
['meta', { name: 'robots', content: isNotFound ? 'noindex, nofollow' : 'index, follow' }],
['meta', { name: 'keywords', content: keywords }],
['meta', { property: 'og:title', content: title }],
['meta', { property: 'og:description', content: description }],
['meta', { property: 'og:url', content: url }],
['meta', { property: 'og:image', content: ogImage }],
['meta', { property: 'og:type', content: isArticle ? 'article' : 'website' }],
['meta', { name: 'twitter:title', content: title }],
['meta', { name: 'twitter:description', content: description }],
['meta', { name: 'twitter:image', content: ogImage }],
]
const organizationLd = {
'@context': 'https://schema.org',
'@type': 'Organization',
'@id': `${siteUrl}/#organization`,
name: siteName,
url: siteUrl,
logo: `${siteUrl}/logo.png`,
image: defaultOgImage,
description:
'恭学教育集团是天津专升本培训领军品牌,专注天津专升本培训、刷题系统、在线网校、权威教材和升学规划服务。',
sameAs: [],
}
return []
const ldScripts: HeadConfig[] = [
['script', { type: 'application/ld+json' }, JSON.stringify(organizationLd)],
]
if (isArticle) {
const fm = pageData.frontmatter
const articleLd = {
'@context': 'https://schema.org',
'@type': 'Article',
headline: title,
description,
url,
image: ogImage,
author: {
'@type': fm.author ? 'Person' : 'Organization',
name: fm.author || siteName,
},
publisher: {
'@id': `${siteUrl}/#organization`,
},
datePublished: fm.publishedAt,
dateModified: fm.publishedAt,
keywords: Array.isArray(fm.tags) ? fm.tags.join(',') : keywords,
mainEntityOfPage: {
'@type': 'WebPage',
'@id': url,
},
}
ldScripts.push(['script', { type: 'application/ld+json' }, JSON.stringify(articleLd)])
} else {
const webPageLd = {
'@context': 'https://schema.org',
'@type': 'WebPage',
name: title,
description,
url,
isPartOf: {
'@id': `${siteUrl}/#organization`,
},
}
ldScripts.push(['script', { type: 'application/ld+json' }, JSON.stringify(webPageLd)])
}
return [...tags, ...ldScripts]
},
buildEnd(siteConfig: SiteConfig) {
const pages = siteConfig.pages.filter((p) => p !== '404.md')
const urls = pages
.map((p) => ({
loc: `${siteUrl}${relativePathToUrl(p)}`,
lastmod: getPageLastmod(p),
changefreq: p === 'index.md' ? 'daily' : 'weekly',
priority: p === 'index.md' ? '1.0' : '0.8',
}))
.sort((a, b) => a.loc.localeCompare(b.loc))
const sitemap = `<?xml version="1.0" encoding="UTF-8"?>\n<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">\n${urls
.map(
(u) =>
` <url>\n <loc>${u.loc}</loc>\n${u.lastmod ? ` <lastmod>${u.lastmod}</lastmod>\n` : ''} <changefreq>${u.changefreq}</changefreq>\n <priority>${u.priority}</priority>\n </url>`,
)
.join('\n')}\n</urlset>\n`
fs.mkdirSync(siteConfig.outDir, { recursive: true })
fs.writeFileSync(path.join(siteConfig.outDir, 'sitemap.xml'), sitemap)
const robots = `User-agent: *\nAllow: /\nDisallow: /404\n\nSitemap: ${siteUrl}/sitemap.xml\n`
fs.writeFileSync(path.join(siteConfig.outDir, 'robots.txt'), robots)
},
})

View File

@@ -1,5 +1,6 @@
---
title: 页面未找到
title: 页面未找到 - 恭学教育集团
description: 抱歉,您访问的页面不存在。返回恭学教育集团官网,了解天津专升本培训课程、刷题系统、在线网校等升学服务。
---
<script setup>

View File

@@ -1,6 +1,7 @@
---
id: sample-article
title: 2026年天津高职升本科考试报名热点问答发布
description: 2026年天津高职升本科考试报名热点问答报考条件、报名时间节点、平行志愿填报策略等政策详解。
summary: 最新报名政策详解:应届生、往届生报考条件,报名时间节点,平行志愿填报策略等重要信息。
category: exam_policy
tags:

View File

@@ -1,6 +1,7 @@
---
id: study-time-plan
title: 天津专升本高效复习时间规划
description: 天津专升本高效复习时间规划:基础、强化、冲刺三阶段备考安排,科目复习建议与每日学习分配技巧。
summary: 科学安排备考时间,从基础巩固到冲刺提分,帮助考生制定清晰的专升本复习计划。
category: study_method
tags:

View File

@@ -1,5 +1,6 @@
---
title: 隐私政策 - 恭学教育集团
description: 恭学教育集团隐私政策说明我们如何收集、使用、存储和保护您的个人信息,以及您享有的相关权利。
---
<script setup>

View File

@@ -1,5 +1,6 @@
---
title: 使用条款 - 恭学教育集团
description: 恭学教育集团使用条款,包括网站服务、知识产权、用户责任、免责说明等内容,使用本网站前请仔细阅读。
---
<script setup>