本篇開始使用前一篇建立的 網站設定 表單來建立一些網站的基本樣式,像是 Navbar、Footer 、 metadata 等。
第一件要做的是老樣子,寫好 Query 語法:
// @/app/sanity/lib/queries.ts
export const WEBSITE_DATA_QUERY = defineQuery(`*[_type == "website"][0]`);
寫好後記得在 Sanity 專案內使用 typegen 後在回到 Next.js 專案內繼續開發
sanity schema extract --enforce-required-fields
sanity typegen generate
Navbar 的話作法很簡單,沒有什麼特殊的技巧,就是之前建立過的東西重新複習一次而已。
// @/app/components/Navbar.tsx
import { client } from "@/app/sanity/lib/client";
import { WEBSITE_DATA_QUERY } from "@/app/sanity/lib/queries";
import SanityImage from "@/app/components/SanityImage";
export default async function Navbar() {
const websiteData = await client.fetch(WEBSITE_DATA_QUERY);
return (
<div className="flex items-center justify-between py-4 border-b border-b-neutral-800">
<div className="flex items-center">
<SanityImage
className="mr-3"
source={websiteData?.logo || ""}
alt="Wayne's Blog Logo"
width={50}
height={50}
/>
<h2 className="text-2xl font-bold text-primary-400">
{websiteData?.title}
</h2>
</div>
</div>
);
}
接著在 layout 中引入就套入到全部頁面了:
// @/app/layout.tsx
// ...
import Navbar from "@/app/components/Navbar";
// ...
export default function RootLayout({
children,
}: Readonly<{
children: React.ReactNode;
}>) {
return (
<html lang="en">
<body
className={`${notoSans.variable} ${jetbrainsMono.variable} antialiased container mx-auto`} // 樣式微調
>
<Navbar /> {/* 引入 Navbar */}
{children}
</body>
</html>
);
}
這就沒什麼特別的了, favicon 使用 icon.tsx
來建立:
( 只要叫做 icon.tsx
,Next.js 預設就會從這個檔案的 export default 找到 icon 了 )
import { client, urlFor } from "@/app/sanity/lib/client";
import { WEBSITE_DATA_QUERY } from "@/app/sanity/lib/queries";
import { ImageResponse } from "next/og";
// Image metadata
export const size = {
width: 32,
height: 32,
};
export const contentType = "image/png";
export default async function Icon() {
const websiteData = await client.fetch(WEBSITE_DATA_QUERY);
const faviconUrl = urlFor(websiteData?.favicon || "")
.width(32)
.height(32)
.url();
return new ImageResponse(
<img src={faviconUrl} alt="Wayne's Blog favicon" width={32} height={32} />,
{
...size,
},
);
}
再來是一些基礎的 Metadata 頁面設定:
// .. 不動
export async function generateMetadata(): Promise<Metadata> {
const websiteData = await client.fetch(WEBSITE_DATA_QUERY);
const ogImageUrl = urlFor(websiteData?.ogImage || "").url();
return {
title: websiteData?.metaTitle,
description: websiteData?.metaDescription,
openGraph: {
title: websiteData?.metaTitle,
description: websiteData?.metaDescription,
images: {
url: ogImageUrl,
width: 770,
height: 480,
alt: websiteData?.metaTitle,
},
},
};
}
// ...不動