תיעוד 239
בניית בלוג באמצעות Nuxt.js ו-Sanity.io ב-Cloudflare Pages
במדריך הזה תבנו יישום בלוג באמצעות Nuxt.js ו-Sanity.io ותפרסמו אותו ב-Cloudflare Pages. Nuxt.js הוא מחולל אתרים סטטיים חזק, שנבנה על מסגרת העבודה Vue.js לחזית. Sanity.io הוא כלי CMS נטול ראש (headless), שנבנה לניהול הנתונים של היישום בלי צורך לתחזק מסד נתונים.
#דרישות מוקדמות
- גרסה עדכנית של npm ↗︎ במחשב שלכם
- חשבון Sanity.io ↗︎
#יצירת פרויקט Sanity חדש
כדי להתחיל, צרו פרויקט Sanity חדש באמצעות אחת מהתבניות של Sanity, תבנית הבלוג. אם תרצו להתאים את התצורה, אפשר לשנות את הסכמה או לבחור תבנית מותאמת אישית.
#התקנת Sanity והגדרת מערך הנתונים
צרו את פרויקט Sanity החדש על ידי התקנת לקוח @sanity/cli מ-npm, והרצת sanity init בטרמינל:
npm
npm i @sanity/cliyarn
yarn add @sanity/clipnpm
pnpm add @sanity/clibun
bun add @sanity/clinpm
npx sanity inityarn
yarn sanity initpnpm
pnpm sanity initכשיוצרים פרויקט Sanity, אפשר לבחור באחת מהסכמות המוגדרות מראש שלהם. סכמות מתארות את הצורה של הנתונים במערך הנתונים של Sanity. אם הייתם מתחילים פרויקט חדש לגמרי, אפשר לבחור לאתחל את הסכמה מאפס, אבל בינתיים בחרו בסכמת Blog.
#בדיקת הסכמה
אחרי שהפרויקט נוצר, אפשר לנווט לתיקייה ולהפעיל את הסטודיו באופן מקומי:
cd my-sanity-projectnpm
npx sanity startyarn
yarn sanity startpnpm
pnpm sanity startסטודיו Sanity הוא המקום שבו אפשר ליצור רשומות חדשות למערך הנתונים. כברירת מחדל, הרצת הסטודיו באופן מקומי הופכת אותו לזמין ב-localhost:3333. כנסו לשם עכשיו וצרו את רשומת המחבר. אפשר גם ליצור כאן פוסטים לבלוג.

#פריסת מערך הנתונים
כשאתם מוכנים לפרוס את הסטודיו, הריצו sanity deploy כדי לבחור כתובת URL ייחודית לסטודיו. המשמעות היא שאתם (או כל מי שתזמינו לנהל את הבלוג) יכולים לגשת לסטודיו בדומיין yoururl.sanity.studio.
npm
npx sanity deployyarn
yarn sanity deploypnpm
pnpm sanity deployאחרי שפרסתם את סטודיו Sanity:
- היכנסו ללוח הניהול של Sanity (manage.sanity.io ↗︎).
- מצאו את הפרויקט.
- בחרו ב-API.
- הוסיפו את
http://localhost:3000כמקור CORS מותר לפרויקט.
המשמעות היא שבקשות שמגיעות למערך הנתונים של Sanity מיישום Nuxt ייכנסו לרשימת ההיתרים.

