Remix SEO优化 #
一、SEO概述 #
Remix 提供了强大的 SEO 支持,包括 meta 函数、links 函数和服务端渲染。
二、Meta标签 #
2.1 基本用法 #
tsx
import type { MetaFunction } from "@remix-run/node";
export const meta: MetaFunction = () => {
return [
{ title: "我的网站 - 首页" },
{ name: "description", content: "这是我的网站描述" },
{ name: "keywords", content: "关键词1, 关键词2" },
];
};
2.2 动态Meta #
tsx
import type { MetaFunction, LoaderFunctionArgs } from "@remix-run/node";
import { json } from "@remix-run/node";
export async function loader({ params }: LoaderFunctionArgs) {
const post = await getPost(params.id);
return json({ post });
}
export const meta: MetaFunction<typeof loader> = ({ data }) => {
return [
{ title: `${data?.post.title} - 我的博客` },
{ name: "description", content: data?.post.excerpt },
{ name: "keywords", content: data?.post.tags?.join(", ") },
];
};
2.3 Open Graph #
tsx
export const meta: MetaFunction<typeof loader> = ({ data }) => {
return [
{ title: data?.post.title },
{ property: "og:title", content: data?.post.title },
{ property: "og:description", content: data?.post.excerpt },
{ property: "og:image", content: data?.post.coverImage },
{ property: "og:type", content: "article" },
{ property: "og:url", content: `https://example.com/posts/${data?.post.id}` },
];
};
2.4 Twitter Card #
tsx
export const meta: MetaFunction<typeof loader> = ({ data }) => {
return [
{ name: "twitter:card", content: "summary_large_image" },
{ name: "twitter:title", content: data?.post.title },
{ name: "twitter:description", content: data?.post.excerpt },
{ name: "twitter:image", content: data?.post.coverImage },
];
};
三、结构化数据 #
3.1 JSON-LD #
tsx
export default function Post() {
const { post } = useLoaderData<typeof loader>();
const structuredData = {
"@context": "https://schema.org",
"@type": "Article",
headline: post.title,
description: post.excerpt,
image: post.coverImage,
author: {
"@type": "Person",
name: post.author.name,
},
datePublished: post.publishedAt,
dateModified: post.updatedAt,
};
return (
<article>
<script
type="application/ld+json"
dangerouslySetInnerHTML={{ __html: JSON.stringify(structuredData) }}
/>
<h1>{post.title}</h1>
<div>{post.content}</div>
</article>
);
}
3.2 面包屑结构化数据 #
tsx
export default function Product() {
const { product, category } = useLoaderData<typeof loader>();
const breadcrumbData = {
"@context": "https://schema.org",
"@type": "BreadcrumbList",
itemListElement: [
{
"@type": "ListItem",
position: 1,
name: "首页",
item: "https://example.com",
},
{
"@type": "ListItem",
position: 2,
name: category.name,
item: `https://example.com/categories/${category.slug}`,
},
{
"@type": "ListItem",
position: 3,
name: product.name,
},
],
};
return (
<div>
<script
type="application/ld+json"
dangerouslySetInnerHTML={{ __html: JSON.stringify(breadcrumbData) }}
/>
{/* 页面内容 */}
</div>
);
}
四、站点地图 #
4.1 动态站点地图 #
创建 app/routes/sitemap[.]xml.ts:
tsx
import type { LoaderFunctionArgs } from "@remix-run/node";
export async function loader({}: LoaderFunctionArgs) {
const posts = await getPosts();
const pages = await getPages();
const sitemap = `<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
<url>
<loc>https://example.com</loc>
<changefreq>daily</changefreq>
<priority>1.0</priority>
</url>
${posts.map((post) => `
<url>
<loc>https://example.com/posts/${post.slug}</loc>
<lastmod>${post.updatedAt}</lastmod>
<changefreq>weekly</changefreq>
<priority>0.8</priority>
</url>
`).join("")}
${pages.map((page) => `
<url>
<loc>https://example.com/${page.slug}</loc>
<lastmod>${page.updatedAt}</lastmod>
<changefreq>monthly</changefreq>
<priority>0.6</priority>
</url>
`).join("")}
</urlset>`;
return new Response(sitemap, {
headers: {
"Content-Type": "application/xml",
},
});
}
4.2 robots.txt #
创建 app/routes/robots[.]txt.ts:
tsx
import type { LoaderFunctionArgs } from "@remix-run/node";
export async function loader({}: LoaderFunctionArgs) {
const robots = `User-agent: *
Allow: /
Sitemap: https://example.com/sitemap.xml`;
return new Response(robots, {
headers: {
"Content-Type": "text/plain",
},
});
}
五、规范链接 #
5.1 Canonical URL #
tsx
export const links: LinksFunction = () => [
{ rel: "canonical", href: "https://example.com/posts/my-post" },
];
5.2 多语言链接 #
tsx
export const links: LinksFunction = () => [
{ rel: "alternate", hrefLang: "en", href: "https://example.com/en/post" },
{ rel: "alternate", hrefLang: "zh", href: "https://example.com/zh/post" },
{ rel: "alternate", hrefLang: "x-default", href: "https://example.com/post" },
];
六、性能与SEO #
6.1 预渲染关键页面 #
tsx
export const headers = () => ({
"Cache-Control": "public, max-age=3600, s-maxage=86400",
});
6.2 图片优化 #
tsx
export const links: LinksFunction = () => [
{
rel: "preload",
href: "/images/hero.webp",
as: "image",
},
];
七、最佳实践 #
7.1 SEO检查清单 #
- [ ] 每个页面有唯一的title
- [ ] 每个页面有描述性的meta description
- [ ] 使用语义化HTML标签
- [ ] 图片有alt属性
- [ ] 链接可爬取
- [ ] 有站点地图
- [ ] 有robots.txt
- [ ] 使用HTTPS
- [ ] 页面加载速度快
7.2 常见问题 #
- 重复内容:使用canonical标签
- 动态内容:确保服务端渲染
- 图片SEO:使用alt属性和懒加载
八、总结 #
本章我们学习了:
- Meta标签:动态设置页面元数据
- 结构化数据:JSON-LD格式
- 站点地图:动态生成sitemap.xml
- 规范链接:canonical和多语言链接
- 最佳实践:SEO检查清单
核心要点:
- 使用meta函数设置页面元数据
- 添加结构化数据增强搜索结果
- 生成站点地图帮助爬虫
- 使用语义化HTML
最后更新:2026-03-28