בניית אתר מדריך נשמעת פשוטה עד שמוצאים את עצמכם מחברים מסדי נתונים, שכבות caching ופריימוורקים ריאקטיביים לצד הלקוח רק כדי להציג מה שמהותית הוא תוכן עניינים שנבחר בקפידה. לאחרונה בניתי את Social Tools List, אתר להשוואת תוכנות למדיה חברתית. המטרה שלי הייתה להעלות אותו לאוויר במהירות, לשמור על מהירות גבוהה, ולהימנע מניהול תשתית עבור תוכן שמשתנה רק כשאני מוסיף או מעדכן כלי. בחרתי ב-stack מבוסס סטטי תחילה: Astro ליצירת האתר, TypeScript לנתונים מובנים, ו-Cloudflare Workers לפריסה. התוצאה היא אתר שנטען באופן מיידי, כמעט ואינו עולה כסף לאירוח, ואינו דורש ניהול מסדי נתונים.
למה גישת ה-Static-First הגיונית עבור אתר מדריך
אפליקציות אינטרנט רבות בוחרות כברירת מחדל ברינדור בצד השרת או בארכיטקטורות של דף יחיד (SPA) כי הן נראות כבחירות מודרניות ובטוחות. אך לא כל אתר מקבל קלט דינמי ממשתמשים בכל בקשה. Social Tools List הוא משאב עתיר קריאה. נתוני ההשוואה משתנים כשאני מעלה עדכון, לא כשאורח מרענן את הדף. רינדור HTML מראש מסיר את הצורך בשאילתות מסד נתונים ב-edge, קומפילציה של תבניות בזמן אמת, או עומס ה-hydration בדפדפן. אני מייצר את האתר בזמן הבנייה (build time), פורס את הקבצים הסטטיים, ומאפשר ל-worker קל משקל לטפל במעטפת. זה שומר על זמני תגובה נמוכים ומבטל קטגוריה שלמה של כשלי זמן ריצה (runtime).
אחסון נתונים ב-TypeScript, לא במסד נתונים
אני לא משתמש במסד נתונים. כל כלי במדריך מוגדר כאובייקט TypeScript עם slug, שם, דומיין ומערך של תהליכי עבודה (workflows) נתמכים. רשומה טיפוסית נראית כך:
{
slug: 'buffer',
name: 'Buffer',
domain: 'buffer.com',
workflows: ['scheduling', 'analytics']
}
לאחסון נתונים באותה שפה שבה האתר נבנה יש שני יתרונות מיידיים. ראשית, בקשות משיכה (pull requests) הופכות לבדיקות תוכן. כשמוסיפים כלי, ה-diff מראה את השדות והערכים המדויקים, וחבר צוות יכול להבחין בשגיאת כתיב או בדומיין שגוי מבלי ללמוד ממשק של CMS. שנית, הקומפיילר של TypeScript אוכף את המבנה של כל רשומה. אם אשכח לכלול slug או אטעה בשם של מפתח workflow, הבנייה תיכשל לפני שהנתונים השגויים יגיעו לדף.
מסד נתונים היה מביא איתו מיגרציות, מחרוזות חיבור (connection strings), אסטרטגיות caching ושגרות גיבוי. עבור מדריך עם כמה מאות רשומות שאני מנהל ידנית, העומס הזה הוא עיכוב מיותר. נתונים סטטיים במודולים של TypeScript הם הבחירה הנכונה והזולה ביותר למודל הזה. החלק של ה"נכון" חשוב. זה לא רק עניין של חיסכון בכסף; זה עניין של הסרת שכבות הפשטה (abstraction layers) שפותרות בעיות שאין לי.
לתת ל-Astro לנהל את הניתוב והרינדור
Astro מייצר דף HTML אחד לכל כלי מתוך נתיב דינמי יחיד. אני מגדיר רכיב layout אחד, ו-Astro מייצר באופן אוטומטי את המטא-דאטה, הכותרות והנתונים המובנים עבור כל רשומה. מכיוון שאותו סט נתונים מניע את האינדקס הראשי, את מרכזי קטגוריות ה-workflow ואת דפי הפירוט הבודדים, אין סיכוי שכרטיס בדף הבית יציג תיאור שונה מזה שמופיע בדף הפירוט עצמו. בהגדרות CMS מסורתיות, רואים לעיתים קרובות חוסר עקביות: ה-API מחזיר גרסה אחת, המטמון גרסה אחרת, ורינדור בצד הלקוח גרסה שלישית. יצירה סטטית ממקור אמת יחיד (single source of truth) מונעת זאת.
ארכיטקטורת ה-islands של Astro מקלה גם היא על הוספת רכיבים אינטראקטיביים קטנים מבלי להכביד על כל הדף ב-JavaScript. האתר נשלח כ-HTML סטטי, ורק סקריפט הסינון מבצע hydration לפינה הספציפית שלו ב-DOM. אין runtime של פריימוורק שעוטף את המסמך כולו. Astro מתייחס גם למטא-דאטה של הדף כאל נושא בעל עדיפות ראשונה. לכל דף כלי יש תגית title ותיאור meta משלו, הנגזרים ישירות מרשומת ה-TypeScript, כך שאין לי צורך בתוסף נפרד או בספריית ניהול head.
סינון ללא פריימוורק
חיפוש וסינון גורמים לעיתים קרובות למפתחים להתקין את React, Vue או ספריית ניהול state כבדה. התנגדתי לכך. Astro מרנדר את הרשימה המלאה כ-HTML פשוט בשרת. סקריפט vanilla JavaScript קטן, פחות מקילובייט אחד, רץ בדפדפן ומחליף את מאפיין ה-display של פריטי הרשימה בהתבסס על תגית workflow או התאמת טקסט.
הגשת ה-markup המלא נראית לא יעילה אם מגיעים מרקע של עבודה מבוססת API. אך כדאי לשקול את ה-overhead של גישה דינמית טיפוסית. הדפדפן מוריד JavaScript bundle, מבצע hydration לעץ הקומפוננטות, קורא ל-endpoint, ממתין ל-JSON, ואז מרנדר שורות. עבור ספרייה המציגה פחות ממאה כלים, הטקס הזה איטי ופחות אמין מאשר פשוט להסתיר divs שכבר נמצאים במסמך. הסקריפט שלי מצמיד event listeners לכפתורי הסינון, קורא attribute מסוג data-workflow בכל שורה, ומגדיר את האיברים שלא תואמים כ-hidden. הפעולה אורכת מילישניות.
מכיוון שהרשימה קיימת ב-HTML הראשוני, האתר ניתן לשימוש גם ללא JavaScript. סורקי מנועי חיפוש רואים כל קישור וכל תיאור. משתמשים ברשתות איטיות או כאלו המשתמשים בחוסמי סקריפטים עדיין מקבלים את הספרייה המלאה. הסינון הוא שיפור, לא חסם.
Sitemaps ו-Robots כקוד
Sitemaps ו-robots.txt אינם מחשבות מאוחרות שנכתבות ידנית. הם Astro routes שצורכים את אותו dataset ו-URL helpers כמו
