You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Nuxt3通过API获取的SEO元数据分享社交媒体不显示问题

问题:Nuxt3博客分享至社交媒体时动态元数据不生效

我基于Nuxt3搭建了博客平台,所有博客内容及对应的SEO元数据均通过API接口响应渲染。目前遇到的问题是:将博客链接分享至社交媒体时,仅能显示app.vue文件中声明的静态元数据,API返回的动态元数据无法正常展示;但在浏览器开发者工具的元素审查面板中,可以查看到所有需要的元数据标签。

相关代码

pages/blog/[slug].vue

<script setup>
import { onMounted, ref, defineProps } from 'vue';
import { useBlogStore } from '~/stores/blog';
import config from '~/app.config.ts'

const route = useRoute()
const blogStore = useBlogStore(); // Create an instance of the blog store
const blog = ref([]);
const { currentRoute } = useRouter();

onMounted(async () => {

  try {
    await blogStore.getBlog(route.params.slug); // Call the getBlogs action
    blog.value = blogStore.$state.blog; // Update the local ref with the fetched data
  } catch (error) {
    console.log(error);
  }

  useHead({
    title: blog.value.title,
    meta: [
      { name: 'description', content: blog.value.meta_description },
      { name: 'keywords', content: blog.value.keywords },
      { name: 'og:image', content: blog.value.image_url },
      { name: 'og:title', content: blog.value.title },
      { name: 'og:description', content: blog.value.meta_description },
      { name: 'og:type', content: 'article' },
      { name: 'og:url', content: config.web.baseURL + currentRoute.value.fullPath },
      { name: 'twitter:card', content: 'summary_large_image' },
      { name: 'twitter:url', content: config.web.baseURL + currentRoute.value.fullPath },
      { name: 'twitter:title', content: blog.value.title },
      { name: 'twitter:description', content: blog.value.meta_description },
      { name: 'twitter:image', content: blog.value.image_url },
    ],
  })
});
</script>

app.vue

<script setup>
  definePageMeta({
    colorMode: 'dark',
  })

  useHead({
    title: 'My Title',
    meta: [
      { name: 'description', content: 'Nuxt app meta description for SEO!' },
      { name: 'theme-color', content: '#000000' },
    ],
  })
</script>

原因分析

社交媒体爬虫只会抓取服务器渲染的初始HTML内容,不会执行客户端JavaScript代码。你将useHead放在onMounted钩子中,这是客户端页面挂载后才会执行的逻辑,爬虫无法获取这部分动态生成的元数据,因此只会读取app.vue中的静态元数据。

解决方案

1. 改用服务端预获取数据的钩子

在Nuxt3中,使用useAsyncData在服务器端预获取博客数据,确保元数据在服务器渲染阶段就已生成,爬虫能直接读取。

修改pages/blog/[slug].vue代码如下:

<script setup>
import { useBlogStore } from '~/stores/blog';
import config from '~/app.config.ts'

const route = useRoute()
const blogStore = useBlogStore();
const { currentRoute } = useRouter();

// 服务端预获取博客数据
const { data: blog } = await useAsyncData('blog', async () => {
  await blogStore.getBlog(route.params.slug);
  return blogStore.$state.blog;
});

// 直接在setup中调用useHead,无需放在onMounted
useHead({
  title: blog.value?.title || '默认博客标题',
  meta: [
    { name: 'description', content: blog.value?.meta_description || '默认博客描述' },
    { name: 'keywords', content: blog.value?.keywords || '' },
    // OG标签规范使用property属性而非name
    { property: 'og:image', content: blog.value?.image_url || '' },
    { property: 'og:title', content: blog.value?.title || '默认博客标题' },
    { property: 'og:description', content: blog.value?.meta_description || '默认博客描述' },
    { property: 'og:type', content: 'article' },
    { property: 'og:url', content: config.web.baseURL + currentRoute.value.fullPath },
    { name: 'twitter:card', content: 'summary_large_image' },
    { name: 'twitter:url', content: config.web.baseURL + currentRoute.value.fullPath },
    { name: 'twitter:title', content: blog.value?.title || '默认博客标题' },
    { name: 'twitter:description', content: blog.value?.meta_description || '默认博客描述' },
    { name: 'twitter:image', content: blog.value?.image_url || '' },
  ],
})
</script>

2. 修正OG标签属性名

遵循Open Graph规范,OG标签应使用property属性而非name,否则部分社交媒体平台可能无法识别标签内容。

3. 添加默认值兜底

为所有元数据字段添加默认值,避免数据获取失败时元数据为空,保证页面基础SEO表现。

4. 验证服务器渲染结果

部署后,可通过curl命令直接请求页面,查看返回的HTML是否包含动态生成的元数据:

curl https://你的博客域名/blog/具体文章slug

若返回的HTML中能看到对应博客的标题、描述等元数据,说明配置生效。


内容的提问来源于stack exchange,提问作者Sourav Dutt

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.03 01:21:20