ساخت یک سایت دایرکتوری ساده به نظر می‌رسد، تا زمانی که خود را در حال اتصال پایگاه‌های داده، لایه‌های کشینگ و فریم‌ورک‌های فرانت‌اند واکنش‌گرا (reactive) می‌بینید، آن هم فقط برای نمایش چیزی که در اصل یک فهرست مطالب منتخب است. من اخیراً Social Tools List را ساختم، سایتی برای مقایسه نرم‌افزارهای رسانه‌های اجتماعی. هدف من این بود که آن را سریع آنلاین کنم، سرعت بالایی داشته باشد و از نگهداری زیرساخت‌هایی برای محتوایی که فقط با اضافه یا به‌روزرسانی یک ابزار تغییر می‌کند، اجتناب کنم. من یک پشته (stack) با اولویت استاتیک را انتخاب کردم: Astro برای تولید سایت، TypeScript برای داده‌های ساختاریافته و Cloudflare Workers برای استقرار (deployment). نتیجه، سایتی است که فوراً بارگذاری می‌شود، هزینه میزبانی آن تقریباً صفر است و نیازی به مدیریت پایگاه داده ندارد.

چرا رویکرد «اول-استاتیک» برای یک دایرکتوری منطقی است

بسیاری از اپلیکیشن‌های وب به طور پیش‌فرض از رندرینگ سمت سرور یا معماری‌های تک‌صفحه‌ای (SPA) استفاده می‌کنند، زیرا آن‌ها را انتخاب‌هایی امن و مدرن می‌دانند. اما هر سایتی در هر درخواست، ورودی‌های پویا از کاربر دریافت نمی‌کند. Social Tools List یک منبع با خوانش بالا (read-heavy) است. داده‌های مقایسه‌ای زمانی تغییر می‌کنند که من یک به‌روزرسانی ارسال کنم، نه زمانی که یک بازدیدکننده صفحه را رفرش کند. رندر کردن HTML از قبل، نیاز به پرس‌وجوهای پایگاه داده در لبه (edge)، کامپایل قالب‌ها در لحظه، یا سربار هیدریشن (hydration) در مرورگر را از بین می‌برد. من سایت را در زمان بیلد (build time) تولید می‌کنم، فایل‌های استاتیک را مستقر می‌کنم و اجازه می‌دهم یک worker سبک، مدیریت پوشش (wrapper) را بر عهده بگیرد. این کار زمان پاسخگویی را پایین نگه می‌دارد و یک دسته کامل از خطاهای زمان اجرا (runtime failures) را حذف می‌کند.

داده‌ها را در TypeScript ذخیره کنید، نه در یک پایگاه داده

من از پایگاه داده استفاده نمی‌کنم. هر ابزار در دایرکتوری به عنوان یک شیء TypeScript با slug، نام، دامنه و آرایه‌ای از جریان‌های کاری (workflows) پشتیبانی‌شده تعریف شده است. یک ورودی معمولی به این صورت است:

{
  slug: 'buffer',
  name: 'Buffer',
  domain: 'buffer.com',
  workflows: ['scheduling', 'analytics']
}

ذخیره داده‌ها در همان زبانی که سایت را می‌سازد، دو مزیت فوری دارد. اول، درخواست‌های Pull Request به بازبینی محتوا تبدیل می‌شوند. وقتی ابزاری اضافه می‌کنم، تفاوت‌ها (diff) فیلدها و مقادیر دقیق را نشان می‌دهد و یک همکار می‌تواند بدون یادگیری رابط کاربری یک CMS، یک غلط تایپی یا یک دامنه اشتباه را تشخیص دهد. دوم، کامپایلر TypeScript ساختار هر رکورد را تحمیل می‌کند. اگر فراموش کنم یک slug را اضافه کنم یا کلید یک workflow را اشتباه تایپ کنم، بیلد قبل از اینکه داده‌های نادرست به یک صفحه برسند، با شکست مواجه می‌شود.

یک پایگاه داده باعث ورود مهاجرت‌ها (migrations)، رشته‌های اتصال (connection strings)، استراتژی‌های کشینگ و روال‌های پشتیبان‌گیری می‌شود. برای دایرکتوری‌ای با چند صد ورودی که من به صورت دستی آن‌ها را مدیریت می‌کنم، این سربار صرفاً یک مانع است. داده‌های استاتیک در ماژول‌های TypeScript ارزان‌ترین انتخاب درست برای این مدل است. بخش «درست» بودن آن مهم است. موضوع فقط صرفه‌جویی در هزینه نیست؛ بلکه حذف لایه‌های انتزاعی (abstraction layers) است که مشکلاتی را حل می‌کنند که من اصلاً ندارم.

