mirror of https://github.com/YunYouJun/valaxy
feat: add schemaOrg & resolve dead links by replace /index
This commit is contained in:
parent
73d6e7af41
commit
66e216f7f5
|
@ -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
|
||||
|
|
|
@ -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/)
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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()
|
||||
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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,
|
||||
}
|
||||
}))
|
||||
}
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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',
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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:
|
||||
|
|
Loading…
Reference in New Issue