SEO JSON-LD improvement
This commit is contained in:
parent
d6884ba09c
commit
ecd2829c95
@ -24,7 +24,7 @@ export function usePageMeta () {
|
|||||||
|
|
||||||
// Canonical URL
|
// Canonical URL
|
||||||
const config = useRuntimeConfig()
|
const config = useRuntimeConfig()
|
||||||
const canonical = `${config.public.appUrl}${route.path}`
|
const canonical = `${config.public.appUrl}${page.pageLink}`
|
||||||
|
|
||||||
// Robots Meta Tag
|
// Robots Meta Tag
|
||||||
const robotsContent = route.path === '/danke' ? 'noindex, nofollow' : 'index, follow'
|
const robotsContent = route.path === '/danke' ? 'noindex, nofollow' : 'index, follow'
|
||||||
@ -83,6 +83,7 @@ export function usePageMeta () {
|
|||||||
{ property: 'og:title', content: metaTitle },
|
{ property: 'og:title', content: metaTitle },
|
||||||
{ property: 'og:description', content: metaDescription },
|
{ property: 'og:description', content: metaDescription },
|
||||||
{ property: 'og:image', content: metaImage },
|
{ property: 'og:image', content: metaImage },
|
||||||
|
{ property: 'og:type', content: 'website' },
|
||||||
{ name: 'twitter:title', content: metaTitle },
|
{ name: 'twitter:title', content: metaTitle },
|
||||||
{ name: 'twitter:description', content: metaDescription },
|
{ name: 'twitter:description', content: metaDescription },
|
||||||
{ name: 'twitter:image', content: metaImage }
|
{ name: 'twitter:image', content: metaImage }
|
||||||
|
|||||||
@ -17,7 +17,13 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
// Nuxt 3 behandelt den 404-Status automatisch
|
import { createError } from 'nuxt/app'
|
||||||
|
|
||||||
|
// Wichtig: Der 404-Status **muss hier geworfen werden**
|
||||||
|
throw createError({
|
||||||
|
statusCode: 404,
|
||||||
|
statusMessage: 'Seite nicht gefunden'
|
||||||
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="sass">
|
<style lang="sass">
|
||||||
|
|||||||
@ -56,6 +56,7 @@
|
|||||||
import { useHtmlConverter } from '@/composables/useHTMLConverter'
|
import { useHtmlConverter } from '@/composables/useHTMLConverter'
|
||||||
import SideBarNaviSlider from '@/components/SideBarNaviSlider.vue'
|
import SideBarNaviSlider from '@/components/SideBarNaviSlider.vue'
|
||||||
import { useI18n } from 'vue-i18n'
|
import { useI18n } from 'vue-i18n'
|
||||||
|
const runtimeConfig = useRuntimeConfig();
|
||||||
|
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
const slug = route.params.link
|
const slug = route.params.link
|
||||||
@ -72,8 +73,6 @@
|
|||||||
return articles.value.find(item => item.slug === slug) ?? null
|
return articles.value.find(item => item.slug === slug) ?? null
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const { convertToHTML } = useHtmlConverter()
|
const { convertToHTML } = useHtmlConverter()
|
||||||
const htmlContent = (content) => convertToHTML(content)
|
const htmlContent = (content) => convertToHTML(content)
|
||||||
|
|
||||||
@ -87,6 +86,74 @@
|
|||||||
const d = new Date(article.value.dateModified)
|
const d = new Date(article.value.dateModified)
|
||||||
return d.toLocaleDateString('de-DE')
|
return d.toLocaleDateString('de-DE')
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// ARTIKEL META AND JSON_LD INFOS
|
||||||
|
watchEffect(() => {
|
||||||
|
if (!article.value) return
|
||||||
|
|
||||||
|
const truncate = (text, maxLength) => {
|
||||||
|
if (!text) return ''
|
||||||
|
return text.length <= maxLength ? text : text.slice(0, maxLength - 1).trimEnd() + '…'
|
||||||
|
}
|
||||||
|
|
||||||
|
const rawTitle = article.value.header
|
||||||
|
const rawDescription = article.value.teaser
|
||||||
|
|
||||||
|
const metaTitle = truncate(rawTitle, 60)
|
||||||
|
const metaDescription = truncate(rawDescription, 160)
|
||||||
|
const metaImage = `${runtimeConfig.public.cmsBaseUrl}${article.value.image?.url}` || 'https://strapi.digimedialoop.de/uploads/DML_Logo_Info_c4011028f9.png'
|
||||||
|
const metaKeywords = article.value.SEO?.keywords || ''
|
||||||
|
const metaType = article.value.SEO?.type || 'article'
|
||||||
|
const canonical = `${runtimeConfig.public.appUrl}${route.fullPath}`
|
||||||
|
|
||||||
|
const jsonLd = {
|
||||||
|
'@context': 'https://schema.org',
|
||||||
|
'@type': 'NewsArticle',
|
||||||
|
headline: rawTitle,
|
||||||
|
image: [metaImage],
|
||||||
|
datePublished: article.value.dateCreated,
|
||||||
|
dateModified: article.value.dateModified,
|
||||||
|
author: {
|
||||||
|
'@type': 'Person',
|
||||||
|
name: author
|
||||||
|
},
|
||||||
|
publisher: {
|
||||||
|
'@type': 'Organization',
|
||||||
|
name: 'digimedialoop',
|
||||||
|
logo: {
|
||||||
|
'@type': 'ImageObject',
|
||||||
|
url: 'https://strapi.digimedialoop.de/uploads/DML_Logo_Info_c4011028f9.png'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
description: metaDescription
|
||||||
|
}
|
||||||
|
|
||||||
|
useHead({
|
||||||
|
title: metaTitle,
|
||||||
|
meta: [
|
||||||
|
{ name: 'description', content: metaDescription },
|
||||||
|
{ name: 'keywords', content: metaKeywords },
|
||||||
|
{ name: 'robots', content: 'index, follow' },
|
||||||
|
{ property: 'og:title', content: metaTitle },
|
||||||
|
{ property: 'og:description', content: metaDescription },
|
||||||
|
{ property: 'og:image', content: metaImage },
|
||||||
|
{ property: 'og:type', content: metaType },
|
||||||
|
{ name: 'twitter:title', content: metaTitle },
|
||||||
|
{ name: 'twitter:description', content: metaDescription },
|
||||||
|
{ name: 'twitter:image', content: metaImage }
|
||||||
|
],
|
||||||
|
link: [
|
||||||
|
{ rel: 'canonical', href: canonical }
|
||||||
|
],
|
||||||
|
script: [
|
||||||
|
{
|
||||||
|
type: 'application/ld+json',
|
||||||
|
children: JSON.stringify(jsonLd)
|
||||||
|
}
|
||||||
|
]
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="sass">
|
<style scoped lang="sass">
|
||||||
|
|||||||
@ -33,7 +33,7 @@
|
|||||||
format="webp"
|
format="webp"
|
||||||
sizes="100vw"
|
sizes="100vw"
|
||||||
class="background-image"
|
class="background-image"
|
||||||
alt=""
|
alt="AI and human"
|
||||||
aria-hidden="true"
|
aria-hidden="true"
|
||||||
/>
|
/>
|
||||||
<div class="container-15 content">
|
<div class="container-15 content">
|
||||||
|
|||||||
@ -100,12 +100,9 @@ const localePath = useLocalePath()
|
|||||||
|
|
||||||
import { useRoute, useRouter } from 'vue-router'
|
import { useRoute, useRouter } from 'vue-router'
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
const router = useRouter()
|
|
||||||
|
|
||||||
import { storeToRefs } from 'pinia'
|
|
||||||
import { useMainStore } from '@/stores/main'
|
import { useMainStore } from '@/stores/main'
|
||||||
const mainStore = useMainStore()
|
const mainStore = useMainStore()
|
||||||
const { cmsUrl, projects, dataFetched } = storeToRefs(mainStore)
|
|
||||||
const project = computed(() => mainStore.getProjectByLink(route.params.link))
|
const project = computed(() => mainStore.getProjectByLink(route.params.link))
|
||||||
const customer = computed(() => {
|
const customer = computed(() => {
|
||||||
if (!project.value || !project.value.customer) return null
|
if (!project.value || !project.value.customer) return null
|
||||||
@ -125,8 +122,6 @@ watch(project, (newProject) => {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
import { useHtmlConverter } from '@/composables/useHTMLConverter'
|
import { useHtmlConverter } from '@/composables/useHTMLConverter'
|
||||||
const { convertToHTML } = useHtmlConverter()
|
const { convertToHTML } = useHtmlConverter()
|
||||||
const htmlContent = (data: string) => {
|
const htmlContent = (data: string) => {
|
||||||
@ -138,7 +133,83 @@ const setCurrentImage = (image: any) => {
|
|||||||
currentImage.value = image
|
currentImage.value = image
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// META INFO UND JSON_LD für Projekte
|
||||||
|
|
||||||
|
const runtimeConfig = useRuntimeConfig()
|
||||||
|
|
||||||
|
watchEffect(() => {
|
||||||
|
if (!project.value) return
|
||||||
|
|
||||||
|
const metaTitle = project.value.projectTitle.length > 60
|
||||||
|
? project.value.projectTitle.slice(0, 57).trim() + '…'
|
||||||
|
: project.value.projectTitle
|
||||||
|
|
||||||
|
const firstParagraph = project.value.projectDescription?.[0]?.children?.[0]?.text || ''
|
||||||
|
const metaDescription = firstParagraph.length > 160
|
||||||
|
? firstParagraph.slice(0, 157).trim() + '…'
|
||||||
|
: firstParagraph
|
||||||
|
|
||||||
|
const customerCompany = project.value.customer?.company || ''
|
||||||
|
const customerCity = project.value.customer?.city || ''
|
||||||
|
|
||||||
|
const technologyKeywords = project.value.technologies?.map(t => t.titel).join(', ') || ''
|
||||||
|
const metaKeywords = [customerCompany, customerCity, technologyKeywords]
|
||||||
|
.filter(Boolean)
|
||||||
|
.join(', ')
|
||||||
|
|
||||||
|
const metaImage = project.value.projectImages?.[0]?.url
|
||||||
|
? `${runtimeConfig.public.cmsBaseUrl}${project.value.projectImages[0].url}`
|
||||||
|
: 'https://strapi.digimedialoop.de/uploads/DML_Logo_Info_c4011028f9.png'
|
||||||
|
|
||||||
|
const canonical = `${runtimeConfig.public.appUrl}${route.fullPath}`
|
||||||
|
|
||||||
|
const jsonLd = {
|
||||||
|
"@context": "https://schema.org",
|
||||||
|
"@type": "CreativeWork",
|
||||||
|
name: project.value.projectTitle,
|
||||||
|
description: metaDescription,
|
||||||
|
url: canonical,
|
||||||
|
image: metaImage,
|
||||||
|
datePublished: project.value.launchDate,
|
||||||
|
author: {
|
||||||
|
"@type": "Organization",
|
||||||
|
name: "digimedialoop"
|
||||||
|
},
|
||||||
|
publisher: {
|
||||||
|
"@type": "Organization",
|
||||||
|
name: "digimedialoop",
|
||||||
|
logo: {
|
||||||
|
"@type": "ImageObject",
|
||||||
|
url: "https://strapi.digimedialoop.de/uploads/DML_Logo_Info_c4011028f9.png"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
useHead({
|
||||||
|
title: metaTitle,
|
||||||
|
meta: [
|
||||||
|
{ name: 'description', content: metaDescription },
|
||||||
|
{ name: 'keywords', content: metaKeywords },
|
||||||
|
{ name: 'robots', content: 'index, follow' },
|
||||||
|
{ property: 'og:title', content: metaTitle },
|
||||||
|
{ property: 'og:description', content: metaDescription },
|
||||||
|
{ property: 'og:image', content: metaImage },
|
||||||
|
{ property: 'og:type', content: 'website' },
|
||||||
|
{ name: 'twitter:title', content: metaTitle },
|
||||||
|
{ name: 'twitter:description', content: metaDescription },
|
||||||
|
{ name: 'twitter:image', content: metaImage }
|
||||||
|
],
|
||||||
|
link: [
|
||||||
|
{ rel: 'canonical', href: canonical }
|
||||||
|
],
|
||||||
|
script: [
|
||||||
|
{
|
||||||
|
type: 'application/ld+json',
|
||||||
|
children: JSON.stringify(jsonLd)
|
||||||
|
}
|
||||||
|
]
|
||||||
|
})
|
||||||
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="sass">
|
<style lang="sass">
|
||||||
|
|||||||
@ -92,15 +92,19 @@ const jsonLdProjects = computed(() => {
|
|||||||
"@type": "ItemList",
|
"@type": "ItemList",
|
||||||
"itemListElement": projects.value.map((project, index) => ({
|
"itemListElement": projects.value.map((project, index) => ({
|
||||||
"@type": "ListItem",
|
"@type": "ListItem",
|
||||||
"position": index + 1,
|
"position": index + 1,
|
||||||
"url": origin + (project.link ? getProjectLink(project.link) : ''),
|
"item": {
|
||||||
|
"@type": "CreativeWork", // alternativ "WebPage", wenn es sich um Projektseiten handelt
|
||||||
|
"@id": origin + (project.link ? getProjectLink(project.link) : ''),
|
||||||
"name": project.projectTitle || 'Projekt',
|
"name": project.projectTitle || 'Projekt',
|
||||||
"image": cmsUrl + (project.projectImages?.[0]?.url) || '',
|
"image": cmsUrl + (project.projectImages?.[0]?.url || ''),
|
||||||
"description": project.projectDescription?.[0]?.children?.[0]?.text || ''
|
"description": project.projectDescription?.[0]?.children?.[0]?.text || ''
|
||||||
}))
|
}
|
||||||
|
}))
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
// useHead einbinden, wenn Projekte da sind
|
// useHead einbinden, wenn Projekte da sind
|
||||||
watchEffect(() => {
|
watchEffect(() => {
|
||||||
if (jsonLdProjects.value) {
|
if (jsonLdProjects.value) {
|
||||||
|
|||||||
@ -6,7 +6,7 @@
|
|||||||
src="/uploads/large_DML_Home_Hero_4f27bc7f8e.webp"
|
src="/uploads/large_DML_Home_Hero_4f27bc7f8e.webp"
|
||||||
class="hero-bg"
|
class="hero-bg"
|
||||||
sizes="sm:100vw md:100vw lg:100vw"
|
sizes="sm:100vw md:100vw lg:100vw"
|
||||||
alt=""
|
alt="website example"
|
||||||
aria-hidden="true"
|
aria-hidden="true"
|
||||||
priority
|
priority
|
||||||
loading="eager"
|
loading="eager"
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user