اجازه دهید Astro مسئولیت مسیریابی و رندرینگ را بر عهده بگیرد

Astro از طریق یک مسیر پویا (dynamic route) واحد، برای هر ابزار یک صفحه HTML تولید می‌کند. من یک کامپوننت Layout تعریف می‌کنم و Astro به طور خودکار متادیتا، تیترها و داده‌های ساختاریافته را برای هر ورودی تولید می‌کند. از آنجایی که همان مجموعه داده، ایندکس اصلی، هاب‌های دسته‌بندی جریان کاری و صفحات جزئیات فردی را هدایت می‌کند، هیچ احتمالی وجود ندارد که یک کارت در صفحه اصلی، توضیحی متفاوت از خود صفحه جزئیات نشان دهد. در تنظیمات سنتی CMS، اغلب شاهد ناهماهنگی هستید: API یک نسخه را برمی‌گرداند، کش نسخه‌ای دیگر و رندر سمت کلاینت نسخه‌ای سوم. تولید استاتیک از یک منبع واحد حقیقت (single source of truth)، از این اتفاق جلوگیری می‌کند.

معماری جزیره‌ای (island architecture) در Astro همچنین اضافه کردن قطعات تعاملی کوچک را بدون آلوده کردن کل صفحه با JavaScript آسان می‌کند. سایت به صورت HTML استاتیک ارسال می‌شود و فقط اسکریپت فیلتر کردن، بخش خاص خود از DOM را هیدراته می‌کند. هیچ زمان اجرای فریم‌ورکی (framework runtime) کل سند را در بر نمی‌گیرد. Astro همچنین با متادیتای صفحه به عنوان یک موضوع درجه اول برخورد می‌کند. هر صفحه ابزار، تگ عنوان و متادیسکریپشن مخصوص به خود را دریافت می‌کند که مستقیماً از رکورد TypeScript مشتق شده است، بنابراین نیازی به پلاگین جداگانه یا کتابخانه مدیریت head ندارم.

فیلتر کردن بدون نیاز به یک فریم‌ورک

جستجو و فیلتر کردن اغلب توسعه‌دهندگان را ترغیب می‌کند تا React، Vue یا یک کتابخانه سنگین مدیریت وضعیت (state management) را نصب کنند. من در برابر این کار مقاومت کردم. Astro لیست کامل را به عنوان HTML ساده در سمت سرور رندر می‌کند. یک اسکریپت کوچک vanilla JavaScript، با حجمی کمتر از یک کیلوبایت، در مرورگر اجرا می‌شود و ویژگی display آیتم‌های لیست را بر اساس یک تگ workflow یا تطابق متن، تغییر می‌دهد.

اگر از پس‌زمینه مبتنی بر API می‌آیید، ارسال کامل نشانه‌گذاری (markup) ممکن است ناکارآمد به نظر برسد. اما بار اضافی (overhead) یک رویکرد پویا و معمول را در نظر بگیرید. مرورگر یک باندل JavaScript را دانلود می‌کند، یک درخت کامپوننت را هیدراته (hydrate) می‌کند، یک endpoint را فراخوانی می‌کند، منتظر JSON می‌ماند و سپس ردیف‌ها را رندر می‌کند. برای دایرکتوری‌ای که کمتر از صد ابزار را لیست می‌کند، این فرآیند طولانی‌تر و کم‌اعتمادتر از مخفی کردن divهایی است که از قبل در سند (document) وجود دارند. اسکریپت من event listenerهایی را به دکمه‌های فیلتر متصل می‌کند، یک ویژگی data-workflow را در هر ردیف می‌خواند و موارد غیر‌مطابق را روی hidden تنظیم می‌کند. این عملیات تنها چند میلی‌ثانیه زمان می‌برد.

از آنجایی که لیست در HTML اولیه موجود است، سایت بدون JavaScript نیز قابل استفاده است. خزنده‌های موتورهای جستجو تمام لینک‌ها و توضیحات را می‌بینند. کاربران با شبکه‌های کند یا کسانی که از مسدودکننده‌های اسکریپت استفاده می‌کنند، همچنان به دایرکتوری کامل دسترسی دارند. فیلتر کردن یک قابلیت تکمیلی است، نه یک مانع.

نقشه‌های سایت (Sitemaps) و robots.txt به عنوان کد

Sitemaps و robots.txt ایده‌هایی نیستند که بعداً و به صورت دستی نوشته شوند. آن‌ها مسیرهای (routes) Astro هستند که از همان مجموعه داده (dataset) و کمکی‌های URL استفاده می‌کنند که