iT邦幫忙

2024 iThome 鐵人賽

DAY 23
0

本篇開始使用前一篇建立的 網站設定 表單來建立一些網站的基本樣式,像是 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

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>
  );
}

Metadata

這就沒什麼特別的了, 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,
      },
    },
  };
}

// ...不動

上一篇
Day 22 - Next.js 文章 Skeleton preview
下一篇
Day 24 - Next.js App Router 使用 NProgress 加入進度條
系列文
用 Sanity 跟 Nextjs 重寫個人部落格30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言