با استفاده از Next.JS و DEV.to یک وبلاگ بسازید

در این مقاله یاد خواهید گرفت که چگونه یک وبلاگ Next.JS با واکشی مستقیم پست های خود از DEV.to بسازید.
من یک بازخورد باورنکردنی از Post Use Notion به عنوان پایگاه داده برای وبلاگ Next.JS دریافت کردم با تشکر از همه شما 🙌
من حتی پست را در صفحه اول daily.dev دیدم 😶
امروز می خواستم با شما به اشتراک بگذارم که چگونه وبلاگ شخصی خود را در کمتر از یک ساعت با استفاده از DEV.to API ساختم.
بیایید شروع کنیم 🔥
1. یک برنامه Next.JS جدید ایجاد کنید
با استفاده از ابزار create-next-app با استفاده از مدیر بسته مورد علاقه خود شروع کنید.
$ npx create-next-app@latest
$ yarn create next-app
$ pnpm create next-app
بررسی کنید برای همه چیز! ما لینتینگ، تایپ و بدیهی است که App Router می خواهیم! من همچنین استفاده از آن را به شدت توصیه می کنم
src
پوشه
2. وابستگی ها را نصب کنید
شما به 9 وابستگی نیاز دارید:
- تذکر: ما از آن برای تجزیه علامت گذاری پست های خود استفاده خواهیم کرد
- remark-html: یک پلاگین Remark برای تبدیل Markdown ما به HTML
- بازگو کردن: کتابخانه ای برای پردازش و گسترش HTML
- rehype-highlight: یک پلاگین Rehype برای وصل کردن highlight.js برای برجسته کردن کد ما
- rehype-slug: یک پلاگین Rehype که شناسه ها را به عناوین پست ما اضافه می کند (لنگرها)
- @jsdevtools/rehype-toc: یک افزونه Rehype که جدولی از محتوا را بر اساس عناوین پست ما تولید می کند
- rehype-stringify: یک پلاگین Rehype که خروجی Rehype ما را به یک رشته تبدیل می کند
- تذکر-بازگفت: یک پلاگین Remark برای استفاده از Remark و Rehype در سیمبیوز
- یکپارچه: این کتابخانه برای آسان کردن استفاده از همه این افزونه ها با هم
$ npm install remark remark-html rehype rehype-highlight rehype-slug @jsdevtools/rehype-toc rehype-stringify remark-rehype unified
$ yarn add remark remark-html rehype rehype-highlight rehype-slug @jsdevtools/rehype-toc rehype-stringify remark-rehype unified
3. واکشی از DEV.to
برای ارائه یک API عمومی فوق العاده که نیازی به احراز هویت ندارد، می توانید اسناد رسمی را در اینجا بیابید.
در مورد ما به دو چیز نیاز داریم:
- دریافت پست های ما
/api/articles?username=<username>
- واکشی یک پست خاص
/api/articles/<username>/<post-slug>
اضافه کردن متغیرهای محیطی
اگر میخواهید نام کاربری یا منبع باز وبلاگ خود را تغییر دهید، اجتناب از مقادیر کدگذاری سخت است.
DEV.to خود را به نام کاربری اضافه کنید .env.local
برای جلوگیری از هاردکد کردن آن:
DEVTO_USERNAME="martinp"
تایپ ها را اضافه کنید
بیایید چند تایپ برای تایپ پاسخ DEV.to API اضافه کنیم src/lib/devto/types.ts
:
// src/lib/devto/types.ts
export type User = {
user_id: number;
name: string;
username: string;
twitter_username: string | null;
github_username: string | null;
website_url: string | null;
profile_image: string;
profile_image_90: string;
};
export type Post = {
type_of: string;
id: number;
title: string;
description: string;
readable_publish_date: string;
slug: string;
path: string;
url: string;
comments_count: number;
collection_id: number | null;
published_timestamp: string;
positive_reactions_count: number;
cover_image: string | null;
social_image: string;
canonical_url: string;
created_at: string;
edited_at: string;
crossposted_at: string | null;
published_at: string;
last_comment_at: string;
reading_time_minutes: number;
tag_list: string[];
tags: string;
user: User;
};
export type PostDetails = Post & {
body_html: string;
body_markdown: string;
tags: string[];
};
من این انواع را به صورت دستی ساختم و شاید دقیقاً با API واقعی مطابقت نداشته باشند، به راحتی آنها را به روز کنید.
توابع واکشی را ایجاد کنید
بعد یک فایل جدید ایجاد کنید src/lib/devto/fetch.ts
، حاوی توابعی است که API را واکشی می کند. جدا کردن آنها از برنامه خود تمرین خوبی است تا به راحتی قابل استفاده مجدد باشند.
// src/lib/devto/fetch.ts
import { notFound } from "next/navigation";
import { Post, PostDetails } from "./types";
export async function fetchPosts(): Promise<Post[]> {
const res = await fetch(
`https://dev.to/api/articles?username=${process.env.DEVTO_USERNAME}`,
{
next: { revalidate: 3 * 60 * 60 },
}
);
if (!res.ok) notFound();
return res.json();
}
export async function fetchPost(slug: string): Promise<PostDetails> {
const res = await fetch(
`https://dev.to/api/articles/${process.env.DEVTO_USERNAME}/${slug}`,
{
next: { revalidate: 3 * 60 * 60 },
}
);
if (!res.ok) notFound();
return res.json();
}
توجه داشته باشید که پارامتر را اضافه می کنیم revalidate: 3 * 60 * 60
. به طور پیش فرض fetch
تابع توسعه یافته توسط Next.JS همه چیز را کش می کند. این وبلاگ شما را بسیار سریع می کند، اما ما همچنین می خواهیم وبلاگ خود را به روز نگه داریم. با این پارامتر به Next.JS می گوییم که حافظه نهان را هر 3 ساعت یکبار اعتبار سنجی کند.
notFound()
مانند یک بازگشت عمل می کند و نشان خواهد دادnot-found.tsx
صفحه اطلاعات بیشتر در اینجا.
4. تابع رندر را ایجاد کنید
حالا بیایید یک تابع ایجاد کنیم تا با استفاده از Remark، Rehype و همه افزونهها، محتوای پستهای شما را ارائه کند:
// src/lib/markdown.ts
import toc from "@jsdevtools/rehype-toc";
import rehypeHighlight from "rehype-highlight";
import rehypeSlug from "rehype-slug";
import rehypeStringify from "rehype-stringify";
import remarkRehype from "remark-rehype";
import remarkParse from "remark-parse";
import { unified } from "unified";
export function renderMarkdown(markdown: string): Promise<string> {
return unified()
.use(remarkParse)
.use(remarkRehype)
.use(rehypeHighlight, { ignoreMissing: true })
.use(rehypeSlug)
.use(rehypeStringify)
.use(toc, {
headings: ["h1", "h2", "h3"],
})
.process(markdown)
.then((res) => res.toString());
}
5. صفحات را ایجاد کنید
اکنون که همه چیز را برای واکشی پست های خود در اختیار دارید، زمان ایجاد صفحات است!
صفحه پست ها
ساده تر از این نمی تواند باشد، به سادگی از خود استفاده کنید fetchPosts
عملکرد و نشان دادن آنها:
// src/app/blog/page.tsx
import { fetchPosts } from "@/lib/devto/fetch";
export default async function Page() {
const posts = await fetchPosts();
return (
<div className="grid grid-cols-1 md:grid-cols-2 gap-4 py-8">
{posts.map((post) => (
<Link href={`/blog/${post.slug}`}>{post.title}</Link>
))}
</div>
);
}
صفحه پست
یک صفحه جدید با یک بخش پویا برای اسلاگ ما ایجاد کنید src/app/blog/[slug]/page.tsx
.
از پارامترها برای واکشی پست استفاده کنید و از آن استفاده کنید renderMarkdown
عملکردی برای تبدیل Markdown شما به HTML.
همچنین می توانید اضافه کنید generateMetadata
برای تنظیم عنوان و توضیحات با استفاده از داده های پست خود.
// src/app/blog/[slug]/page.tsx
import 'highlight.js/styles/github-dark.css'; // Import your favorite highlight.js theme
import { fetchPost, fetchPosts } from "@/lib/devto/fetch";
import { renderMarkdown } from "@/lib/markdown";
export async function generateMetadata({
params,
}: {
params: { slug: string };
}) {
const { title, description } = await fetchPost(params.slug);
return {
title,
description,
};
}
export default async function Page({ params }: { params: { slug: string } }) {
const { body_markdown } = await fetchPost(params.slug);
const content = await renderMarkdown(body_markdown);
return (
<>
<article>
<div dangerouslySetInnerHTML={{ __html: content }} />
</article>
</>
);
}
توجه داشته باشید که شما دو بار تماس می گیرید fetchPost
روش، پس دوبار واکشی می کنید؟ نه! از حافظه نهان استفاده میکند، میتوانید هنگام اجرای آن، آن را تأیید کنید dev
سرور، باید ببینید cache: HIT
.
و می دانید چه چیزی واقعاً جالب است؟
به لیست پست های خود بروید و پیوندها را نگه دارید، باید در کنسول خود مشاهده کنید /blog/[slug]
پیش رندر صفحات برای پیش بینی ناوبری کاربر 🔥
6. جلوتر رفتن
امیدوارم این پست به شما انگیزه داده باشد تا یک وبلاگ باورنکردنی بسازید! کار خود را در بخش نظرات به اشتراک بگذارید! 💬
آه و اگر محتوای بیشتری مانند این می خواهید، من را دنبال کنید: