Le temps réel inclut
Une inclusion peut s'abonner à une chaîne en temps réel. Au premier chargement, il s'affiche côté serveur comme n'importe quelle autre inclusion ; après cela, un abonnement SSE côté client récupère le fragment chaque fois que le serveur publie sur le canal.
<bext-include src="/partials/order-status"
realtime="orders:123"
></bext-include>
Pas de cadre, pas de gestion étatique. Juste un <div> qui se met à jour lorsque le serveur le demande.
Attributs#
| Attribut | But |
|---|---|
realtime |
Nom de la chaîne/du sujet auquel s'abonner |
realtime-mode |
replace (par défaut), append, prepend |
realtime-event |
Filtrer sur un type d'événement spécifique |
Comment ça marche#
Sur SSR : L'inclusion se résout normalement – HTML complet côté serveur. bext encapsule le résultat dans un conteneur avec des métadonnées en temps réel :
<div data-bext-realtime="orders:123"
data-bext-include-src="/partials/order-status"
data-bext-realtime-mode="replace">
<p>Order #123: Preparing</p>
</div>
Sur le client : bext injecte un petit script en ligne (une fois par page) qui découvre tous les éléments [data-bext-realtime] et s'abonne à leurs chaînes via EventSource :
// Injected automatically when any realtime include is present
var es = new EventSource('/__bext/realtime/sse?topic=orders:123');
es.onmessage = function(ev) {
// Re-fetch the include HTML
fetch('/__bext/include?src=/partials/order-status')
.then(r => r.text())
.then(html => {
el.innerHTML = html; // or append/prepend based on mode
});
};
Lors de la publication : Tout code côté serveur qui met à jour les données sous-jacentes est publié sur le canal :
realtime_hub.publish("orders:123", json!({
"event": "order-update",
"status": "shipped"
}));
Tous les clients abonnés récupèrent automatiquement l'inclusion.
Modes de mise à jour#
Contrôlez la manière dont les mises à jour s'appliquent au DOM :
<!-- Replace: new content replaces current (default) -->
<bext-include src="/partials/status"
realtime="orders:123"
realtime-mode="replace"
></bext-include>
<!-- Append: new content added to the end (live feeds) -->
<bext-include src="/partials/latest-message"
realtime="chat:42"
realtime-mode="append"
></bext-include>
<!-- Prepend: new content added to the start (notifications) -->
<bext-include src="/partials/notification"
realtime="user:42:notifs"
realtime-mode="prepend"
></bext-include>
Filtrage des événements#
Un canal peut diffuser plusieurs types d'événements. Utilisez realtime-event pour vous abonner à un seul :
<!-- Only re-render when the server publishes event="price-change" -->
<bext-include src="/partials/price-tag"
realtime="stock:AAPL"
realtime-event="price-change"
></bext-include>
<!-- Only re-render when event="volume-update" -->
<bext-include src="/partials/volume-meter"
realtime="stock:AAPL"
realtime-event="volume-update"
></bext-include>
Tous deux sont abonnés à stock:AAPL mais react à différents événements. Le serveur publie :
realtime_hub.publish("stock:AAPL", json!({
"event": "price-change",
"price": 171.25
}));
Seul le price-tag inclut les nouvelles récupérations. Le volume-meter ignore cet événement.
Plusieurs inclusions partagent le même canal sur une page ouverte , une connexion EventSource, pas une par inclusion. Lorsqu'une publication se déclenche, toutes sont récupérées via le point de terminaison /__bext/include/batch en un seul aller-retour.
Autorisation de chaîne#
Le hub en temps réel applique des règles d'authentification par canal (même système que les autres abonnements en temps réel) :
[realtime.auth]
# User-specific channels require authentication
"user:*" = { auth = "required" }
# Admin-only channels
"admin:*" = { auth = "required", role = "admin" }
# Public channels — no auth needed
"public:*" = { auth = "none" }
Les abonnés non autorisés reçoivent 403 Forbidden du point de terminaison SSE. Le SSR de l'inclusion se produit toujours (sous réserve du propre attribut auth de l'inclusion) — seul l'abonnement en direct est bloqué.
Le canal en temps réel autorise l'abonnement en direct séparément du propre attribut auth de l'inclusion. Une chaîne publique ( auth = "none" ) peut toujours être utilisée avec une inclusion auth="required" – mais l'abonnement lui-même sera ouvert à tous.
Couplage avec la mise en cache#
Le SSR initial d'une inclusion en temps réel peut toujours être mis en cache avec une courte durée de vie pour absorber les rafales de requêtes :
<bext-include src="/partials/live-scores"
realtime="scores:match-42"
ttl="5s"
></bext-include>
- La première requête est résolue et mise en cache pendant 5 secondes.
- Les chargements initiaux ultérieurs de la page (au cours de la fenêtre de 5 secondes) ont atteint le cache.
- L'abonnement côté client conserve le contenu à jour après le chargement.
N'utilisez pas de longues durées de vie : le cache est un filet de sécurité, pas la source de vérité. La chaîne en temps réel est ce qui maintient le contenu à jour.
Gestion des connexions#
Plusieurs inclusions sur la même page partageant la même chaîne partagent une seule connexion :
<bext-include src="/partials/order-header" realtime="orders:123"></bext-include>
<bext-include src="/partials/order-timeline" realtime="orders:123"></bext-include>
<bext-include src="/partials/order-total" realtime="orders:123"></bext-include>
Un EventSource est ouvert pour orders:123 ; les trois incluent l’écoute et la récupération lorsqu’un événement arrive. Le point de terminaison par lots /__bext/include/batch résout les trois en une seule requête :
POST /__bext/include/batch
[
{ "src": "/partials/order-header" },
{ "src": "/partials/order-timeline" },
{ "src": "/partials/order-total" }
]
Renvoie un tableau de chaînes HTML résolues, chacune échangée dans son élément respectif.
Reconnexion côté client#
Le script injecté gère la reconnexion automatique :
- Lors de la déconnexion, il attend 1 à 5 secondes (intervalle exponentiel) et se reconnecte.
- Côté serveur, le hub temps réel de bext reprend l'abonnement en toute transparence.
- Les événements en cours qui se sont produits lors de la déconnexion sont relus si le client envoie un en-tête
Last-Event-ID(comportement SSE standard).
Performance#
- Un EventSource par canal unique par page (pas par inclusion).
- La récupération utilise
/__bext/include?src=...qui respecte le cache de l'inclusion. - Les publications côté serveur sont diffusées via la pub/sub interne du hub en temps réel.
- Avec le backend Redis, les publications se propagent sur des instances à l'échelle horizontale.
Exemple : tableau de bord en direct#
<body>
<h1>Dashboard</h1>
<section>
<h2>Active orders</h2>
<bext-include src="/partials/active-order-count"
realtime="system:orders"
ttl="10s"
></bext-include>
</section>
<section>
<h2>Recent events</h2>
<bext-include src="/partials/event-feed"
realtime="system:events"
realtime-mode="prepend"
>Loading events...</bext-include>
</section>
<section>
<h2>Server health</h2>
<bext-include src="/partials/health-metrics"
realtime="system:health"
realtime-event="alert"
ttl="30s"
></bext-include>
</section>
</body>
Trois canaux différents, trois modes de mise à jour différents, tous auto-mis à jour. Pas de framework de tableau de bord, pas de code de gestion WebSocket.
Voir aussi#
- Hub en temps réel — l'infrastructure SSE/WS sous-jacente
- WebSockets et SSE — quand utiliser chaque transport
- La mise en cache inclut — association du cache avec le temps réel