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

相关推荐
子兮曰5 小时前
Bun v1.3.14 深度解析:Image API、HTTP/3、全局虚拟存储与五十项变革
前端·后端·bun
kyriewen6 小时前
今天,百年巨头一次砍了9200人,而一个离职科学家的实话让全网睡不着觉
前端·openai·ai编程
问心无愧05137 小时前
ctf show web 入门42
android·前端·android studio
kyriewen7 小时前
老板逼我上AI,我偷偷在浏览器里跑LLaMA,省下20万API费
前端·react.js·llm
Beginner x_u7 小时前
前端八股整理(手写 02)|数组转树、数组扁平化、随机打乱一个数组
前端·数组·数组转树·数组扁平化
KaMeidebaby7 小时前
卡梅德生物技术快报|禽类成纤维细胞 FISH 实验:鸟类性别染色体基因定位技术实现与数据验证
前端·数据库·其他·百度·新浪微博
天若有情6738 小时前
前端高阶性能优化:跳出传统懒加载与预加载,基于用户行为做轻量预判加载
前端·性能优化
小小小小宇8 小时前
前端转后端:SQL 是什么
前端
张元清9 小时前
React Observer Hooks:7 种监听 DOM 而不写样板代码的方式
前端·javascript·面试
广州华水科技9 小时前
单北斗GNSS变形监测是什么?主要有怎样的应用与优势?
前端