Vue 3 博客 SEO 优化:Meta 标签、Sitemap、Schema.org 实战

前言

SEO 是博客获取自然流量的关键。一个 SEO 友好的博客可以带来持续稳定的访问量。

核心优化策略

1. Meta 标签配置

typescript 复制代码
// src/utils/seo.ts
interface PageMeta {
  title: string
  description: string
  keywords?: string
  image?: string
  type?: 'website' | 'article'
  publishedTime?: string
  modifiedTime?: string
  author?: string
  tags?: string[]
}

export function setPageMeta(meta: PageMeta) {
  const { title, description, keywords, image, type, publishedTime, author } = meta
  
  // 基础标签
  document.title = `${title} | 博客名称`
  
  // Meta 标签
  setMeta('description', description)
  if (keywords) setMeta('keywords', keywords)
  if (author) setMeta('author', author)
  
  // Open Graph
  setMeta('og:title', title)
  setMeta('og:description', description)
  setMeta('og:type', type || 'website')
  setMeta('og:image', image)
  setMeta('og:site_name', '博客名称')
  
  // Twitter Card
  setMeta('twitter:card', 'summary_large_image')
  setMeta('twitter:title', title)
  setMeta('twitter:description', description)
  setMeta('twitter:image', image)
  
  // Article 特定标签
  if (type === 'article') {
    if (publishedTime) setMeta('article:published_time', publishedTime)
    if (author) setMeta('article:author', author)
  }
}

function setMeta(name: string, content: string) {
  let meta = document.querySelector(`meta[name="${name}"], meta[property="${name}"]`)
  
  if (!meta) {
    meta = document.createElement('meta')
    if (name.startsWith('og:') || name.startsWith('twitter:') || name.startsWith('article:')) {
      meta.setAttribute('property', name)
    } else {
      meta.setAttribute('name', name)
    }
    document.head.appendChild(meta)
  }
  
  meta.setAttribute('content', content)
}

2. 生成 Sitemap

typescript 复制代码
// scripts/generate-sitemap.ts
import { writeFileSync } from 'fs'
import { articles } from '../src/data/articles'

const BASE_URL = 'https://your-blog.com'

const urls = [
  { loc: BASE_URL, priority: '1.0', changefreq: 'daily' },
  { loc: `${BASE_URL}/about`, priority: '0.8', changefreq: 'monthly' },
  { loc: `${BASE_URL}/archives`, priority: '0.7', changefreq: 'weekly' },
  { loc: `${BASE_URL}/tags`, priority: '0.6', changefreq: 'weekly' },
]

// 添加文章页面
articles.forEach(article => {
  urls.push({
    loc: `${BASE_URL}/article/${article.id}`,
    priority: '0.9',
    changefreq: 'monthly'
  })
})

const sitemap = `<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
${urls.map(url => `  <url>
    <loc>${url.loc}</loc>
    <priority>${url.priority}</priority>
    <changefreq>${url.changefreq}</changefreq>
  </url>`).join('\n')}
</urlset>`

writeFileSync('dist/sitemap.xml', sitemap)
console.log('Sitemap generated!')

3. Schema.org 结构化数据

vue 复制代码
<!-- src/components/seo/ArticleSchema.vue -->
<template>
  <script type="application/ld+json" v-html="schemaData" />
</template>

<script setup lang="ts">
import { computed } from 'vue'
import type { Article } from '@/types/article'

const props = defineProps<{
  article: Article
}>()

const schemaData = computed(() => {
  const article = props.article
  
  return JSON.stringify({
    '@context': 'https://schema.org',
    '@type': 'Article',
    headline: article.title,
    description: article.summary || article.description,
    image: article.cover,
    author: {
      '@type': 'Person',
      name: '作者名称'
    },
    publisher: {
      '@type': 'Organization',
      name: '博客名称',
      logo: {
        '@type': 'ImageObject',
        url: 'https://your-blog.com/logo.png'
      }
    },
    datePublished: article.publishedAt,
    dateModified: article.updatedAt || article.publishedAt,
    mainEntityOfPage: {
      '@type': 'WebPage',
      '@id': `https://your-blog.com/article/${article.id}`
    }
  })
})
</script>

4. 路由级别 SEO

