← بلاگ

کونسول میں سرور رینڈرنگ

⁦Text.js 0.4⁩ سرور پر صفحہ رینڈر کرکے اس کے فریمز سیل ڈیلٹاز کی صورت میں کونسول تک اسٹریم کر سکتا ہے۔ اسٹیٹک روٹس بھی اپنا پہلا فریم HTML میں پری رینڈر کرتے ہیں۔

Adam

⁦Text.js 0.4⁩ نے صفحہ رینڈر کرنے کے لیے ایک دوسری جگہ شامل کی۔ ڈیفالٹ طور پر، صفحے کا React ٹری براؤزر کے ٹیب میں چلتا ہے۔ جو صفحہ runtime = "server" ایکسپورٹ کرتا ہے وہ اس کے بجائے سرور پر چلتا ہے، اور براؤزر اس کے فریم وصول کرتا ہے۔

app/status/page.tsxtsx
import { Suspense, type ReactNode } from "react";import os from "node:os";import { Box, Text } from "textjs";export const runtime = "server";async function Load(): Promise<ReactNode> {  const [one, five] = os.loadavg();  return <Text>{`load ${one.toFixed(2)} ${five.toFixed(2)}`}</Text>;}export default function StatusPage(): ReactNode {  return (    <Box flexDirection="column">      <Text bold>Status</Text>      <Suspense fallback={<Text color="gray">Reading host…</Text>}>        <Load />      </Suspense>    </Box>  );}

Load ایک async function کمپوننٹ ہے۔ سرور پر یہ اپنا پرامس حل ہونے تک قریب ترین <Suspense> یا loading.tsx پر معطل رہتا ہے، اور فائلیں، ڈیٹابیس اور node:* ماڈیول براہ راست پڑھ سکتا ہے۔ ایسِنک کمپوننٹس ڈیٹا لوڈ کرتے ہیں اور ہکس استعمال نہیں کر سکتے۔ ان کے آس پاس کے عام کمپوننٹس میں ہکس، فوکس، useInput، <Link> اور useRouter() برقرار رہتے ہیں، جو دونوں رن ٹائمز میں یکساں کام کرتے ہیں۔

براؤزر کے ڈاؤن لوڈز

سرور پر رینڈر شدہ صفحہ React، Yoga یا آپ کے روٹس نہیں بھیجتا۔ براؤزر تقریباً 21 ⁦kB gzip⁩ کا ایک فریم پلیئر لوڈ کرتا ہے، جس میں ایک CellBuffer اور وہی CssConsoleDriver ہوتا ہے جو کلائنٹ رینڈرر استعمال کرتا ہے۔ یہ فریم اسی ⁦15fps⁩ رفتار سے چلاتا ہے، اس لیے سرور پر رینڈر شدہ فریم وہی console.log کال پیدا کرتا ہے جو کلائنٹ کرتا۔

پروٹوکول

ہر ٹیب سرور پر اپنا سیشن کھولتا ہے: ایک React روٹ، میموری میں راؤٹر ہسٹری اور ایک سیل بفر۔ ایک اینڈ پوائنٹ دونوں سمتوں کا ڈیٹا منتقل کرتا ہے:

browser tab ──(GET /__textjs/live, server-sent events)──▶ frame deltasbrowser tab ──(POST /__textjs/live, JSON)──────────────▶ keys, paste, wheel, resize, focus, Back/Forward

سرور صرف وہ قطاریں بھیجتا ہے جن کا ہیش اس کنکشن کو موصول ہونے والے آخری فریم کے بعد بدلا ہو۔ ہر قطار متن کے سلسلوں کی صورت میں جاتی ہے، جن کے اسٹائل ایک ایسے ٹیبل میں ہوتے ہیں جس کے صرف آخر میں اندراجات جوڑے جاتے ہیں۔ چوڑے گرافیم، ایموجی کلسٹر اور نیم شفاف رنگ اپنی اصل حالت میں پہنچتے ہیں۔

فریموں کے درمیان کم از کم 66 ms کا وقفہ ہوتا ہے، اور ہر فریم کا فرق اس ڈیٹا کے مقابلے میں نکالا جاتا ہے جسے ساکٹ نے آخری بار قبول کیا تھا۔ جب سست کنکشن ڈیٹا وصول کرنا روک دے تو درمیانی حالتیں چھوڑ دی جاتی ہیں، اور اگلا فریم تازہ ترین حالت پر پہنچ جاتا ہے۔

اگر اسٹریم منقطع ہو جائے تو پلیئر x-textjs-live ہیڈر میں اپنا سیشن آئی ڈی بھیج کر دوبارہ کنیکٹ ہوتا ہے اور ایک نئے کی فریم سے اسی سیشن کو، اس کی حالت برقرار رکھتے ہوئے، جاری رکھتا ہے۔ سیشن آئی ڈیز 192 بٹ کی رینڈم قدریں ہیں جو میموری میں رکھی جاتی ہیں، کبھی URL میں نہیں۔ کنکشن کے بغیر سیشن رینڈرنگ روک دیتا ہے اور 30 سیکنڈ بعد ان ماؤنٹ ہو جاتا ہے۔

