Render phía máy chủ trong console
Text.js 0.4 có thể render trang trên máy chủ và truyền frame đến console dưới dạng phần thay đổi của ô. Các route tĩnh cũng prerender frame đầu tiên vào HTML.
Text.js 0.4 bổ sung nơi thứ hai để trang render. Theo mặc định, cây React của trang chạy trong tab trình duyệt. Trang xuất runtime = "server" sẽ chạy trên máy chủ, còn trình duyệt nhận các frame của trang.
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 là component async function. Trên máy chủ, nó tạm dừng tại <Suspense> hoặc loading.tsx gần nhất cho đến khi promise hoàn tất, và có thể đọc trực tiếp tệp, cơ sở dữ liệu và module node:*. Component bất đồng bộ tải dữ liệu và không thể dùng hook. Các component thông thường bao quanh chúng vẫn giữ hook, focus, useInput, <Link> và useRouter(), với hành vi giống nhau trong cả hai runtime.
Trình duyệt tải xuống những gì
Trang được render phía máy chủ không gửi kèm React, Yoga hay các route của bạn. Trình duyệt tải trình phát frame có dung lượng gzip khoảng 21 kB, chứa một CellBuffer và cùng CssConsoleDriver mà bộ render phía máy khách sử dụng. Trình phát điều tiết frame ở cùng tốc độ 15 fps, nên frame được render phía máy chủ tạo ra lời gọi console.log giống như phía máy khách.
Giao thức
Mỗi tab mở phiên riêng trên máy chủ: một gốc React, lịch sử router trong bộ nhớ và bộ đệm ô. Một endpoint truyền dữ liệu theo cả hai chiều:
browser tab ──(GET /__textjs/live, server-sent events)──▶ frame deltasbrowser tab ──(POST /__textjs/live, JSON)──────────────▶ keys, paste, wheel, resize, focus, Back/ForwardMáy chủ chỉ gửi những dòng có mã băm thay đổi kể từ frame cuối mà kết nối đó nhận được. Mỗi dòng được gửi dưới dạng các đoạn văn bản tham chiếu đến bảng kiểu chỉ cho phép nối thêm. Grapheme rộng, cụm emoji và màu bán trong suốt đều đến nơi nguyên vẹn.
Các frame cách nhau ít nhất 66 ms, và mỗi frame được so sánh để tìm khác biệt với dữ liệu mà socket chấp nhận lần cuối. Khi kết nối chậm không tiếp tục tiêu thụ dữ liệu, các trạng thái trung gian sẽ bị bỏ qua, và frame tiếp theo chuyển thẳng đến trạng thái mới nhất.
Nếu luồng bị ngắt, trình phát kết nối lại với ID phiên trong header x-textjs-live và tiếp tục phiên đó từ một keyframe mới, giữ nguyên trạng thái. ID phiên là giá trị ngẫu nhiên 192 bit lưu trong bộ nhớ, không bao giờ nằm trong URL. Phiên không có kết nối sẽ ngừng render và bị unmount sau 30 giây.
Cả hai endpoint đều từ chối yêu cầu liên trang, và mọi sự kiện đầu vào đều được kiểm tra theo schema cố định. Một phiên chấp nhận tối đa 64 sự kiện mỗi POST và khoảng 240 sự kiện mỗi giây.
Component bất đồng bộ và dữ liệu
Promise của mỗi component bất đồng bộ được lưu vào bộ nhớ đệm theo phiên, với khóa là component và props của nó, nên lần render thử lại sẽ dùng lại yêu cầu đang xử lý. Bộ nhớ đệm được xóa khi route thay đổi và khi bạn gọi router.refresh():
const router = useRouter();useInput((e) => e.input === "r" && router.refresh());Module chỉ dành cho máy chủ có tên dạng *.server.ts. Bản dựng sẽ thất bại nếu mã gửi đến trình duyệt import một module như vậy hoặc import module tích hợp node:*, đồng thời nêu tên module thực hiện import.
Prerender từng phần
Prerender từng phần hoạt động mà không cần máy chủ. Giờ đây, text build render mỗi route tĩnh một lần và nhúng frame đầu tiên vào HTML của route dưới dạng một <script> cổ điển nhỏ, để console hiển thị trước khi bất kỳ module nào tải.
Shell là toàn bộ nội dung render đồng bộ. Ranh giới <Suspense> vẫn đang chờ sẽ hiển thị fallback, bao gồm cả component bất đồng bộ, và fetch() không bao giờ hoàn tất trong lúc shell render.
Khi JavaScript khởi động, bộ render phía máy khách hoặc trình phát frame truyền frame thực qua luồng. Vì 0.4 cũng nối thêm frame thay vì xóa, bộ render đang hydrate khởi động với initialClear: false và ghi bên dưới shell, còn màn che của nó đẩy shell ra khỏi vùng nhìn thấy trong cùng lần vẽ của DevTools. Shell xóa console đúng một lần.
✓ Prerendering console shells (5/5) · 1 server-rendered, frame player 56.5 kB (21.3 kB gzip)Shell được render theo kích thước viewport đã cấu hình, mặc định là 120 × 36, vì quá trình dựng không thể biết kích thước console của khách truy cập. Khi bật viewport.responsive, frame trực tiếp đầu tiên đổi kích thước cho vừa console đang neo. Route động không render shell, và ppr: false trong text.config.ts tắt prerender.
Chọn runtime
Runtime phía máy khách vẫn là mặc định. Thao tác gõ phím hiển thị trong cùng tác vụ, và ứng dụng có thể triển khai lên bất kỳ dịch vụ lưu trữ tĩnh nào. Runtime phía máy chủ phù hợp với bảng tổng quan và giao diện quản trị đọc dữ liệu riêng tư, cũng như các route có phần phụ thuộc mà bạn không muốn đưa vào gói mã trình duyệt. Một ứng dụng có thể kết hợp cả hai, và điều hướng giữa chúng sẽ tải URL đích dưới dạng tài liệu mới.
Tài liệu tham khảo đầy đủ nằm trong Render phía máy chủ.