typescript 复制代码
// src/router/index.ts
import { createRouter, createWebHistory } from 'vue-router'
import type { RouteRecordRaw } from 'vue-router'

const routes: RouteRecordRaw[] = [
  {
    path: '/',
    component: () => import('@/views/Home.vue'),
    meta: {
      title: '首页',
      description: '技术博客,分享前端开发经验'
    }
  },
  {
    path: '/article/:id',
    component: () => import('@/views/Article.vue'),
    meta: {
      title: '文章详情',
      description: ''
    }
  }
]

const router = createRouter({
  history: createWebHistory(),
  routes,
  scrollBehavior(to, from, savedPosition) {
    if (savedPosition) {
      return savedPosition
    } else {
      return { top: 0 }
    }
  }
})

// 全局前置守卫设置 Meta
router.beforeEach((to, from, next) => {
  const meta = to.meta as Record<string, any>
  
  if (meta.title) {
    document.title = `${meta.title} | 博客名称`
  }
  
  if (meta.description) {
    let descMeta = document.querySelector('meta[name="description"]')
    if (!descMeta) {
      descMeta = document.createElement('meta')
      descMeta.setAttribute('name', 'description')
      document.head.appendChild(descMeta)
    }
    descMeta.setAttribute('content', meta.description)
  }
  
  next()
})

export default router

5. robots.txt

复制代码
# public/robots.txt
User-agent: *
Allow: /
Disallow: /api/
Disallow: /admin/

Sitemap: https://your-blog.com/sitemap.xml

6. 性能优化

typescript 复制代码
// src/utils/seo.ts

// 预连接关键资源
export function addPreconnect() {
  const links = [
    { rel: 'preconnect', href: 'https://fonts.googleapis.com' },
    { rel: 'preconnect', href: 'https://fonts.gstatic.com', crossorigin: '' },
    { rel: 'dns-prefetch', href: 'https://www.google-analytics.com' }
  ]
  
  links.forEach(link => {
    const el = document.createElement('link')
    el.rel = link.rel
    el.href = link.href
    if ('crossorigin' in link) {
      el.setAttribute('crossorigin', '')
    }
    document.head.appendChild(el)
  })
}

// 图片懒加载
export function setupLazyLoading() {
  const images = document.querySelectorAll('img[data-src]')
  
  const observer = new IntersectionObserver((entries) => {
    entries.forEach(entry => {
      if (entry.isIntersecting) {
        const img = entry.target as HTMLImageElement
        img.src = img.dataset.src || ''
        img.removeAttribute('data-src')
        observer.unobserve(img)
      }
    })
  })
  
  images.forEach(img => observer.observe(img))
}

搜索引擎提交

  1. 注册并验证网站
  2. 提交 Sitemap
  3. 监控索引状态

百度搜索资源平台

  1. 注册并验证网站
  2. 提交 Sitemap(需要 XML 格式)
  3. 配置移动适配

💡 SEO 检查清单

  • 每个页面有唯一且描述性的 title

  • 所有页面有 meta description

  • 图片添加 alt 属性

  • 使用语义化 HTML 标签

  • 生成并提交 Sitemap

  • 添加结构化数据

  • 网站速度优化

  • 移动端适配
    🔗 工具推荐

  • Google PageSpeed Insights

  • Lighthouse

  • Schema.org Validator

相关推荐
Apple_羊先森2 小时前
# MOSS-TTS-Nano 教程 02:CLI 与 Web Demo 实战
前端·人工智能
Bat U2 小时前
JavaEE|多线程(三)
java·前端·java-ee
仿生狮子7 小时前
把 Git 提交历史变成一条流动的河——Project River
vue.js·设计·vibecoding
xixingzhe211 小时前
idea启动vue项目
java·vue.js·intellij-idea
超级无敌暴龙兽11 小时前
和我一起刷面试题呀
前端·面试
wzl2026121311 小时前
企业微信定时群发技术实现与实操指南(原生接口+工具落地)
java·运维·前端·企业微信
小码哥_常11 小时前
Robots.txt:互联网爬虫世界的“隐形规则”
前端
小码哥_常12 小时前
Android开发神器:AndroidAutoSize,轻松搞定屏幕适配
前端
前端一小卒12 小时前
前端工程师的全栈焦虑,我用 60 天治好了
前端·javascript·后端