NextJS 应用路由中实现功能性 SEO 的实用指南:静态和动态元数据
隆重推出……
首先,无需纠结 ReactJS 和 NextJS 哪个更好。如果您正在考虑优化您的网站/Web 应用,例如:
- 搜索引擎——抓取和索引——让您的网站和内容在搜索结果中获得排名
- 如果您正在构建动态元数据,以便在分享链接时获得精美的链接描述和图像预览……那么您就知道 NextJS 是您的最佳选择。
我们暂且不讨论页面路由和应用路由的区别,我个人非常喜欢应用路由架构带来的强大功能。从便捷的文件路由、更灵活的动态分段路由模式、以及用于服务器端操作的通用路由等等,应用路由功能非常强大。因此,本指南将只介绍如何使用应用路由来实现 SEO。
入门
首先,让我们创建一个新的 NextJS 项目(如果您还没有的话)。按照提示操作并设置您的偏好(我不会忽略对 TypeScript 的支持——您也应该这样做😜)。
npx create-next-app@latest
打开代码编辑器并启动开发服务器。
npm run dev
元数据
import type { Metadata } from "next";
import { Inter } from "next/font/google";
import "./globals.css";
const inter = Inter({ subsets: ["latin"] });
export const metadata: Metadata = {
title: "Create Next App",
description: "Generated by create next app",
};
export default function RootLayout({
children,
}: Readonly<{
children: React.ReactNode;
}>) {
return (
<html lang="en">
<body className={inter.className}>{children}</body>
</html>
);
}
上面的代码片段显示了文件中默认的 RootLayout 组件,它用作全局布局,所有页面和子页面都将从中继承任何 UI - 这也意味着,除非您从单个页面显式导出元数据对象(或) layout.tsx,否则您从该组件导出的元数据对象将由所有其他页面共享。page.tsxpage.jsx
- 您可以定义并导出名为“使用元数据类型(用于静态详细信息)”的变量
metadata,或者定义并导出名为“使用元数据类型(用于静态详细信息)”的函数generateMetadata(此函数对于加载动态数据特别有用,然后可用于创建动态页面的元数据对象)。 - 元数据可以在布局文件中定义,并在嵌套页面中被覆盖。
- NextJS 会根据您的元数据自动为每个页面生成合适的标签。
一个完整的元数据对象是什么样的?
export const metadata = {
title: 'My Awesome Website',
description: 'Discover amazing content and services on My Awesome Website',
// Basic metadata
applicationName: 'My Awesome App',
authors: [{ name: 'Stephen Omoregie', url: 'https://cre8stevedev.me' }],
generator: 'Next.js',
keywords: ['next.js', 'react', 'javascript'],
referrer: 'origin-when-cross-origin',
themeColor: '#4285f4',
colorScheme: 'dark',
viewport: 'width=device-width, initial-scale=1',
creator: 'Stephen Omoregie',
publisher: 'Cre8steve Dev',
// Open Graph metadata
openGraph: {
title: 'My Awesome Website',
description: 'Discover amazing content and services',
url: 'https://cre8stevedev.me',
siteName: 'My Awesome Website',
images: [
{
url: 'https://myawesomewebsite.com/og-image.jpg',
width: 1200, // This is the recommended size in pixels
height: 630,
alt: 'My Awesome Website og-image',
},
],
locale: 'en_US',
type: 'website',
},
// Twitter metadata
twitter: {
card: 'summary_large_image',
title: 'My Awesome Website',
description: 'Discover amazing content and services',
creator: '@cre8stevedev',
images: ['https://myawesomewebsite.com/twitter-image.jpg'],
},
// Verification for search engines
// You can get these values from the respective
// search engines when you submit your site for
// indexing
verification: {
google: 'google-site-verification=1234567890',
yandex: 'yandex-verification=1234567890',
yahoo: 'yahoo-site-verification=1234567890',
},
// Alternate languages
alternates: {
canonical: 'https://myawesomewebsite.com',
languages: {
'en-US': 'https://myawesomewebsite.com/en-US',
'es-ES': 'https://myawesomewebsite.com/es-ES',
},
},
// Icons
icons: {
icon: '/favicon.ico',
shortcut: '/favicon-16x16.png',
apple: '/apple-touch-icon.png',
other: [
{
rel: 'apple-touch-icon-precomposed',
url: '/apple-touch-icon-precomposed.png',
},
],
},
// Manifest
manifest: '/site.webmanifest',
// App-specific metadata
appleWebApp: {
capable: true,
title: 'My Awesome App',
statusBarStyle: 'black-translucent',
},
// Robots directives
robots: {
index: true,
follow: true,
nocache: true,
googleBot: {
index: true,
follow: true,
noimageindex: true,
'max-video-preview': -1,
'max-image-preview': 'large',
'max-snippet': -1,
},
},
// Format detection
formatDetection: {
email: false,
address: false,
telephone: false,
},
};
别跑,别跑!不,你不需要在页面上使用所有这些属性。它们只是作为参考,帮助你创建满足自身需求的元数据对象。可以说,许多属性都相当直观易懂。OpenGraph 对象使用 OpenGraph 协议,允许你的应用在社交分享时显示链接、图片和描述的预览。此外,还有一个专门用于 Twitter 元数据的对象属性。
让我们直接进入正题——如何在你的项目中使用它
请记住,如果您有希望嵌套页面共享的静态元数据,只需将元数据对象放在最顶层的组件中(在本例中,该组件是layout.tsx路由结构中的文件)。例如:
my-nextjs-project/
│
├── app/
│ ├── (auth)/
│ │ ├── signin/
│ │ │ └── page.tsx
│ │ ├── signup/
│ │ │ └── page.tsx
│ │ └── layout.tsx
│ │
│ ├── about/
│ │ └── page.tsx
│ ├── api/
│ │ ├── post/
│ │ │ ├── [slug]/
│ │ └── route.ts
│ ├── layout.tsx
│ ├── robots.ts
│ ├── sitemap.ts
│ └── page.tsx
├── public/
│ ├── favicon.ico
│ └── ...
│
├── components/
│ └── ...
│
├── lib/
│ └── customMetaDataGenerator.ts
│
├── styles/
│ └── globals.css
│
├── package.json
├── next.config.js
├── tsconfig.json
└── README.md
请注意上面的项目结构,我们将参考它来了解您的文件/配置的位置。
创建可重用的元数据函数
让我们创建一个实用函数,它接收一个属性对象,我们可以向该对象传递自定义值,以便在任何页面上生成元数据。我们将调用这个自定义函数,并将其赋值给名为“metadata”的变量对象,该对象将从页面导出。
// File location: @/lib/customMetaDataGenerator.ts
import { Metadata } from 'next';
interface PageSEOProps {
title: string;
description?: string;
canonicalUrl?: string;
ogType?: string;
ogImage?: string;
twitterCard?: string;
keywords?: string[];
}
export function customMetaDataGenerator({
title,
description = "Join the vibrant community that's bringing Nigerians together like never before...",
canonicalUrl = 'https://naijarium.vercel.app',
ogType = 'website',
keywords = [
"an array", "of default", "keywords"
],
ogImage = 'https://url-to-your-image-this-is-a-default-value-for-optional-parameter',
twitterCard = 'summary_large_image',
}: PageSEOProps): Metadata {
// Create Site Title
const siteTitle = 'Your Website Name';
const fullTitle = `${title} | ${siteTitle}`;
return {
title: fullTitle,
description,
keywords: keywords.join(', '),
openGraph: {
title: fullTitle,
description,
type: ogType,
url: canonicalUrl,
images: [
{
url: ogImage,
},
],
},
twitter: {
card: twitterCard,
title: fullTitle,
description,
images: [ogImage],
},
alternates: {
canonical: canonicalUrl,
},
};
}
在根布局中使用自定义函数
// @/app/layout.tsx
import { customMetaDataGenerator } from '@/lib/customMetaDataGenerator';
// Define Metadata for the general site layout
// We're relying on the default parameters defined in the function,
// That's why we're only passing `title` in the object
export const metadata: Metadata = customMetaDataGenerator({
title: 'Social Media Forum for Nigerians',
});
...
// The rest of your layout.tsx code follows.
你也可以在所有 page.tsx 文件中针对各个路由执行此操作——如果你有静态数据,例如包含静态数据的多页面网站,这将非常有用。但如果你要将其应用于动态网站(例如包含不同项目的作品集、博客、电子商务网站等),该怎么办呢?那么你需要能够生成特定于请求资源的数据,并在服务器端构建元数据。
生成动态元数据
请注意,要让搜索引擎能够使用元数据,就必须在服务器端运行。因此,如果您的page.tsx客户端组件使用了“use client”指令,那么次优方案是将元数据生成函数放在layout.tsx该路由段中,这样就可以在服务器端生成元数据并返回给客户端组件。
例如,当你generateMetadata在 `<directory> page.tsx` 或`<directory>` 标签中导出名为 `<function>` 的函数时,例如,当一个动态路由接受一个`<params>` 作为参数时,NextJS 会在服务器端自动调用该函数,并且该函数还可以访问该路由的参数。因此,你可以使用 `<slug>` 来获取动态资源的数据,并构建将要返回给该页面的元数据对象。layout.tsxslug
以下是创建 URL 动态元数据的示例:https://naijarium.vercel.app/post/how-to-become-a-full-stack-dev
// @/app/post/[slug]/layout.tsx
import { customMetaDataGenerator } from '@/lib/customMetaDataGenerator';
import { Metadata } from 'next';
import { fetchSinglePost } from '@/lib/fetchSinglePost';
type Props = {
params: { slug: string };
children: React.ReactNode;
};
// This function will be called and it will generate
// The metadata object for the page when the route is visited
export async function generateMetadata({ params }: Props): Promise<Metadata> {
// Fetch the post data using the slug
// Implement your own custom data fetch logic
// That returns the resource
const post = await fetchSinglePost(params.slug);
if (!post) {
return customMetaDataGenerator({
title: 'Post Not Found | Naijarium',
});
}
// Generate the metadata using the fetched post data
return customMetaDataGenerator({
title: post.title!,
description: ` Created by: ${post.author_username} - ${post.content).slice(0, 150)} + "...Read More`,
ogImage: post.image,
keywords: post.keywords,
canonicalUrl: `https://your-website.com/post/${post.slug}`
});
}
// Export the layout component that returns the nested page (s)
export default function Layout({ children }: Props) {
return <>{children}</>;
}
机器人程序和站点地图
网站元数据非常重要,对很多方面都很有帮助。但如果您希望搜索引擎能够索引和抓取您的网页,那么您还需要两个重要的文件——robots.txt 和 sitemap.xml。
robots.ts
- 生成 robot.txt 文件
- 控制搜索引擎爬虫
- 定义哪些页面应该被索引,哪些页面不应该被索引。
- 可以屏蔽特定的网络爬虫,也可以允许所有爬虫。
sitemap.ts
- 生成 sitemap.xml 文件
- 列出您网站上的所有重要页面
- 帮助搜索引擎理解您的网站结构
- 可以包含页面更新信息、重要性和更新频率。
它的妙处在于,如果您将 robots.txt 和 sitemap.xml 文件放在应用程序目录中,NextJS 会自动处理它们的创建robots.ts,sitemap.ts从而无需手动管理即可提高网站的 SEO。
示例robots.ts文件:
import { MetadataRoute } from 'next';
export default function robots(): MetadataRoute.Robots {
return {
rules: {
userAgent: '*', // allow all crawlers
allow: '/', // allow crawling all pages
disallow: ['/api/', '/api'], // don't crawl api routes
},
sitemap: 'https://your-website.com/sitemap.xml',
};
}
示例sitemap.ts文件
import getAllPosts from '@/actions/getAllPosts';
import { MetadataRoute } from 'next';
export default async function sitemap(): Promise<MetadataRoute.Sitemap> {
const baseUrl = 'https://your-website.com';
const posts = await getAllPosts();
const postEntries = posts.map((post) => ({
url: `${baseUrl}/blog/${post.slug}`,
lastModified: new Date(post.updatedAt!),
changeFrequency: 'weekly' as const,
priority: 0.8,
}));
return [
{
url: baseUrl,
lastModified: new Date(),
changeFrequency: 'daily',
priority: 1,
},
...postEntries,
];
}
这是怎么回事?现在,您可以手动列出网站中想要添加到站点地图的页面,但如果您有一个动态网站(例如博客或电子商务网站),您可能需要根据您拥有的资源生成页面链接。
您只需返回一个包含站点地图类型的数组。搜索引擎将使用此信息来了解您的网站/应用程序的结构。
最后一步,只剩一点点了。
该next-sitemap软件包是一个用于 Next.js 应用程序的工具,它具有以下功能:
- 自动生成站点地图和 robots.txt 文件
- 支持静态和动态站点地图
- 允许自定义配置
- 可以为大型应用程序生成站点地图
- 可以集成到构建过程中
如何使用?
next-sitemap.config.ts在项目根目录下创建一个文件,并将以下内容添加到该文件中。
module.exports = {
siteUrl: 'https://your-website-url.com',
generateRobotsTxt: true,
};
更新脚本对象package.json以包含
{
"name": "your-project-name",
"version": "0.1.0",
"private": true,
"scripts": {
"dev": "next dev",
"build": "next build",
"start": "next start",
"lint": "next lint",
"postbuild": "next-sitemap"
},
// ...other configurations here
}
总结一下,呃……再见!
呼!现在一切就绪,您可以在开发者工具中检查您的网站,或者分享您网站的链接,以查看链接预览是否已生效(当然是在部署之后,而不是本地主机链接)。
尽情享受开发过程吧!当你需要在 NextJS 应用中实现 SEO 时,随时可以参考本指南。
注意:除了页面加载速度、响应速度和内容相关性之外,还有许多其他因素会影响页面排名。但至少 NextJS 让你能够利用 ReactJS 组件构建 UI 的灵活性,同时又不影响搜索引擎对网站的抓取能力。
祝您编程愉快!史蒂夫向您致以问候!
其他资源:
文章来源:https://dev.to/cre8stevedev/practical-guide-to-implementing-functioning-seo-in-nextjs-app-router-static-dynamic-metadata-4ae2