برنامه نویسی

با استفاده از 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. جلوتر رفتن


امیدوارم این پست به شما انگیزه داده باشد تا یک وبلاگ باورنکردنی بسازید! کار خود را در بخش نظرات به اشتراک بگذارید! 💬

آه و اگر محتوای بیشتری مانند این می خواهید، من را دنبال کنید:

نوشته های مشابه

دیدگاهتان را بنویسید

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *

دکمه بازگشت به بالا