← وبلاگ

رندر سمت سرور در کنسول

⁦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 است. در سرور، تا زمان تکمیل promise خود، اجرا را به نزدیک‌ترین <Suspense> یا loading.tsx تعلیق می‌کند و می‌تواند فایل‌ها، پایگاه‌های داده و ماژول‌های node:* را مستقیم بخواند. کامپوننت‌های ناهمگام داده‌ها را بارگیری می‌کنند و نمی‌توانند از هوک‌ها استفاده کنند. کامپوننت‌های عادی پیرامون آن‌ها هوک‌ها، فوکوس، useInput، <Link> و useRouter() را حفظ می‌کنند که در هر دو محیط اجرا رفتار یکسانی دارند.

آنچه مرورگر دانلود می‌کند

صفحه رندرشده در سمت سرور، React، Yoga یا مسیرهای شما را به مرورگر نمی‌فرستد. مرورگر یک پخش‌کننده فریم با حجم gzip حدود 21 kB بارگیری می‌کند که یک CellBuffer و همان CssConsoleDriver مورد استفاده رندرکننده سمت کاربر را در خود دارد. این پخش‌کننده فریم‌ها را با همان نرخ 15 فریم بر ثانیه زمان‌بندی می‌کند، پس فریم رندرشده در سمت سرور همان فراخوانی 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 ثانیه از حالت نصب خارج می‌شود.

هر دو نقطه پایانی درخواست‌های بین‌سایتی را رد می‌کنند و هر رویداد ورودی بر اساس یک طرح‌واره ثابت اعتبارسنجی می‌شود. هر نشست حداکثر 64 رویداد در هر POST و حدود 240 رویداد در ثانیه می‌پذیرد.

کامپوننت‌های ناهمگام و داده‌ها

⁦promise⁩ هر کامپوننت ناهمگام برای هر نشست، با کلیدی بر اساس کامپوننت و propهای آن، در حافظه نهان ذخیره می‌شود تا رندری که دوباره تلاش می‌کند از همان درخواست در حال اجرا استفاده کند. با تغییر مسیر و هنگام فراخوانی router.refresh() حافظه نهان پاک می‌شود:

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

ماژول‌های مخصوص سرور با الگوی *.server.ts نام‌گذاری می‌شوند. اگر کدی که به مرورگر فرستاده می‌شود یکی از آن‌ها یا یک ماژول داخلی node:* را وارد کند، ساخت شکست می‌خورد و نام واردکننده را اعلام می‌کند.

پیش‌رندر جزئی

پیش‌رندر جزئی بدون سرور کار می‌کند. دستور text build اکنون هر مسیر ایستا را یک بار رندر می‌کند و نخستین فریم آن را به‌صورت یک <script> کلاسیک کوچک در HTML مسیر قرار می‌دهد، بنابراین کنسول پیش از بارگیری هر ماژولی ترسیم می‌شود.

پوسته شامل تمام چیزهایی است که به‌صورت همگام رندر می‌شوند. مرز <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، اندازه نخستین فریم زنده با کنسول متصل به پنجره تطبیق می‌یابد. مسیرهای پویا پوسته‌ای رندر نمی‌کنند و تنظیم ppr: false در فایل text.config.ts پیش‌رندر را خاموش می‌کند.

انتخاب محیط اجرا

محیط اجرای سمت کاربر همچنان پیش‌فرض است. اثر فشردن کلیدها در همان وظیفه ترسیم می‌شود و برنامه روی هر میزبان ایستایی مستقر می‌شود. محیط اجرای سرور برای داشبوردها و نماهای مدیریتی که داده‌های خصوصی را می‌خوانند و مسیرهایی که نمی‌خواهید وابستگی‌هایشان در بسته مرورگر باشد مناسب است. یک برنامه می‌تواند هر دو را ترکیب کند و جابه‌جایی میان آن‌ها URL مقصد را به‌عنوان سندی جدید بارگیری می‌کند.

مرجع کامل در رندر سمت سرور آمده است.