Фреймворк Fresh построен на базе Deno и ориентирован на рендеринг на стороне сервера с минимальным использованием JavaScript на клиенте. Это делает его подходящим для реализации real-time чатов с высокой производительностью и низкой задержкой. Основой такого решения является WebSocket-соединение, обеспечивающее постоянный двусторонний канал между клиентом и сервером.
Ключевые элементы архитектуры:
Во Fresh маршруты создаются в папке routes. Для
WebSocket-чата создается отдельный маршрут, например
routes/api/ws.ts. Основные шаги:
import { WebSocket, acceptWebSocket } from "https://deno.land/std/ws/mod.ts";
const clients = new Set<WebSocket>();
export const handler = async (req: Request) => {
if (req.headers.get("upgrade") !== "websocket") {
return new Response("Only websocket connections allowed", { status: 400 });
}
const { socket, response } = Deno.upgradeWebSocket(req);
clients.add(socket);
socket.onmess age = (event) => {
for (const client of clients) {
if (client !== socket) {
client.send(event.data);
}
}
};
socket.oncl ose = () => clients.delete(socket);
return response;
};
В этом примере сервер хранит все активные соединения в
Set и рассылает полученные сообщения всем подключенным
клиентам, кроме отправителя.
Для минимизации нагрузки на сервер при большом числе участников необходимо использовать структуры данных с быстрым доступом, например:
Map<string, WebSocket> для привязки пользователей
к сокетам.Пример управления пользователями:
const users = new Map<string, WebSocket>();
function addUser(id: string, socket: WebSocket) {
users.set(id, socket);
}
function removeUser(id: string) {
users.delete(id);
}
function broadcastMessage(senderId: string, message: string) {
for (const [id, socket] of users) {
if (id !== senderId) socket.send(message);
}
}
На клиенте подключение к WebSocket выглядит следующим образом:
const socket = new WebSocket("ws://localhost:8000/api/ws");
socket.ono pen = () => {
console.log("Connected to chat server");
};
socket.onmess age = (event) => {
const messageList = document.getElementById("messages");
const msgElem = document.createElement("li");
msgElem.textContent = event.data;
messageList.appendChild(msgElem);
};
function sendMessage(text: string) {
socket.send(text);
}
Особенности Fresh: клиентский JavaScript можно минимизировать, поскольку рендеринг интерфейса производится на сервере, а клиент только обрабатывает события WebSocket.
Fresh использует islands architecture, где интерактивные элементы загружаются на клиент только при необходимости. Для чата это значит:
Пример структуры компонента:
/** @jsx h */
import { h } from "preact";
import { useState, useEffect } from "preact/hooks";
export default function ChatIsland() {
const [messages, setMessages] = useState<string[]>([]);
useEffect(() => {
const socket = new WebSocket("ws://localhost:8000/api/ws");
socket.onmess age = (event) => setMessages((prev) => [...prev, event.data]);
}, []);
return (
<ul>
{messages.map((msg, i) => <li key={i}>{msg}</li>)}
</ul>
);
}
wss://.Для небольших чатов можно использовать in-memory массив, но для производственных решений:
senderId -> receiverId -> socket.Эта архитектура обеспечивает быстрый, масштабируемый и безопасный real-time чат с полным контролем над состоянием и логикой сообщений.