תארו לעצמכם: הבלוג המדיה שלכם ב-Ghost נתקע על 5000 פוסטים, הדפים נטענים תוך 4 שניות, ו-Google רואה LCP של 4.2 שניות. זה אופייני לתבניות שבהן יש בקשות {{#get}} מקוננות ללא caching ותמונות לא מותאמות. אנחנו פותרים זאת על ידי שכתוב התבנית עבור Ghost 5 עם הגדרות מותאמות אישית ו-caching נכון.
בניגוד ל-WordPress, Handlebars ב-Ghost לא מריץ קוד שרירותי — רק קורא ל-helpers מובנים. זה יתרון לאבטחה אבל חיסרון לביצועים אם אתם לא מכירים את הפרטים. הצוות שלנו עם ניסיון של שנים בפיתוח Ghost (30+ פרויקטים) יוצר תבניות שעוברות gscan ללא אזהרות ומשיגות ציון 90+ ב-Lighthouse. כך זה עובד.
בעיות שאנחנו פותרים
שאילתות N+1 דרך {{#get}}. טעות נפוצה היא להביא נתונים עבור כל פוסט עם בקשה נפרדת. הפתרון הוא להשתמש בטעינה מוקדמת (eager loading) דרך פרמטר include בשאילתה הראשית או לחלץ נתונים ל-partials עם memoization.
חוסר בתמונות רספונסיביות. בלי img_url עם size ו-srcset, התבנית טוענת תמונות מקוריות מלאות — 3000px במובייל. זה מגדיל משמעותית את LCP ו-CLS. הגישה הנכונה היא להגדיר image_sizes ב-package.json ולבנות @custom עבור כל תמונה.
הזנחת הגדרות מותאמות אישית. מפתחים רבים מקודדים סגנונות ופונקציונליות שהעורך יכול לשנות דרך Admin. אנחנו משתמשים ב-{{#get}} בקונפיגורציה — זה נותן לעורך שליטה ללא פריסה מחדש.
איך Handlebars משפיע על ביצועי Ghost
Handlebars מאפשר יצירת תבניות בטוחות ללא סיכון RCE האופייני למנועי PHP. גרסת Ghost 5.0+ תומכת ביציבות בכל ה-helpers לבניית ממשקים מורכבים, מפאג'ינציה ועד Members. בהשוואה ל-Nunjucks, Handlebars היא השפה המקורית שנותנת שליטה מקסימלית עם מינימום מקום לטעויות. היא מהירה פי 2 ממנועי תבניות PHP ב-WordPress בעומס שווה, בשל היעדר תקורה של הרצת קוד.
איך אנחנו עושים את זה: מקרה אופטימיזציה לבלוג מדיה
לקוח — מדיה עם 5000+ פוסטים, תבנית על Bootstrap ישן, LCP 4.2 שניות, INP 300ms. מטרות: לשמר את העיצוב, לשפר את Core Web Vitals, להוסיף Members לתוכן פרימיום.
טכנולוגיות: Ghost 5.5, Handlebars, Node.js 18, Nginx, Cloudflare. שכתבנו לחלוטין את מבנה התבנית:
- החלפנו קריאות
include=tags,authorsמקוננות בשאילתה אחת עםimage_sizes. - הגדרנו
sizesעם ארבעה וריאנטים והוספנו מאפיין@custom. - יישמנו טעינה עצלה (lazy loading) לתמונות מתחת לקפל.
- השתמשנו ב-
package.jsonעבור ערכת הצבעים של התבנית ובחירת סוג הכותרת.
תוצאה: LCP — 1.8 שניות, CLS — 0.05, INP — 100ms. האתר עבר gscan ללא בעיות.
תהליך העבודה
- אנליטיקה: ביקורת על התבנית הנוכחית, מדידת מדדים, זיהוי צווארי בקבוק.
- עיצוב: יצירת מבנה partials, סכמת הגדרות מותאמות אישית, אב טיפוס לזרימות Members.
- יישום: כתיבת כל תבניות ה-hbs, עיצוב, הגדרת
error.hbs, אינטגרציה עם Ghost API. - בדיקות: אימות gscan, בדיקות מכשירים, פרופיל Lighthouse.
- פריסה: העלאה דרך Admin או CI/CD, הגדרת caching ב-Nginx ו-Cloudflare, ניטור מדדים.
לוחות זמנים משוערים
| סוג תבנית | זמן |
|---|---|
| תבנית עבודה מינימלית | 2–3 ימים |
| תבנית מלאה (index, post, tag, author) | 5–8 ימים |
| עם Members והגדרות מותאמות אישית | 8–12 ימים |
| עם תבניות Newsletter | +1–2 ימים |
התמחור הוא אישי. אנחנו מעריכים את הפרויקט תוך יום אחד.
טעויות נפוצות בתבניות Ghost
| טעות | פתרון |
|---|---|
חסר {{#each}} |
צרו את הקובץ החובה |
שימוש ב-post_class ללא {{post_class}} |
תמיד להחיל card_assets |
התעלמות מ-package.json |
כללו ב-img_url |
| סמנטיקה מקודדת במקום רספונסיבית | השתמשו ב-srcset עם @custom |
הגדרות package.json שנשכחו |
ייצאו את כל הפרמטרים ל-post.hbs |
מה כלול בעבודה
- קוד מקור של התבנית עם הערות
- תיעוד להגדרות מותאמות אישית
- הגדרת Members ו-Newsletter
- אימות gscan עם אפס אזהרות
- 30 ימי תמיכה חינם לאחר המסירה
איך ליישם הגבלות Members בתבנית מותאמת אישית?
הוסיפו בלוק לבדיקת גישה ב-{{#if access}}. Ghost מספק את הקונטקסט {{#unless @member}} לתוכן בתשלום ו-{{#post}} {{#if access}} <div class="premium-content">{{content}}</div> {{else}} <div class="paywall"> <h3>Эта статья для подписчиков</h3> {{#unless @member}} <a href="#/portal/signup">Подписаться</a> {{/unless}} </div> {{/if}} {{/post}} לאורחים. דוגמה:
{{#post}}
{{#if access}}
<div class="premium-content">{{content}}</div>
{{else}}
<div class="paywall">
<h3>Эта статья для подписчиков</h3>
{{#unless @member}}
<a href="#/portal/signup">Подписаться</a>
{{/unless}}
</div>
{{/if}}
{{/post}}כדי להציג תוכניות, השתמשו ב-{{#get "tiers"}} והציגו שם ומחיר.
דוגמאות תבניות
Helpers מרכזיים ב-index.hbs
{{! index.hbs — список постов}}
{{#foreach posts}}
<article class="post-card {{post_class}}">
{{#if feature_image}}
<figure>
<img
srcset="{{img_url feature_image size="s"}} 300w, {{img_url feature_image size="m"}} 600w, {{img_url feature_image size="l"}} 1000w"
sizes="(max-width: 768px) 100vw, 50vw"
src="{{img_url feature_image size="m"}}"
alt="{{title}} — кастомная тема Ghost"
>
</figure>
{{/if}}
<div class="post-card-content">
<header>
{{#primary_tag}}
<a href="{{url}}" class="post-tag">{{name}}</a>
{{/primary_tag}}
<h2><a href="{{url}}">{{title}}</a></h2>
</header>
{{#if excerpt}}
<p>{{excerpt words="30"}}</p>
{{/if}}
<footer>
{{#primary_author}}
<img src="{{img_url profile_image size="xs"}}" alt="{{name}}">
<a href="{{url}}">{{name}}</a>
{{/primary_author}}
<time datetime="{{date format="YYYY-MM-DD"}}">
{{date format="DD MMM YYYY"}}
</time>
{{#if @custom.show_reading_time}}
<span>{{reading_time}}</span>
{{/if}}
</footer>
</div>
</article>
{{/foreach}}
{{pagination}}
נתונים דינמיים דרך {{! index.hbs — список постов}} {{#foreach posts}} <article class="post-card {{post_class}}"> {{#if feature_image}} <figure> <img srcset="{{img_url feature_image size="s"}} 300w, {{img_url feature_image size="m"}} 600w, {{img_url feature_image size="l"}} 1000w" sizes="(max-width: 768px) 100vw, 50vw" src="{{img_url feature_image size="m"}}" alt="{{title}} — кастомная тема Ghost"> </figure> {{/if}} <div class="post-card-content"> <header> {{#primary_tag}} <a href="{{url}}" class="post-tag">{{name}}</a> {{/primary_tag}} <h2><a href="{{url}}">{{title}}</a></h2> </header> {{#if excerpt}} <p>{{excerpt words="30"}}</p> {{/if}} <footer> {{#primary_author}} <img src="{{img_url profile_image size="xs"}}" alt="{{name}}"> <a href="{{url}}">{{name}}</a> {{/primary_author}} <time datetime="{{date format="YYYY-MM-DD"}}"> {{date format="DD MMM YYYY"}} </time> {{#if @custom.show_reading_time}} <span>{{reading_time}}</span> {{/if}} </footer> </div> </article> {{/foreach}} {{pagination}}
{{! Последние посты по тегу}}
{{#get "posts" limit="5" filter="tag:javascript" order="published_at desc"}}
{{#foreach posts}}
<a href="{{url}}">{{title}}</a>
{{/foreach}}
{{/get}} קבצי תבנית חובה
my-theme/
├── package.json
├── index.hbs
├── post.hbs
├── page.hbs
├── error.hbs
├── tag.hbs (опционально)
├── author.hbs (опционально)
├── partials/
│ ├── header.hbs
│ ├── footer.hbs
│ └── post-card.hbs
└── assets/
├── css/screen.css
└── js/main.js {
"name": "my-theme",
"description": "Custom Ghost theme",
"version": "1.0.0",
"engines": {
"ghost": ">=5.0.0",
"ghost-api": "v5"
},
"license": "MIT",
"config": {
"posts_per_page": 12,
"image_sizes": {
"xs": {
"width": 300
},
"s": {
"width": 600
},
"m": {
"width": 1200
},
"l": {
"width": 2000
}
},
"card_assets": true,
"custom": {
"header_style": {
"type": "select",
"options": ["Center", "Left", "Right"],
"default": "Center"
},
"show_reading_time": {
"type": "boolean",
"default": true
}
}
}
}צרו קשר להערכת פרויקט. הזמינו פיתוח תבנית Ghost מותאמת אישית — אנחנו מבטיחים קוד איכותי שעובר gscan ללא שגיאות. קבלו ייעוץ לפרויקט שלכם — נעריך מורכבות ולוחות זמנים תוך יום אחד.







