سایت شخصی با Next.js و Payload؛ مدل را قبل از صفحه اول ببند
سایت شخصی تا وقتی چند صفحه ثابت است با مارکداون راه میافتد. وقتی پیشنویس، زمانبندی، رسانه، فیلد سئو، و ادمینی که خودت واقعاً استفاده کنی وارد میشوند، مارکداون کم میآورد. mehd.ir را برای همین روی Next.js و Payload CMS دوباره ساختم. هم نمونهکار باشد هم سیستم انتشار.
Next.js سمت عمومی را پوشش میدهد: App Router، React Server Component، بهینهسازی تصویر، و استقرار بدون یک داستان جدا برای عملیات فرانت. Payload سمت تحریریه را: کالکشن TypeScript، ادمین محلی، کنترل دسترسی، و بدنه Lexical در Postgres. خواستم یک ریپو باشد. مسیر فرانت و schema کنار هم بمانند تا با عوض شدن فیلد، TypeScript هر دو طرف غر بزند.
مدل قبل از ظاهر صفحه اول
کالکشن را قبل از صیقل صفحه خانه تعریف کردم.
پست: عنوان، اسلاگ، `content` از نوع Lexical، تصویر هیرو، `publishedAt`، عنوان و توضیح متا، دسته، نویسنده، و `_status`.
دسته و کاربر و رسانه: کالکشن نازک پشتیبان. صفحه فقط جایی که چیدمان، پست نیست.
اشتباهی که نزدیک بود بکنم این بود که صفحه خانه را در یک فیلد بدنه غول CMS بریزم. بلاک ساختیافته و رابطه، فهرست را سریع نگه میدارد و فیلد سئو را درجه یک میکند. اگر همهچیز یک JSON آزاد باشد، هم کوئری فهرست چاق میشود هم ویرایش یک بخش کوچک یعنی دست زدن به کل سند.
برای سایت شخصی وسوسه مدل «انعطاف کامل» قوی است چون تنها ویراستار خودتی و محدودیت را دستوپاگیر میبینی. شش ماه بعد همان انعطاف یعنی یادت نیست هیرو کجا پر میشود و چرا یک پست در فهرست توضیح ندارد. فیلد صریح، حافظه را از سر تو به schema منتقل میکند.
سیمکشی App Router
مسیر عمومی، پست منتشرشده را از Local API خود Payload یا کوئری کششده میخواند. ادمین روی پنل Payload میماند. پیشنمایش پیشنویس مهم است. ویراستار باید پست منتشرنشده را ببیند بدون اینکه `_status` را منتشر کند. اگر تنها راه دیدن نتیجه، انتشار باشد، یا محتوای نیمهکاره زنده میرود یا عادت پیشنمایش میمیرد.
برای فهرست فقط فیلد کارت را میآورم: عنوان، اسلاگ، تاریخ، توضیح، هیرو. JSON کامل Lexical را برای صفحه اندیس نمیکشم. همین یک عادت، با رشد بایگانی، Postgres و زمان تا اولین بایت را راستگو نگه میدارد. صفحه مقاله جداگانه سند را میگیرد. این دو کوئری را یکی نکن چون «هر دو پستاند».
باطلسازی را به اسلاگ ببند تا بعد از بهروزرسانی در CMS، کش ISR یا CDN بدنه کهنه ندهد. ذخیره سبز در ادمین بهتنهایی انتشار نیست. معیار من یک URL زنده است که عنوان مورد انتظار را با پاسخ موفق برگرداند.
رسانه و سئو
هر پست یک هیرو دارد که هم در چیدمان مقاله میآید هم در مسیر تصویر OG. `meta.title` و `meta.description` را جایی که لازم است از H1 جدا نگه دار. عنوان صفحه برای کلیک در نتایج جستجو همیشه همان عنوان مقاله نیست. اسلاگ را بعد از انتشار سبک عوض نکن. رتبهای که ساخته شده به آدرس چسبیده، نه به سلیقه امروز تو.
تصویر از `Image` در Next.js رد میشود با `sizes` صریح. هیرو بدون ابعاد، روی نمونهکار یک تله ساده برای پرش چیدمان است. CLS را با انیمیشن جبران نکن. جا را از اول نگه دار.
محیط و عادتهایی که تکرار میکنم
پروداکشن به `POSTGRES_URL`، `PAYLOAD_SECRET`، و URL عمومی سرور نیاز دارد. راز را در کلاینت نگذار و Local API را از کامپوننت کلاینت صدا نزن. مسیر خواندن عمومی روی سرور بماند.
چیزی که تکرار میکنم: اول schema، بعد UI. کوئری کارت جدا از کوئری مقاله. بازتولید بخشی از انتشار باشد نه فکر بعدی. ادمین را خستهکننده نگه دار تا نوشتن آسان بماند.
چیزی که دورش را خط میکشم: یک بدنه بیساختار برای کل سایت. کشیدن کل بایگانی با fetch کلاینت روی صفحه خانه. عوض کردن اسلاگ بعد از اینکه دیده شده. حذف فیلد متا به این بهانه که «عنوان کافی است».
Next.js و Payload برای سایت شخصی جفت قویای هستند اگر CMS را زیرساخت محصول بدانی نه تزئین. خود پشته موضوع نیست. سایتی موضوع است که عصر جمعه بشود بیخطر بهروزش کرد.
پیشنمایش و کش را با هم تمرین کن
یک بار مسیر واقعی را از ادمین تا URL عمومی برو. پیشنویس را ذخیره کن و مطمئن شو فهرست عمومی آن را نشان نمیدهد. منتشر کن و ببین همان اسلاگ بدون انتظار برای انقضای تصادفی کش، عنوان جدید را میدهد. اگر باطلسازی به اسلاگ بند است، عوض کردن اسلاگ یعنی دو آدرس: آدرس قدیم که باید بمیرد یا ریدایرکت شود، و آدرس جدید که باید گرم شود. این را در چکلیست انتشار بنویس نه در حافظه.
رسانه را هم جزو همین مسیر بدان. آپلود هیرو بدون alt قابل فهم، یا بدون اندازه، باگ محتواست نه جزئیات طراحی. چون سایت شخصی معمولاً یک نفره است، هیچ بازبین دومی این را نمیگیرد مگر اینکه خود فرم ادمین فیلد را جدی کرده باشد.
Local API را در تابع سرور نگه دار و نتیجه را به شکل باریک به کلاینت بده. کامپوننت کلاینت که خودش فهرست پست را از CMS میکشد، هم راز و هم شکل کوئری را به مرورگر میبرد. برای بایگانی کوچک هم این عادت بد است، چون همان کد بعدها با صد سند تکرار میشود.
پرسشهای کوتاه
مارکداون را کی کنار بگذارم؟ وقتی پیشنویس، رسانه، و فیلد متا دیگر استثناء نیستند و برای هر کدام یک راه میانبر جدا ساختهای.
همه صفحهها باید کالکشن جدا باشند؟ نه. صفحه را برای چیدمانی بگذار که پست نیست. بقیه را پست یا بلاک نگه دار.
فهرست را هم با همان کوئری مقاله پر کنم؟ نه. فیلد کارت کافی است. بدنه Lexical در فهرست، هزینه هر بازدید را به اندازه خواندن مقاله میکند.