Ein Datum, das im Browser gestern war
Vue SSR schrieb 14 Jul in UTC. Der Client schrieb 13 Jul in Berlin. Hydration fiel, die Seite renderte zweimal.
Case-Kacheln blitzten beim ersten Paint. Vue warnte, dass das Server-HTML nicht passte. Der Mismatch war ein Kalendertag. Der vue-Container hat TZ=UTC. Intl.DateTimeFormat in Node druckte 14 Jul 2026. Der Browser in Berlin druckte 13 Jul 2026 für denselben ISO-Zeitstempel aus der API. Hydration verglich Textknoten und gab auf. Der Client renderte den Baum erneut. TTFB sah gut aus. Zeit bis zur stabilen Seite nicht.
Dieselbe Fehlerklasse traf Locale. SSR nutzte die Locale aus dem Pfad. Ein Erstbesuch ohne Cookie las trotzdem navigator.language auf dem Client, wenn ein Helper das in setup las. en auf dem Server, ru im Browser, zwei Wörterbücher, Hydration fehl.
Das Problem war, denselben Zeitpunkt in zwei Uhren zu formatieren. Besucher sahen einen Flash und ein doppeltes Render. Ich brauchte den Anzeige-String aus SSR im serialisierten State und kein Intl bei mount.
Auf dem Server formatieren, den String schicken
PHP sendet published_at als ISO-8601 UTC. Der SSR-Entry formatiert einmal für die Request-Locale und legt den Anzeige-String in den serialisierten State. Der Client druckt diesen String. Er ruft Intl bei mount nicht. Ein Sprachwechsel ist eine neue URL und ein neues Dokument. So arbeitet i18n auf dieser Site schon.
- Daten, die der Besucheruhr folgen müssen, falls es sie gibt, sind client-only und im SSR-HTML leer.
- Die TZ des vue-Prozesses bleibt UTC. Europe/Berlin in Docker zu raten repariert ein Büro und bricht einen Crawler.
- Hydration-Warnungen im vue-Log sind Deploy-Fehler, kein Konsolenrauschen.
Der Flash war weg. Das Dokument im ersten HTML ist das Dokument nach Hydrate. Doppeltes Rendern war der Preis, ein Datum an zwei Stellen zu formatieren.
Was ich daraus mitnehme
Hydration vergleicht Text, keine Zeitpunkte. Zwei Zonen sind zwei Strings, auch wenn das ISO gleich ist.
Einmal auf dem Server formatieren. Den String schicken. Intl bei mount ist ein zweiter Kalender, den ich nicht wollte.
navigator.language in setup ist ein Locale-Mismatch beim Erstbesuch. Die Pfad-Locale ist die Quelle.
