feat: add schemaOrg & resolve dead links by replace /index

This commit is contained in:
YunYouJun 2023-03-28 12:12:57 +08:00
parent 73d6e7af41
commit 66e216f7f5
13 changed files with 153 additions and 15 deletions

View File

@ -1,3 +1,6 @@
# DeadLinks Test
- [/pages/test/deadlinks/](/test/deadlinks/)
- [/pages/test/deadlinks/](/test/deadlinks/): Should be OK
- [/pages/about/index](/about/index): Should be OK
- [/pages/about/](/about/): Should be OK
- [/pages/about](/about): Should be OK

View File

@ -0,0 +1,16 @@
---
title: Schema.org for SEO
---
采用 [Schema.org](https://schema.org/) 标准,可以让搜索引擎更好地理解网站内容,从而提高网站在搜索结果中的排名。
基于 [@vueuse/schema-org](https://unhead-schema-org.harlanzw.com/) 实现。
- Identity 采用了 Person (Personal Website or Blog)
> [Person | @unhead/schema.org](https://unhead-schema-org.harlanzw.com/guide/guides/identity#person)
## Validators
- [Google 富媒体搜索结果测试](https://search.google.com/test/rich-results)
- [架构标记验证器](https://validator.schema.org/)

View File

@ -1,4 +1,5 @@
<script lang="ts" setup>
import { defineWebPage, useSchemaOrg } from '@vueuse/schema-org'
import { useFrontmatter, usePostTitle, useSiteStore } from 'valaxy'
import { useI18n } from 'vue-i18n'
@ -8,6 +9,12 @@ const frontmatter = useFrontmatter()
const title = usePostTitle(frontmatter)
const site = useSiteStore()
useSchemaOrg([
defineWebPage({
'@type': 'CollectionPage',
}),
])
</script>
<template>

View File

@ -3,6 +3,7 @@ import { computed } from 'vue'
import { useCategory, useFrontmatter, usePostTitle, useSiteStore } from 'valaxy'
import { useI18n } from 'vue-i18n'
import { useRoute } from 'vue-router'
import { defineWebPage, useSchemaOrg } from '@vueuse/schema-org'
const { t } = useI18n()
@ -29,6 +30,12 @@ const posts = computed(() => {
})
const title = usePostTitle(frontmatter)
useSchemaOrg([
defineWebPage({
'@type': 'CollectionPage',
}),
])
</script>
<template>

View File

@ -2,6 +2,8 @@
import { computed } from 'vue'
import { useFrontmatter, useFullUrl, useSiteConfig } from 'valaxy'
import { defineArticle, useSchemaOrg } from '@vueuse/schema-org'
const siteConfig = useSiteConfig()
const frontmatter = useFrontmatter()
const url = useFullUrl()
@ -12,6 +14,20 @@ const showSponsor = computed(() => {
return siteConfig.value.sponsor.enable
})
useSchemaOrg(
defineArticle({
'@type': 'BlogPosting',
'headline': frontmatter.value.title,
'description': frontmatter.value.description,
'author': [
{
name: siteConfig.value.author.name,
url: siteConfig.value.author.link,
},
],
}),
)
</script>
<template>

View File

@ -3,8 +3,15 @@ import { useFrontmatter, useInvisibleElement, usePostTitle, useSiteStore, useTag
import { useI18n } from 'vue-i18n'
import { computed, ref } from 'vue'
import { useRoute, useRouter } from 'vue-router'
import { defineWebPage, useSchemaOrg } from '@vueuse/schema-org'
import { useThemeConfig } from '../composables'
useSchemaOrg([
defineWebPage({
'@type': 'CollectionPage',
}),
])
const route = useRoute()
const router = useRouter()

View File

@ -7,6 +7,7 @@ import ValaxyUserApp from '/@valaxyjs/UserAppVue'
import ValaxyThemeApp from '/@valaxyjs/ThemeAppVue'
import pkg from 'valaxy/package.json'
import { useI18n } from 'vue-i18n'
import { definePerson, defineWebPage, defineWebSite, useSchemaOrg } from '@vueuse/schema-org'
import ValaxyAddons from './components/ValaxyAddons.vue'
import { isDark, useFrontmatter } from './composables'
@ -25,8 +26,9 @@ const fm = useFrontmatter()
const { locale } = useI18n()
const title = computed(() => fm.value[`title_${locale.value}`] || fm.value.title)
useHead({
title: computed(() => fm.value[`title_${locale.value}`] || fm.value.title),
title,
titleTemplate: computed(() => fm.value.titleTemplate || ((title: string) => title ? `${title} - ${siteConfig.value.title}` : siteConfig.value.title)),
link: [
{
@ -54,6 +56,7 @@ useHead({
// seo
// todo: get first image url from markdown
const siteUrl = computed(() => fm.value.url || siteConfig.value.url)
useSeoMeta({
description: computed(() => fm.value.excerpt || siteConfig.value.description),
ogDescription: computed(() => fm.value.excerpt || siteConfig.value.description),
@ -61,11 +64,29 @@ useSeoMeta({
ogSiteName: computed(() => siteConfig.value.title),
ogTitle: computed(() => fm.value.title || siteConfig.value.title),
ogImage: computed(() => siteConfig.value.favicon),
ogUrl: computed(() => fm.value.url || siteConfig.value.url),
ogUrl: siteUrl,
})
const onContentUpdated = ref()
provide('onContentUpdated', onContentUpdated)
// for SEO
useSchemaOrg([
// https://unhead-schema-org.harlanzw.com//guide/guides/identity.html
// Personal Website or Blog
definePerson({
name: computed(() => siteConfig.value.author.name),
url: siteUrl,
image: computed(() => siteConfig.value.author.avatar),
sameAs: computed(() => siteConfig.value.social.map(s => s.link)),
}),
defineWebSite({
name: title,
datePublished: computed(() => fm.value.date),
dateModified: computed(() => fm.value.updated),
}),
defineWebPage(),
])
</script>
<template>

View File

@ -0,0 +1,23 @@
import { initValaxyConfig } from 'valaxy'
import { type UserModule } from '../types'
// https://unhead-schema-org.harlanzw.com/
export const install: UserModule = async ({ head, isClient, router }) => {
// Disables on client build, allows 0kb runtime
if (isClient && import.meta.env.PROD)
return
const valaxyConfig = initValaxyConfig()
const { SchemaOrgUnheadPlugin } = await import('@vueuse/schema-org')
head?.use(SchemaOrgUnheadPlugin({
// config
host: valaxyConfig.value.siteConfig.url || 'https://valaxy.site',
inLanguage: valaxyConfig.value.siteConfig.lang || 'en',
}, () => {
return {
path: router.currentRoute.value.path,
...router.currentRoute.value.meta,
}
}))
}

View File

@ -3,6 +3,7 @@ import type { ViteSSGContext } from 'vite-ssg'
import { install as installValaxy } from '../modules/valaxy'
import { install as installPinia } from '../modules/pinia'
import { install as installNprogress } from '../modules/nprogress'
import { install as installSchema } from '../modules/schemaOrg'
export default function setupMain(ctx: ViteSSGContext) {
// @ts-expect-error inject in runtime
@ -10,6 +11,9 @@ export default function setupMain(ctx: ViteSSGContext) {
const injection_arg = ctx
installValaxy(ctx)
installSchema(ctx)
installPinia(ctx)
installNprogress(ctx)

View File

@ -109,7 +109,8 @@ export async function createMarkdownToVueRenderFn(
) {
const md = await createMarkdownRenderer(options)
pages = pages.map(p => slash(p.replace(/\.md$/, '')))
// for dead link detection
pages = pages.map(p => slash(p.replace(/\.md$/, '')).replace(/\/index$/, ''))
const replaceRegex = genReplaceRegexp(userDefines, isBuild)
@ -192,13 +193,20 @@ export async function createMarkdownToVueRenderFn(
? url.slice(1)
: path.relative(srcDir, path.resolve(dir, url)),
),
)
).replace(/\/index$/, '')
if (
!pages.includes(resolved)
&& !fs.existsSync(path.resolve(dir, publicDir, `${resolved}.html`))
&& !(resolved.endsWith('/') && (
pages.includes(resolved.slice(0, -1)) || pages.includes(`${resolved}index`)
))
!(
resolved.endsWith('/')
? (
pages.includes(resolved.slice(0, -1))
)
: (
pages.includes(resolved)
|| fs.existsSync(path.resolve(dir, publicDir, `${resolved}.html`))
|| fs.existsSync(path.resolve(dir, publicDir, `${resolved}/index.html`))
)
)
)
recordDeadLink(url)
}

View File

@ -12,6 +12,8 @@ import { getIndexHtml } from '../common'
* dependencies used by client
*/
const clientDeps = [
'@vueuse/schema-org',
'@vueuse/head',
'@vueuse/integrations/useFuse',
'body-scroll-lock',

View File

@ -68,6 +68,7 @@
"@vueuse/core": "^9.13.0",
"@vueuse/head": "^1.1.23",
"@vueuse/integrations": "^9.13.0",
"@vueuse/schema-org": "^2.1.2",
"body-scroll-lock": "4.0.0-beta.0",
"consola": "^2.15.3",
"critters": "^0.0.16",
@ -127,7 +128,7 @@
"@types/katex": "^0.16.0",
"@types/markdown-it": "^12.2.3",
"@types/nprogress": "^0.2.0",
"@types/yargs": "^17.0.23",
"@types/yargs": "^17.0.24",
"debug": "^4.3.4",
"diacritics": "^1.3.0",
"https-localhost": "^4.7.1",

View File

@ -130,11 +130,12 @@ importers:
'@types/katex': ^0.16.0
'@types/markdown-it': ^12.2.3
'@types/nprogress': ^0.2.0
'@types/yargs': ^17.0.23
'@types/yargs': ^17.0.24
'@vitejs/plugin-vue': ^4.1.0
'@vueuse/core': ^9.13.0
'@vueuse/head': ^1.1.23
'@vueuse/integrations': ^9.13.0
'@vueuse/schema-org': ^2.1.2
body-scroll-lock: 4.0.0-beta.0
consola: ^2.15.3
critters: ^0.0.16
@ -194,6 +195,7 @@ importers:
'@vueuse/core': 9.13.0_vue@3.2.47
'@vueuse/head': 1.1.23_vue@3.2.47
'@vueuse/integrations': 9.13.0_hzz2dxfa5xczya3cgo7rr32wcu
'@vueuse/schema-org': 2.1.2_aw3eexg7qncxvqmgho2eyf5gxi
body-scroll-lock: 4.0.0-beta.0
consola: 2.15.3
critters: 0.0.16
@ -252,7 +254,7 @@ importers:
'@types/katex': 0.16.0
'@types/markdown-it': 12.2.3
'@types/nprogress': 0.2.0
'@types/yargs': 17.0.23
'@types/yargs': 17.0.24
debug: 4.3.4
diacritics: 1.3.0
https-localhost: 4.7.1
@ -1443,8 +1445,8 @@ packages:
resolution: {integrity: sha512-iO9ZQHkZxHn4mSakYV0vFHAVDyEOIJQrV2uZ06HxEPcx+mt8swXoZHIbaaJ2crJYFfErySgktuTZ3BeLz+XmFA==}
dev: true
/@types/yargs/17.0.23:
resolution: {integrity: sha512-yuogunc04OnzGQCrfHx+Kk883Q4X0aSwmYZhKjI21m+SVYzjIbrWl8dOOwSv5hf2Um2pdCOXWo9isteZTNXUZQ==}
/@types/yargs/17.0.24:
resolution: {integrity: sha512-6i0aC7jV6QzQB8ne1joVZ0eSFIstHsCrobmOtghM11yGlH0j43FKL2UhWdELkyps0zuf7qVTUVCCR+tgSlyLLw==}
dependencies:
'@types/yargs-parser': 21.0.0
dev: true
@ -1600,6 +1602,13 @@ packages:
'@unhead/shared': 1.1.23
dev: false
/@unhead/schema-org-vue/0.5.0:
resolution: {integrity: sha512-UEB54jF2+HBoHKqyqdw7oNLqdhDfEX/FSImtPT3oTDTfGBWpc7RGUPe2RG/j4N78Kk3SsfnHxxJO0GKIGPJRwQ==}
engines: {node: '>=14.0.0'}
peerDependencies:
'@unhead/vue': '>=1.1.9'
dev: false
/@unhead/schema/1.0.16:
resolution: {integrity: sha512-dBi/etixNyy2RBhQHYZE41efytHyv4RyVSyVXQTH4FYmBupvRUGZmGeuJMCp066V3CMTpfkV6V0oWPxqjdZ4MA==}
dependencies:
@ -2058,6 +2067,20 @@ packages:
resolution: {integrity: sha512-gdU7TKNAUVlXXLbaF+ZCfte8BjRJQWPCa2J55+7/h+yDtzw3vOoGQDRXzI6pyKyo6bXFT5/QoPE4hAknExjRLQ==}
dev: false
/@vueuse/schema-org/2.1.2_aw3eexg7qncxvqmgho2eyf5gxi:
resolution: {integrity: sha512-ZaVZ6RCeG4nTCbpruDv6GEzKxE3BH/V+x3yOAcSlLzK3CSuqZQjF0EAgV0PrlNVemN+whSSRqBG9wiWp6AOtoQ==}
engines: {node: '>=14.0.0'}
peerDependencies:
'@vueuse/head': '>=1.0.21'
vue: '>=3.2.0'
dependencies:
'@unhead/schema-org-vue': 0.5.0
'@vueuse/head': 1.1.23_vue@3.2.47
vue: 3.2.47
transitivePeerDependencies:
- '@unhead/vue'
dev: false
/@vueuse/shared/9.13.0_vue@3.2.47:
resolution: {integrity: sha512-UrnhU+Cnufu4S6JLCPZnkWh0WwZGUp72ktOF2DFptMlOs3TOdVv8xJN53zhHGARmVOsz5KqOls09+J1NR6sBKw==}
dependencies: