רינדור בצד השרת במסוף
Text.js 0.4 יכול לרנדר דף בשרת ולהזרים את הפריימים שלו למסוף כהפרשי תאים. נתיבים סטטיים גם מבצעים רינדור מראש של הפריים הראשון לתוך ה-HTML.
Text.js 0.4 הוסיף מקום נוסף לרינדור דף. כברירת מחדל, עץ ה-React של הדף רץ בכרטיסיית הדפדפן. דף שמייצא runtime = "server" רץ בשרת במקום זאת, והדפדפן מקבל את הפריימים שלו.
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 שבו משתמש המרנדר בצד הלקוח. הוא מציג פריימים באותו קצב של 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 זה מזה, ולכל אחד מחושב ההפרש מול מה שה-socket קיבל לאחרונה. כשחיבור איטי מפסיק לפנות נתונים, המערכת מדלגת על מצבי ביניים, והפריים הבא קופץ למצב העדכני ביותר.
אם הזרם מתנתק, הנגן מתחבר מחדש עם מזהה הסשן שלו בכותרת x-textjs-live וממשיך את אותו סשן מפריים מפתח חדש, תוך שמירה על מצבו. מזהי סשן הם ערכים אקראיים של 192 סיביות שנשמרים בזיכרון, ולעולם לא בכתובת ה-URL. סשן ללא חיבור מפסיק לרנדר ומוסר מעץ הרכיבים לאחר 30 שניות.
שתי נקודות הקצה דוחות בקשות מאתרים אחרים, וכל אירוע קלט עובר אימות מול סכמה קבועה. סשן מקבל לכל היותר 64 אירועים בכל בקשת POST וכ-240 בשנייה.
רכיבים אסינכרוניים ונתונים
ההבטחה של כל רכיב אסינכרוני נשמרת במטמון נפרד לכל סשן, עם הרכיב וה-props שלו כמפתח, כך שניסיון רינדור חוזר משתמש שוב בבקשה שכבר מתבצעת. המטמון מתנקה כשהנתיב משתנה ובעת קריאה ל-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 מופעל, הפריים החי הראשון מתאים את גודלו למסוף המעוגן. נתיבים דינמיים לא מרנדרים מעטפת, והגדרת ppr: false בקובץ text.config.ts מכבה את הרינדור מראש.
בחירת סביבת ריצה
סביבת הריצה בצד הלקוח נשארת ברירת המחדל. הקשות מקשים מצוירות באותה משימה, ואפשר לפרוס את האפליקציה בכל שירות אירוח סטטי. סביבת הריצה בצד השרת מתאימה ללוחות בקרה ולתצוגות ניהול שקוראים נתונים פרטיים, ולנתיבים שאת התלויות שלהם רוצים להשאיר מחוץ לחבילת הקוד לדפדפן. אפליקציה אחת יכולה לשלב את שתיהן, וניווט ביניהן טוען את כתובת ה-URL של היעד כמסמך חדש.
התיעוד המלא נמצא ב-רינדור בצד השרת.