دونوں اینڈ پوائنٹس کراس سائٹ درخواستیں مسترد کرتے ہیں، اور ہر ان پٹ ایونٹ کی جانچ ایک مقررہ اسکیما کے مطابق ہوتی ہے۔ ایک سیشن ہر POST میں زیادہ سے زیادہ 64 ایونٹس اور فی سیکنڈ تقریباً 240 ایونٹس قبول کرتا ہے۔

ایسِنک کمپوننٹس اور ڈیٹا

ہر ایسِنک کمپوننٹ کا پرامس ہر سیشن کے لیے کمپوننٹ اور اس کے پراپس کی کلید کے ساتھ کیش ہوتا ہے، اس لیے دوبارہ کوشش کرنے والا رینڈر پہلے سے جاری درخواست استعمال کرتا ہے۔ روٹ بدلنے اور آپ کے router.refresh() کال کرنے پر کیش صاف ہوتا ہے:

const router = useRouter();useInput((e) => e.input === "r" && router.refresh());

صرف سرور کے لیے مخصوص ماڈیولز کے نام *.server.ts ہوتے ہیں۔ اگر براؤزر کو بھیجا جانے والا کوڈ ایسا کوئی ماڈیول یا node:* بلٹ اِن امپورٹ کرے تو بلڈ ناکام ہو جاتا ہے اور امپورٹ کرنے والے کا نام بتاتا ہے۔

جزوی پری رینڈرنگ

جزوی پری رینڈرنگ سرور کے بغیر کام کرتی ہے۔ text build اب ہر اسٹیٹک روٹ کو ایک بار رینڈر کرتا ہے اور اس کا پہلا فریم روٹ کے HTML میں ایک چھوٹے کلاسک <script> کی صورت میں شامل کرتا ہے، اس لیے کسی بھی ماڈیول کے لوڈ ہونے سے پہلے کونسول پینٹ ہو جاتا ہے۔

شیل میں ہر وہ چیز شامل ہے جو سنکرونس طور پر رینڈر ہوتی ہے۔ جو <Suspense> باؤنڈری ابھی انتظار کر رہی ہو وہ اپنا فال بیک دکھاتی ہے، اس میں ایسِنک کمپوننٹس بھی شامل ہیں، اور شیل کی رینڈرنگ کے دوران fetch() کبھی حل نہیں ہوتا۔

جب JavaScript شروع ہوتا ہے تو کلائنٹ رینڈرر یا فریم پلیئر اصل فریم اسٹریم کرتا ہے۔ چونکہ 0.4 بھی فریم صاف کرنے کے بجائے آخر میں جوڑتا ہے، اس لیے ہائیڈریٹ کرنے والا رینڈرر initialClear: false کے ساتھ شروع ہوتا ہے اور شیل کے نیچے لاگ کرتا ہے، اور اس کا پردہ اسی DevTools پینٹ میں شیل کو نظروں سے ہٹا دیتا ہے۔ کونسول صرف ایک بار، شیل کے ہاتھوں صاف ہوتا ہے۔

 ✓ Prerendering console shells (5/5) · 1 server-rendered, frame player 56.5 kB (21.3 kB gzip)

شیل مقرر کردہ viewport سائز پر رینڈر ہوتا ہے، جو ڈیفالٹ طور پر 120 × 36 ہے، کیونکہ بلڈ وزیٹر کے کونسول کا سائز نہیں جان سکتا۔ viewport.responsive آن ہو تو پہلا لائیو فریم ڈاک شدہ کونسول کے مطابق اپنا سائز بدلتا ہے۔ ڈائنامک روٹس کوئی شیل رینڈر نہیں کرتے، اور text.config.ts فائل میں ppr: false پری رینڈرنگ بند کر دیتا ہے۔

رن ٹائم کا انتخاب

کلائنٹ رن ٹائم ڈیفالٹ رہتا ہے۔ کی اسٹروکس اسی ٹاسک میں پینٹ ہوتے ہیں، اور ایپ کسی بھی اسٹیٹک ہوسٹ پر ڈیپلائے ہوتی ہے۔ سرور رن ٹائم ان ڈیش بورڈز اور ایڈمن ویوز کے لیے موزوں ہے جو نجی ڈیٹا پڑھتے ہیں، اور ان روٹس کے لیے جن کی ڈیپنڈنسیز آپ براؤزر بنڈل میں نہیں چاہتے۔ ایک ایپ دونوں کو ملا سکتی ہے، اور ان کے درمیان نیویگیٹ کرنے پر ہدف کا URL ایک نئی دستاویز کے طور پر لوڈ ہوتا ہے۔

مکمل حوالہ سرور رینڈرنگ میں ہے۔