前言
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))
}
搜索引擎提交
Google Search Console
- 注册并验证网站
- 提交 Sitemap
- 监控索引状态
百度搜索资源平台
- 注册并验证网站
- 提交 Sitemap(需要 XML 格式)
- 配置移动适配
💡 SEO 检查清单
每个页面有唯一且描述性的 title
所有页面有 meta description
图片添加 alt 属性
使用语义化 HTML 标签
生成并提交 Sitemap
添加结构化数据
网站速度优化
移动端适配
🔗 工具推荐Google PageSpeed Insights
Lighthouse
Schema.org Validator