المحتويات
جلب البيانات
خصائص الخادم هي قناة البيانات الوحيدة التي يوفّرها Carats. تجلب الدالة الخادمية على الخادم، ويتلقى المكوّن المُبرَز النتيجة.
دورة حياة الدالة الخادمية
يحلّ الخادم الدالة الخادمية للمسار المطلوب قبل عمل مكوّن الصفحة، لذا يُصيَّر المكوّن دائمًا ببيانات مكتملة ولا توجد حالة تحميل للتعامل معها. وعندما تُلقي دالة خادمية خطأ، يُمرَّر الخطأ إلى مكوّن تعليق error.
// src/server/culets/trade.ts
import { culet } from '@carats/ssr';
export default culet<TradeData>('/trade/:symbol', async (req) => {
const symbol = req.params.symbol;
if (!symbol) {
throw new Error('Symbol is required');
}
const res = await fetch(`https://api.example.com/quote/${symbol}`);
if (!res.ok) {
throw new Error(`Upstream quote failed with ${res.status}`);
}
return res.json();
});
الخطأ المُلقى، سواء جاء من الدالة الخادمية أو من المكوّن، يصل إلى مكوّن تعليق `error` بوصفه `Error`. أما المسار غير المطابَق فيصل إلى `notFound` بدلًا منه.
شكل الطلب
يتلقى معالج الدالة الخادمية كل ما حمله الطلب: params لمعاملات المسار، بالإضافة إلى url وquery وheaders وcookies وmethod. لا توجد خاصية data، لأن الدوال الخادمية لا تُبلَغ إلا عبر GET.
import { culet } from '@carats/ssr';
culet<Dashboard>('/dashboard', (req) => {
return {
// Route parameters
// req.params
// Query string: req.query
// Cookies: req.cookies
// Headers: req.headers
// req.url, req.method
theme: req.cookies.theme ?? 'dark',
tab: req.query.tab ?? 'overview',
};
});
لأن `cookies` و`headers` متاحان، يمكن للدالة الخادمية تخصيص الصفحة لكل زائر دون أي بنية تحتية إضافية. لكن الاستجابة تبقى مخزّنة لكل مسار، لذا لا تضع بيانات خاصة بالزائر في دالة خادمية لم يُعلَّم المكوّن المستهلك在她 بـ `recast`.
المعالجات غير المتزامنة
يمكن أن تكون الدالة الخادمية غير متزامنة، والانتظار داخلها هو الطريقة المعتادة للتحدث إلى قاعدة بيانات أو واجهة برمجية بعيدة. ويُكفي إرجاع وعد؛ ولا حاجة للانتظار عند الاستدعاء.
culet<TradeData>('/trade/:symbol', async (req) => {
const [quote, book] = await Promise.all([
fetchQuote(req.params.symbol),
fetchOrderBook(req.params.symbol)
]);
return { ...quote, orderBook: book };
});
قواعد مطابقة المسارات
تُربط الدالة الخادمية بصفحتها حصرًا بسلسلة المسار. ويجب أن يتطابق مسار الدالة الخادمية ومسار الوجه حرفًا بحرف، بما في ذلك أسماء المعاملات، وإلا فسيُصيَّر المكوّن دون بيانات. أما المسار الذي لا دالة خادمية له فيُصيَّر بلا خصائص، وهو الحال الطبيعي للصفحات الثابتة.
// The facet route
'/trade/:symbol'
// The culet route — must be identical, including the parameter name
culet<TradeData>('/trade/:symbol', handler)
لا توجد مطابقة ضمنية باسم الملف أو باسم المكوّن. خطأ مطبعي في سلسلة المسار يفشل بصمت: تُصيَّر الصفحة، وتكون الخصائص `undefined`، ويظهر الخطأ كانهيار داخل المكوّن. تسجيل مسار الدالة الخادمية كثابت مُسمّى واستيراده في الموضعين يُزيل هذا الاحتمال.
التخزين المؤقت وrecast
يحتفظ الإطار بأحدث خصائص خادم لكل مسار ويعيد استخدامها، فلا يعيد المكوّن المُبرَز الجلب في كل تصيير. وحين يجب على صفحة أن تُظهر بيانات طازجة دائمًا، خرج من ذلك لكل مكوّن عبر خيار recast.
إجبار إعادة الجلب
غلِّف المكوّن بـ { recast: true } لتخطي التخزين المؤقت في كل تصيير. وينطبق ذلك على ذلك المكوّن وحده، ما يجعله مناسبًا لصفحات مثل لوحة مباشرة بدل أن يكون قاعدة لكل الموقع.
import { Burnish } from '@carats/render';
// Cached: reuses the most recent server props for the route
export default Burnish<TradeData>(function (data) {
return <h1>{data.symbol} {data.price}</h1>;
});
import { Burnish } from '@carats/render';
// Not cached: refetches on every render
export default Burnish<TradeData>(function (data) {
return <h1>{data.symbol} {data.price}</h1>;
}, { recast: true });
`recast` يخص المكوّن لا الدالة الخادمية، لذا صفحة واحدة حية لا تُجبر كل مستهلكي ذلك المسار على إعادة الجلب.
الكائن العام لخصائص الخادم
تُعرض الخصائص المُسلسلة على window.carats.ssp. وssp.for هو المسار الذي تنتمي إليه، وssp.data هي القيمة. وهذا ما يتيح لمكوّن غير مُبرَز، أو لشيفرة عادية داخل استدعاء afterMount، قراءة بيانات جلبها الخادم بالفعل بدل إطلاق طلب ثانٍ.
import { afterMount } from '@carats/hooks';
export default function LiveBadge() {
afterMount(() => {
// Read the props the server already resolved for this route
const ssp = window.carats.ssp;
const el = document.getElementById('live-badge');
if (!el || !ssp) return;
el.textContent = `${ssp.for}: ${JSON.stringify(ssp.data)}`;
});
return <span id="live-badge"></span>;
}
الكائن العام في العميل مُصنَّف، لذا لا يحتاج `window.carats` إلى cast. و`ssp.for` هو المسار الذي تنتمي إليه الحزمة، ما يتيح لمكوّن مشترك التحقق من أن البيانات التي على وشك قراءتها حُلَّت للصفحة التي يُصيَّر فيها، وتخطّيها إن لم تكن كذلك.