#יצירת פרויקט Nuxt.js חדש
בשלב הבא, צרו פרויקט Nuxt.js. בטרמינל חדש, השתמשו ב-create-nuxt-app כדי להגדיר פרויקט Nuxt חדש:
npm
npx create-nuxt-app blogyarn
yarn dlx create-nuxt-app blogpnpm
pnpx create-nuxt-app blogחשוב לוודא שבוחרים במצב רינדור Universal (SSR / SSG) וביעד פריסה Static (Static/JAMStack hosting), בזמן שעוברים על תהליך ההגדרה.
אחרי שסיימתם את הפרויקט, בצעו cd לתוך הפרויקט החדש, והפעילו שרת פיתוח מקומי באמצעות הרצת yarn dev (או, אם בחרתם ב-npm כמנהל החבילות, npm run dev):
cd blognpm
npm run devyarn
yarn run devpnpm
pnpm run dev#שילוב Sanity.io
אחרי שיישום Nuxt.js מוגדר, הוסיפו את התוסף @sanity/nuxt של Sanity לפרויקט Nuxt:
npm
npm i @nuxtjs/sanity @sanity/clientyarn
yarn add @nuxtjs/sanity @sanity/clientpnpm
pnpm add @nuxtjs/sanity @sanity/clientbun
bun add @nuxtjs/sanity @sanity/clientכדי להגדיר את התוסף ביישום Nuxt.js, תצטרכו לספק כמה פרטי תצורה. הדרך הקלה ביותר לעשות זאת היא להעתיק את תיקיית sanity.json מהסטודיו אל תיקיית היישום (אם כי יש גם שיטות אחרות: עיינו בתיעוד של @nuxt/sanity ↗︎).
Adding sanity.jsonsh
cp ../my-sanity-project/sanity.json .לבסוף, הוסיפו את @nuxtjs/sanity כ-build module בתצורת Nuxt:
nuxt.config.jsjs
{
buildModules: ["@nuxtjs/sanity"];
}#הגדרת רכיבים
כש-Sanity מוגדר ביישום, אפשר להתחיל להשתמש בו כדי לרנדר את הבלוג. עכשיו תגדירו כמה דפים שימשכו נתונים מ-API של Sanity וירנדרו אותם. שימו לב: אם אינכם מכירים את Nuxt, מומלץ לעיין במדריך Nuxt ↗︎, שילמד אתכם כמה מושגי יסוד סביב בניית יישומים עם Nuxt.
#הגדרת דף האינדקס
כדי להתחיל, עדכנו את דף index, שירנדר כשמבקרים בנתיב השורש (/). בקובץ pages/index.vue:
pages/index.vuehtml
<template>
<div class="container">
<div>
<h1 class="title">My Blog</h1>
</div>
<div class="posts">
<div v-for="post in posts" :key="post._id">
<h2><a v-bind:href="post.slug.current" v-text="post.title" /></h2>
</div>
</div>
</div>
</template>
<script>
import { groq } from "@nuxtjs/sanity";
export default {
async asyncData({ $sanity }) {
const query = groq`*[_type == "post"]`;
const posts = await $sanity.fetch(query);
return { posts };
},
};
</script>
<style>
.container {
margin: 2rem;
min-height: 100vh;
}
.posts {
margin: 2rem 0;
}
</style>Vue SFCs, או single file components, הם תכונה ייחודית של Vue שמאפשרת לשלב JavaScript, HTML ו-CSS בקובץ אחד. ב-pages/index.vue מסופקת תגית template, שמייצגת את רכיב Vue.
חשוב: v-for משמש כהנחיה שאומרת ל-Vue לרנדר HTML עבור כל post במערך של posts:
Inspecting the v-for directivehtml
<div v-for="post in posts" :key="post._id">
<h2><a v-bind:href="post.slug.current" v-text="post.title" /></h2>
</div>כדי למלא את מערך posts הזה, משתמשים בפונקציה asyncData, ש-Nuxt מספק כדי לבצע קריאות אסינכרוניות (לדוגמה, בקשות רשת) ולמלא את נתוני הדף.
האובייקט $sanity מסופק על ידי השילוב של Nuxt ו-Sanity.js כאמצעי לבצע בקשות למערך הנתונים של Sanity. באמצעות קריאה ל-$sanity.fetch והעברת שאילתה, אפשר לאחזר נתונים ספציפיים ממערך הנתונים של Sanity, ולהחזיר אותם כנתוני הדף.
אם לא השתמשתם ב-Sanity בעבר, כנראה שאינכם מכירים את GROQ, שפת השאילתות GRaph Oriented Query language ש-Sanity מספק לממשק עם מערך הנתונים. GROQ היא שפה חזקה שמאפשרת לומר ל-API של Sanity אילו נתונים רוצים להוציא ממערך הנתונים. בשאילתה הראשונה, תאמרו ל-Sanity לאחזר כל אובייקט במערך הנתונים עם ערך _type של post:
A basic GROQ queryjs
const query = groq`*[_type == "post"]`;
const posts = await $sanity.fetch(query);#הגדרת דף פוסט הבלוג
דף index שלנו מרנדר קישור לכל פוסט בלוג במערך הנתונים, ומשתמש בערך slug כדי לקבוע את כתובת ה-URL של פוסט. לדוגמה, אם אני יוצר פוסט בלוג בשם "Hello World" ומגדיר את ה-slug ל-hello-world, יישום Nuxt אמור להיות מסוגל לטפל בבקשה לדף /hello-world, ולאחזר את פוסט הבלוג המתאים מ-Sanity.
ל-Nuxt יש תמיכה מובנית בדפים מהסוג הזה, על ידי יצירת קובץ חדש ב-pages בפורמט _slug.vue. בפונקציה asyncData של הדף, אפשר אחר כך להשתמש בארגומנט params כדי להפנות אל ה-slug:
pages/_slug.vuehtml
<script>
export default {
async asyncData({ params, $sanity }) {
console.log(params); // { slug: "hello-world" }
},
};
</script>עם זה בראש, אפשר לבנות את pages/_slug.vue כך שייקח את ערך slug הנכנס, יבצע שאילתה ל-Sanity כדי למצוא את פוסט הבלוג התואם, וירנדר את כותרת post של פוסט הבלוג:
pages/_slug.vuehtml
<template>
<div class="container">
<div v-if="post">
<h1 class="title" v-text="post.title" />
<div class="content"></div>
</div>
<h4><a href="/">← Go back</a></h4>
</div>
</template>
<script>
import { groq } from "@nuxtjs/sanity";
export default {
async asyncData({ params, $sanity }) {
const query = groq`*[_type == "post" && slug.current == "${params.slug}"][0]`;
const post = await $sanity.fetch(query);
return { post };
},
};
</script>
<style>
.container {
margin: 2rem;
min-height: 100vh;
}
.content {
margin: 2rem 0;
max-width: 38rem;
}
p {
margin: 1rem 0;
}
</style>בביקור, לדוגמה, ב-/hello-world, Nuxt ייקח את ה-slug הנכנס hello-world, ויבצע שאילתת GROQ ל-Sanity עבור כל אובייקט עם _type של post, וגם slug שתואם לערך /hello-world. מתוך הקבוצה הזו אפשר לקבל את האובייקט הראשון במערך (באמצעות אופרטור אינדקס המערך שמוצאים ב-JavaScript: [0]) ולהגדיר אותו כ-post בנתוני הדף.
#רינדור תוכן לפוסט בלוג
רינדרתם את כותרת post של הבלוג, אבל עדיין חסר התוכן של פוסט הבלוג עצמו. כדי לרנדר אותו, ייבאו את החבילה sanity-blocks-vue-component ↗︎, שלוקחת את פורמט Portable Text ↗︎ של Sanity ומרנדרת אותו כרכיב Vue.
קודם, התקינו את חבילת npm:
npm
npm i sanity-blocks-vue-componentyarn
yarn add sanity-blocks-vue-componentpnpm
pnpm add sanity-blocks-vue-componentbun
bun add sanity-blocks-vue-componentאחרי שהחבילה מותקנת, צרו את plugins/sanity-blocks.js, שיייבא את הרכיב וירשום אותו כרכיב Vue בשם block-content:
plugins/sanity-blocks.jsjs
import Vue from "vue";
import BlockContent from "sanity-blocks-vue-component";
Vue.component("block-content", BlockContent);בתצורת Nuxt, nuxt.config.js, ייבאו את הקובץ הזה כחלק מההנחיה plugins:
nuxt.config.jsjs
{
plugins: ["@/plugins/sanity-blocks.js"];
}ב-pages/_slug.vue אפשר עכשיו להשתמש ברכיב <block-content> כדי לרנדר את התוכן. זה מקבל את הצורה של רכיב HTML מותאם אישית, ומקבל שלושה ארגומנטים: :blocks, שמציין מה לרנדר (במקרה שלנו, child), v-for, שמקבל איטרטור של המקום שממנו לקבל את child (במקרה שלנו, post.body), ו-:key, שעוזר ל-Vue לעקוב אחרי רינדור מצב ↗︎ באמצעות ערך ייחודי לכל פוסט: כלומר, ערך _id.
pages/_slug.vuehtml
<template>
<div class="container">
<div v-if="post">
<h1 class="title" v-text="post.title" />
<div class="content">
<block-content
:blocks="child"
v-for="child in post.body"
:key="child._id"
/>
</div>
</div>
<h4><a href="/">← Go back</a></h4>
</div>
</template>
<script>
import { groq } from "@nuxtjs/sanity";
export default {
async asyncData({ params, $sanity }) {
const query = groq`*[_type == "post" && slug.current == "${params.slug}"][0]`;
const post = await $sanity.fetch(query);
return { post };
},
};
</script>
<style>
.container {
margin: 2rem;
min-height: 100vh;
}
.content {
margin: 2rem 0;
max-width: 38rem;
}
p {
margin: 1rem 0;
}
</style>ב-pages/index.vue אפשר להשתמש ברכיב block-content כדי לרנדר תקציר של התוכן, על ידי לקיחת הבלוק הראשון בתוכן פוסט הבלוג ורינדור שלו:
pages/index.vuehtml
<template>
<div class="container">
<div>
<h1 class="title">My Blog</h1>
</div>
<div class="posts">
<div v-for="post in posts" :key="post._id">
<h2><a v-bind:href="post.slug.current" v-text="post.title" /></h2>
<div class="summary">
<block-content
:blocks="post.body[0]"
v-bind:key="post.body[0]._id"
v-if="post.body.length"
/>
</div>
</div>
</div>
</div>
</template>
<script>
import { groq } from "@nuxtjs/sanity";
export default {
async asyncData({ $sanity }) {
const query = groq`*[_type == "post"]`;
const posts = await $sanity.fetch(query);
return { posts };
},
};
</script>
<style>
.container {
margin: 2rem;
min-height: 100vh;
}
.posts {
margin: 2rem 0;
}
.summary {
margin-top: 0.5rem;
}
</style>יש עוד דברים רבים בתוך סכמת הבלוג שאפשר להוסיף לפרויקט. כתרגיל, שקלו אחד מהבאים כדי להמשיך לפתח את ההבנה של איך בונים עם CMS נטול ראש:
- צרו את
pages/authors.vue, ורנדרו רשימת מחברים (בדומה ל-pages/index.vue, אבל עבור אובייקטים עם_type == "author") - קראו את תיעוד Sanity על שימוש בהפניות ב-GROQ ↗︎, והשתמשו בו כדי לרנדר מידע על המחבר בדף פוסט בלוג
#פרסום עם Cloudflare Pages
פרסום הפרויקט עם Cloudflare Pages הוא תהליך בן שני שלבים: קודם, דחפו את הפרויקט ל-GitHub, ואחר כך, בלוח הבקרה של Cloudflare Pages, הגדירו פרויקט חדש על בסיס מאגר GitHub הזה. Pages יפרוס גרסה חדשה של האתר בכל פעם שתפרסמו, ואפילו יגדיר פריסות תצוגה מקדימה בכל פעם שתפתחו בקשת משיכה חדשה.
כדי לדחוף את הפרויקט ל-GitHub, צרו מאגר חדש ↗︎, ופעלו לפי ההוראות כדי לדחוף את מאגר Git המקומי ל-GitHub.
אחרי שדחפתם את הפרויקט ל-GitHub, פרסו את האתר ל-Pages:
- בלוח הבקרה של Cloudflare, עברו לדף Workers & Pages. עברו אל Workers & Pages ↗
- בחרו ב-Create application > Pages > Import an existing Git repository.
- בחרו במאגר GitHub החדש שיצרתם ובחרו ב-Begin setup.
- במקטע Set up builds and deployments, תחת Build settings > Framework preset, בחרו ב-Nuxt. Pages יגדיר עבורכם את השדות הנכונים באופן אוטומטי.
כשהאתר נפרס, תקבלו כתובת URL ייחודית לצפייה בו בסביבת הייצור.
כדי לפרוס את הפרויקט באופן אוטומטי כשהנתונים ב-Sanity.io משתנים, אפשר להשתמש ב-Deploy Hooks. צרו כתובת URL חדשה של Deploy Hook ב-Pages project > Settings. בדף Settings של פרויקט Sanity, מצאו את המקטע Webhooks, והוסיפו את כתובת ה-URL של Deploy Hook, כפי שרואים למטה:

עכשיו, כשמבצעים שינוי במערך הנתונים של Sanity.io, Sanity יבצע בקשה לכתובת ה-URL הייחודית של Deploy Hook, וזה יתחיל פריסת Cloudflare Pages חדשה. בדרך הזו, יישום Pages יישאר מעודכן כשמוסיפים פוסטי בלוג חדשים, או עורכים פוסטים קיימים.
#סיכום
על ידי השלמת המדריך הזה, פרסתם בהצלחה בלוג משלכם, שמופעל על ידי Nuxt, Sanity.io ו-Cloudflare Pages. אפשר למצוא את קוד המקור של שני בסיסי הקוד ב-GitHub:
- חזית הבלוג: https://github.com/signalnerve/nuxt-sanity-blog ↗︎
- מערך הנתונים של Sanity: https://github.com/signalnerve/sanity-blog-schema ↗︎
אם נהניתם מהמדריך הזה, ייתכן שתתעניינו ללמוד איך אפשר להשתמש ב-Cloudflare Workers, פלטפורמת הפונקציות נטולת השרת החזקה שלנו, כדי להרחיב את האתר הקיים. עיינו במדריך Build an API for your front end using Pages Functions כדי ללמוד עוד.