我遇到的问题源于尝试将动态 OpenGraph 元标记应用到 Nuxt 3(以及扩展的 Vue 3)中动态生成的路线。
我尝试通过 Javascript 动态设置元标记 - 这似乎是 Nuxt 3 目前支持的唯一动态选项,但无济于事。显然,当开放图谱抓取工具请求页面时,它不会运行任何 JavaScript,这意味着我的元标记不会被应用。
我不想在服务器端渲染这些页面,保持它们动态生成是这个问题的重要组成部分。
到目前为止,我已经尝试使用
<Head>
标签,内容属性动态生成:
<Head>
<Meta hid="og:url" property="og:url" :content="`https://my-site.com/{$route.path}`" />
</Head>
这会导致元标记正确应用,但仅在 Javascript 执行之后。正如我之前提到的,开放图网络抓取工具没有正确应用它。
我希望找到的解决方案是一种可以在构建时添加元标记的方法 - 这可能吗?或者还有我没有考虑的更好的解决方案吗?
我相信你需要一个服务器来为你解决这个问题,所以页面应该是SSR,或者你需要在构建时(SSG)生成这些页面。
你也可以使用
useHead
可组合,但我认为你至少应该在这些页面使用 SSR 或 SSG:
的属性可以是动态的,接受引用、计算和反应属性。元参数还可以接受返回对象的函数,以使整个对象具有反应性。useHead
在此处了解更多信息:https://nuxt.com/docs/api/composables/use-head
请检查:https://stackblitz.com/edit/nuxt-starter-51pnfj
并且您的代码中有一个错误。如果您使用组合 API,您的代码应该是这样的:
<Head>
<Meta hid="og:url" property="og:url" :content="`https://my-site.com${route.path}`" />
</Head>
<script setup>
const route = useRoute();
</script>
如果您使用选项 API,您的代码应该是这样的:
<Head>
<Meta hid="og:url" property="og:url" :content="`https://my-site.com${$route.path}`" />
</Head>
如果您没有将
ssr
设置为 false
,默认情况下它将为 true,或者如果您不希望所有页面都使用 SSR,我认为您可以使用混合渲染。所以结果会是这样的:
您尝试过define-nuxt-route-middleware吗?它允许在构建时运行您的可组合函数。所以你的元应该已经正确应用于 SEO。相反,我在每个页面中使用 definePageMeta 而不是 useHead :
[某些页面].vue
<script setup>
definePageMeta({
order: 1,
label: "Perusahaan",
title: "Perusahaan/Klien",
description:
"Kami memudahkan administrasi, semua absensi pekerja dapat dengan mudah dilacak riwayatnya serta memantau serta mengatur kehadiran pekerja dengan Geotagging dan pengelompokan area kerja untuk perusahaan atau klien",
icon: "domain",
transparent: true,
image: "/perusahaan/invoice.png",
});
<script/>
并使用 useHead 一次,
中间件/meta.js
export default defineNuxtRouteMiddleware(async (to, from) => {
let data = null,
url = null,
params = null;
if (to.fullPath?.includes("berita") && to?.params?.slug) {
url = new URL(`${useRuntimeConfig().public?.database}/Articles/get`);
params = {
jsonQuery: JSON.stringify({
slug: to?.params?.slug,
}),
limit: 1,
};
} else if (to.fullPath?.includes("faq") && to?.params?.id) {
url = new URL(`${useRuntimeConfig().public?.database}/FAQ/get`);
params = {
jsonQuery: JSON.stringify({
_id: to?.params?.id,
}),
limit: 1,
};
// console.log(data);
}
if (url && params) {
Object.keys(params).forEach((key) =>
url.searchParams.append(key, params[key])
);
data = await fetch(url, {
method: "GET",
});
data = await data?.json();
}
if (data?.success)
data = data?.result?.[0];
if (data || (to?.meta?.title && to?.meta?.description)) {
useHead(
useNuxtApp().$metaGenerator(
data?.title || to?.meta?.title,
data?.description || data?.excerpt || to?.meta?.description,
to?.fullPath,
data?.picture || to?.meta?.image,
to?.meta?.keywords
)
);
}
});
$metaGenerator(插件/index.js):
export default defineNuxtPlugin((nuxtApp) => {
return {
metaGenerator: (
title,
description,
path,
image,
keywords,
site = "@website"
) => {
const defaultKeywords = [
"lowongan kerja",
];
if (Array.isArray(keywords)) keywords.concat(defaultKeywords);
else keywords = defaultKeywords.concat(keywords || "");
if (!image) {
image = "/favicon.ico";
}
const url =
`${useRuntimeConfig().hostname}${path}` ||
useRuntimeConfig().hostname;
return {
title,
meta: [
{
name: "description",
content: description,
},
{
rel: "canonical",
href: url,
},
{
rel: "amphtml",
href: url,
},
{
name: "keywords",
content: keywords,
},
// google
{
itemprop: "name",
content: title,
},
{
itemprop: "description",
content: description,
},
{
itemprop: "image",
content: image,
},
// twitter card
{
name: "twitter:card",
content: "summary_large_image",
},
{ name: "twitter:site", content: site },
{
name: "twitter:title",
content: title,
},
{
name: "twitter:description",
content: description,
},
{
name: "twitter:image",
content: image,
},
{
name: "twitter:image:alt",
content: title,
},
{
name: "twitter:url",
content: url,
},
// Open Graph
{ property: "og:site_name", content: site },
{ property: "og:type", content: "website" },
{
property: "og:title",
content: title,
},
{
property: "og:description",
content: description,
},
{
property: "og:image",
content: image,
},
{
property: "og:url",
content: url,
},
{
property: "og:image:secure_url",
content: image,
},
{
property: "og:image:alt",
content: title,
},
],
link: [
{
rel: "canonical",
href: url,
},
{
rel: "amphtml",
href: url,
},
],
};
},
}
})
useSeoMeta
对我有用,而 useHead
则不然。我试图使用以下代码更改描述元。
useSeoMeta({
description: () => 'New meta description',
});
我使用 useSeoMeta 它对我有用
<script setup lang="ts">
useSeoMeta({
title: () => 'My Amazing Site',
ogTitle: () => 'My Amazing Site',
description: () => 'This is my amazing site, let me tell you all about it.',
ogDescription: () => 'This is my amazing site, let me tell you all about it.',
ogImage: () => 'https://example.com/image.png',
twitterCard: () => 'summary_large_image',
})
</script>