Alles begann mit einer Rechnung von Vercel. Nein, eigentlich fing es viel früher an – mit kleinen Frustrationen, die sich anhäuften. Die Notwendigkeit, für grundlegende Funktionen wie DDoS-Schutz, detailliertere Protokolle oder sogar eine anständige Firewall, Build-Warteschlangen usw. zu bezahlen. Das Gefühl, in einer immer teurer werdenden Lieferantenbindung gefangen zu sein.
„Und das Schlimmste: Unsere wertvollen SEO-Header wurden einfach nicht mehr auf dem Server in einer Anwendung gerendert, die den Seitenrouter verwendet. Ein echtes Problem für jeden Entwickler!?“
Aber was mich wirklich zum Umdenken brachte, war die Richtung, in die Next.js ging. Die Einführung von Usage-Client- und Usage-Server-Anweisungen, die theoretisch die Entwicklung vereinfachen sollten, in der Praxis jedoch eine weitere Ebene der Komplexität bei der Verwaltung hinzufügten. Es war, als würden wir in die Zeit von PHP zurückkehren und Dateien mit Anweisungen markieren, um ihnen mitzuteilen, wo sie ausgeführt werden sollen.
Und dabei hört es noch nicht auf. Der App Router, eine interessante Idee, aber so umgesetzt, dass ein praktisch neues Framework innerhalb von Next.js entstand. Plötzlich hatten wir zwei völlig unterschiedliche Möglichkeiten, dasselbe zu tun. Das „Alte“ und das „Neue“ – mit subtil unterschiedlichen Verhaltensweisen und versteckten Fallen.
Da wurde mir klar: Warum nicht die unglaubliche Infrastruktur von Cloudflare nutzen, mit Workern, die am Edge laufen, R2 für die Speicherung, KV für verteilte Daten ... Und natürlich den unglaublichen DDoS-Schutz, das globale CDN, die Firewall und die Regeln für Seiten und Routen und alles andere, was Cloudflare bietet.
Und das Beste: ein faires Preismodell, bei dem Sie für das bezahlen, was Sie nutzen, ohne Überraschungen.
So wurde React Edge geboren. Ein Framework, das nicht versucht, das Rad neu zu erfinden, sondern vielmehr ein wirklich einfaches und modernes Entwicklungserlebnis bietet.
Als ich mit der Entwicklung von React Edge begann, hatte ich ein klares Ziel: ein Framework zu schaffen, das Sinn macht. Sie müssen sich nicht mehr mit verwirrenden Anweisungen abmühen, müssen kein Vermögen mehr für Grundfunktionen ausgeben und, was am wichtigsten ist, Sie müssen sich nicht mehr mit der künstlichen Komplexität herumschlagen, die durch die Trennung von Client und Server entsteht. Ich wollte Geschwindigkeit, etwas, das Leistung liefert, ohne auf Einfachheit zu verzichten. Dank meiner Kenntnisse der React-API und meiner jahrelangen Erfahrung als Javascript- und Golang-Entwickler wusste ich genau, wie man mit Streams und Multiplexing umgeht, um das Rendering und die Datenverwaltung zu optimieren.
Cloudflare Workers bot mir mit seiner leistungsstarken Infrastruktur und globalen Präsenz die perfekte Umgebung, um diese Möglichkeiten zu erkunden. Ich wollte etwas, das wirklich hybrid ist, und diese Kombination aus Tools und Erfahrung hat React Edge zum Leben erweckt: ein Framework, das echte Probleme mit modernen und effizienten Lösungen löst.
React Edge bietet einen revolutionären Ansatz für die React-Entwicklung. Stellen Sie sich vor, Sie könnten eine Klasse auf dem Server schreiben und sie direkt vom Client aufrufen, mit vollständiger Typisierung und ohne Konfiguration. Stellen Sie sich ein verteiltes Caching-System vor, das „einfach funktioniert“ und die Ungültigmachung durch Tags oder Präfixe ermöglicht. Stellen Sie sich vor, Sie könnten den Status zwischen Server und Client auf transparente und sichere Weise teilen. Neben der Vereinfachung der Authentifizierung und der Bereitstellung eines effizienten Internationalisierungsansatzes, CLI und vielem mehr.
Ihre RPC-Kommunikation ist so natürlich, dass sie wie Magie wirkt – Sie schreiben Methoden in einer Klasse und rufen sie vom Client aus auf, als wären sie lokal. Das intelligente Multiplexing-System stellt sicher, dass auch bei gleichzeitigem Aufruf mehrerer Komponenten nur eine Anfrage an den Server gestellt wird. Der ephemere Cache vermeidet unnötige wiederholte Anfragen und das alles funktioniert sowohl auf dem Server als auch auf dem Client.
Einer der mächtigsten Punkte ist der app.useFetch-Hook, der das Datenabruferlebnis vereinheitlicht. Auf dem Server werden während der SSR Daten vorab geladen. Auf dem Client versorgt es sich automatisch mit diesen Daten und ermöglicht Aktualisierungen bei Bedarf. Und mit der Unterstützung für automatische Abfragen und abhängigkeitsbasierte Reaktivität war die Erstellung dynamischer Schnittstellen noch nie so einfach.
Aber das ist noch nicht alles. Das Framework bietet ein leistungsstarkes Routing-System (inspiriert vom fantastischen Hono), eine in Cloudflare R2 integrierte Asset-Verwaltung und eine elegante Möglichkeit zur Fehlerbehandlung über die HttpError-Klasse. Middleware kann Daten problemlos über einen gemeinsamen Speicher an den Client senden, und alles wird aus Sicherheitsgründen automatisch verschleiert.
Das Beeindruckendste? Fast der gesamte Code des Frameworks ist hybrid. Es gibt keine „Client“- und keine „Server“-Version – derselbe Code funktioniert in beiden Umgebungen und passt sich automatisch an den Kontext an. Der Kunde erhält nur das, was er benötigt, wodurch das endgültige Paket äußerst optimiert wird.
Und das Tüpfelchen auf dem i: All dies läuft auf der Edge-Infrastruktur von Cloudflare Workers und bietet außergewöhnliche Leistung zu fairen Kosten. Keine Überraschungen auf der Rechnung, keine grundlegenden Funktionen, die hinter erzwungenen Unternehmensplänen verborgen sind, sondern nur ein solides Framework, das es Ihnen ermöglicht, sich auf das Wesentliche zu konzentrieren: die Erstellung unglaublicher Anwendungen. Darüber hinaus nutzt React Edge das gesamte Cloudflare-Ökosystem, einschließlich Warteschlangen, langlebige Objekte, KV-Speicher und mehr, um eine robuste, skalierbare Grundlage für Ihre Anwendungen bereitzustellen.
Vite wurde als Basis verwendet, sowohl für die Entwicklungsumgebung als auch zum Testen und Erstellen. Vite ermöglicht mit seiner beeindruckenden Geschwindigkeit und modernen Architektur einen agilen und effizienten Workflow. Es beschleunigt nicht nur die Entwicklung, sondern optimiert auch den Build-Prozess und stellt sicher, dass der Code schnell und genau kompiliert wird. Ohne Zweifel war Vite die perfekte Wahl für React Edge.
Haben Sie sich jemals gefragt, wie es wäre, React-Anwendungen zu entwickeln, ohne sich um die Client/Server-Barriere kümmern zu müssen? Ohne sich Dutzende von Anweisungen wie „Use Client“ oder „Use Server“ merken zu müssen? Und noch besser: Was wäre, wenn Sie Serverfunktionen so aufrufen könnten, als wären sie lokal, mit vollständiger Eingabe und ohne Konfiguration?
Und das Beste daran: Das alles funktioniert sowohl auf dem Server als auch auf dem Client, ohne dass irgendetwas mit „Use Client“ oder „Use Server“ markiert werden muss. Das Framework weiß je nach Kontext, was zu tun ist. Sollen wir gehen?
Stellen Sie sich vor, Sie könnten Folgendes tun:
// No servidor class UserAPI extends Rpc { async searchUsers(query: string, filters: UserFilters) { // Validação com Zod const validated = searchSchema.parse({ query, filters }); return this.db.users.search(validated); } } // No cliente const UserSearch = () => { const { rpc } = app.useContext<App.Context>(); // TypeScript sabe exatamente o que searchUsers aceita e retorna! const { data, loading, error, fetch: retry } = app.useFetch( async (ctx) => ctx.rpc.searchUsers('John', { age: '>18' }) ); };
// pages/api/search.ts export default async handler = (req, res) => { // Configurar CORS // Validar request // Tratar erros // Serializar resposta // ...100 linhas depois... } // app/search/page.tsx 'use client'; import { useEffect, useState } from 'react'; export default const SearchPage = () => { const [search, setSearch] = useState(''); const [filters, setFilters] = useState({}); const [data, setData] = useState(null); const [loading, setLoading] = useState(false); useEffect(() => { let timeout; const doSearch = async () => { setLoading(true); try { const res = await fetch('/api/search?' + new URLSearchParams({ q: search, ...filters })); if (!res.ok) throw new Error('Search failed'); setData(await res.json()); } catch (err) { console.error(err); } finally { setLoading(false); } }; timeout = setTimeout(doSearch, 300); return () => clearTimeout(timeout); }, [search, filters]); // ... resto do componente }
Vergessen Sie alles, was Sie über das Abrufen von Daten in React wissen. app.useFetch von React Edge bringt einen völlig neuen und leistungsstarken Ansatz. Stellen Sie sich einen Haken vor, der:
Sehen wir uns das in Aktion an:
// No servidor class UserAPI extends Rpc { async searchUsers(query: string, filters: UserFilters) { // Validação com Zod const validated = searchSchema.parse({ query, filters }); return this.db.users.search(validated); } } // No cliente const UserSearch = () => { const { rpc } = app.useContext<App.Context>(); // TypeScript sabe exatamente o que searchUsers aceita e retorna! const { data, loading, error, fetch: retry } = app.useFetch( async (ctx) => ctx.rpc.searchUsers('John', { age: '>18' }) ); };
Das obige Beispiel verbirgt eine leistungsstarke Funktion: intelligentes Multiplexing. Wenn Sie ctx.rpc.batch verwenden, führt React Edge die Aufrufe nicht nur stapelweise durch, sondern dedupliziert automatisch identische Aufrufe:
// pages/api/search.ts export default async handler = (req, res) => { // Configurar CORS // Validar request // Tratar erros // Serializar resposta // ...100 linhas depois... } // app/search/page.tsx 'use client'; import { useEffect, useState } from 'react'; export default const SearchPage = () => { const [search, setSearch] = useState(''); const [filters, setFilters] = useState({}); const [data, setData] = useState(null); const [loading, setLoading] = useState(false); useEffect(() => { let timeout; const doSearch = async () => { setLoading(true); try { const res = await fetch('/api/search?' + new URLSearchParams({ q: search, ...filters })); if (!res.ok) throw new Error('Search failed'); setData(await res.json()); } catch (err) { console.error(err); } finally { setLoading(false); } }; timeout = setTimeout(doSearch, 300); return () => clearTimeout(timeout); }, [search, filters]); // ... resto do componente }
Einer der beeindruckendsten Teile ist, wie useFetch mit SSR umgeht:
// Primeiro, definimos nossa API no servidor class PropertiesAPI extends Rpc { async searchProperties(filters: PropertyFilters) { const results = await this.db.properties.search(filters); // Cache automático por 5 minutos return this.createResponse(results, { cache: { ttl: 300, tags: ['properties'] } }); } async getPropertyDetails(ids: string[]) { return Promise.all( ids.map(id => this.db.properties.findById(id)) ); } } // Agora, no cliente, a mágica acontece const PropertySearch = () => { const [filters, setFilters] = useState<PropertyFilters>({ price: { min: 100000, max: 500000 }, bedrooms: 2 }); // Busca reativa com debounce inteligente const { data: searchResults, loading: searchLoading, error: searchError } = app.useFetch( async (ctx) => ctx.rpc.searchProperties(filters), { // Quando filters muda, refaz a busca deps: [filters], // Mas espera 300ms de 'silêncio' antes de buscar depsDebounce: { filters: 300 } } ); // Agora, vamos buscar os detalhes das propriedades encontradas const { data: propertyDetails, loading: detailsLoading, fetch: refreshDetails } = app.useFetch( async (ctx) => { if (!searchResults?.length) return null; // Isso parece fazer múltiplas chamadas, mas... return ctx.rpc.batch([ // Na verdade, tudo é multiplexado em uma única requisição! ...searchResults.map(result => ctx.rpc.getPropertyDetails(result.id) ) ]); }, { // Atualiza sempre que searchResults mudar deps: [searchResults] } ); // Interface bonita e responsiva return ( <div> <FiltersPanel value={filters} onChange={setFilters} disabled={searchLoading} /> {searchError && ( <Alert status='error'> Erro na busca: {searchError.message} </Alert> )} <PropertyGrid items={propertyDetails || []} loading={detailsLoading} onRefresh={() => refreshDetails()} /> </div> ); };
Das RPC-System von React Edge wurde im Hinblick auf Sicherheit und Kapselung entwickelt. Nicht alles in einer RPC-Klasse wird dem Client automatisch angezeigt:
const PropertyListingPage = () => { const { data } = app.useFetch(async (ctx) => { // Mesmo que você faça 100 chamadas idênticas... return ctx.rpc.batch([ ctx.rpc.getProperty('123'), ctx.rpc.getProperty('123'), // mesma chamada ctx.rpc.getProperty('456'), ctx.rpc.getProperty('456'), // mesma chamada ]); }); // Mas na realidade: // 1. O batch agrupa todas as chamadas em UMA única requisição HTTP // 2. Chamadas idênticas são deduplicas automaticamente // 3. O resultado é distribuído corretamente para cada posição do array // 4. A tipagem é mantida para cada resultado individual! // Entao.. // 1. getProperty('123') // 2. getProperty('456') // E os resultados são distribuídos para todos os chamadores! };
Eine der leistungsstärksten Funktionen von RPC ist die Möglichkeit, APIs in Hierarchien zu organisieren:
const ProductPage = ({ productId }: Props) => { const { data, loaded, loading, error } = app.useFetch( async (ctx) => ctx.rpc.getProduct(productId), { // Controle fino de quando executar shouldFetch: ({ worker, loaded }) => { // No worker (SSR): sempre busca if (worker) return true; // No cliente: só busca se não tiver dados return !loaded; } } ); // No servidor: // 1. useFetch faz a chamada RPC // 2. Dados são serializados e enviados ao cliente // 3. Componente renderiza com os dados // No cliente: // 1. Componente hidrata com os dados do servidor // 2. Não faz nova chamada (shouldFetch retorna false) // 3. Se necessário, pode refazer a chamada com data.fetch() return ( <Suspense fallback={<ProductSkeleton />}> <ProductView product={data} loading={loading} error={error} /> </Suspense> ); };
Die Organisation von APIs in Hierarchien bringt mehrere Vorteile mit sich:
Das RPC-System von React Edge macht die Client-Server-Kommunikation so natürlich, dass Sie fast vergessen, dass Sie Remote-Anrufe tätigen. Und mit der Möglichkeit, APIs in Hierarchien zu organisieren, können Sie komplexe Strukturen erstellen und gleichzeitig Ihren Code organisiert und sicher halten.
React Edge bietet ein elegantes und flexibles Internationalisierungssystem, das variable Interpolation und komplexe Formatierung ohne umfangreiche Bibliotheken unterstützt.
class PaymentsAPI extends Rpc { // Propriedades nunca são expostas private stripe = new Stripe(process.env.STRIPE_KEY); // Métodos começando com $ são privados private async $validateCard(card: CardInfo) { return await this.stripe.cards.validate(card); } // Métodos começando com _ também são privados private async _processPayment(amount: number) { return await this.stripe.charges.create({ amount }); } // Este método é público e acessível via RPC async createPayment(orderData: OrderData) { // Validação interna usando método privado const validCard = await this.$validateCard(orderData.card); if (!validCard) { throw new HttpError(400, 'Invalid card'); } // Processamento usando outro método privado const payment = await this._processPayment(orderData.amount); return payment; } } // No cliente: const PaymentForm = () => { const { rpc } = app.useContext<App.Context>(); // ✅ Isso funciona const handleSubmit = () => rpc.createPayment(data); // ❌ Isso não é possível - métodos privados não são expostos const invalid1 = () => rpc.$validateCard(data); const invalid2 = () => rpc._processPayment(100); // ❌ Isso também não funciona - propriedades não são expostas const invalid3 = () => rpc.stripe; };
Verwendung im Code:
// APIs aninhadas para melhor organização class UsersAPI extends Rpc { // Subclasse para gerenciar preferences preferences = new UserPreferencesAPI(); // Subclasse para gerenciar notificações notifications = new UserNotificationsAPI(); async getProfile(id: string) { return this.db.users.findById(id); } } class UserPreferencesAPI extends Rpc { async getTheme(userId: string) { return this.db.preferences.getTheme(userId); } async setTheme(userId: string, theme: Theme) { return this.db.preferences.setTheme(userId, theme); } } class UserNotificationsAPI extends Rpc { // Métodos privados continuam privados private async $sendPush(userId: string, message: string) { await this.pushService.send(userId, message); } async getSettings(userId: string) { return this.db.notifications.getSettings(userId); } async notify(userId: string, notification: Notification) { const settings = await this.getSettings(userId); if (settings.pushEnabled) { await this.$sendPush(userId, notification.message); } } } // No cliente: const UserProfile = () => { const { rpc } = app.useContext<App.Context>(); const { data: profile } = app.useFetch( async (ctx) => { // Chamadas aninhadas são totalmente tipadas const [user, theme, notificationSettings] = await ctx.rpc.batch([ // Método da classe principal ctx.rpc.getProfile('123'), // Método da subclasse de preferências ctx.rpc.preferences.getTheme('123'), // Método da subclasse de notificações ctx.rpc.notifications.getSettings('123') ]); return { user, theme, notificationSettings }; } ); // ❌ Métodos privados continuam inacessíveis const invalid = () => rpc.notifications.$sendPush('123', 'hello'); };
React Edge erkennt und lädt Ihre Übersetzungen automatisch und kann Benutzereinstellungen problemlos in Cookies speichern. Aber damit haben Sie schon gerechnet, oder?
// No servidor class UserAPI extends Rpc { async searchUsers(query: string, filters: UserFilters) { // Validação com Zod const validated = searchSchema.parse({ query, filters }); return this.db.users.search(validated); } } // No cliente const UserSearch = () => { const { rpc } = app.useContext<App.Context>(); // TypeScript sabe exatamente o que searchUsers aceita e retorna! const { data, loading, error, fetch: retry } = app.useFetch( async (ctx) => ctx.rpc.searchUsers('John', { age: '>18' }) ); };
Authentifizierung war schon immer ein Problem bei Webanwendungen. Verwaltung von JWT-Tokens, sichere Cookies, erneute Validierung – all dies erfordert normalerweise eine Menge Boilerplate-Code. React Edge ändert dies komplett.
Sehen Sie, wie einfach es ist, ein vollständiges Authentifizierungssystem zu implementieren:
// pages/api/search.ts export default async handler = (req, res) => { // Configurar CORS // Validar request // Tratar erros // Serializar resposta // ...100 linhas depois... } // app/search/page.tsx 'use client'; import { useEffect, useState } from 'react'; export default const SearchPage = () => { const [search, setSearch] = useState(''); const [filters, setFilters] = useState({}); const [data, setData] = useState(null); const [loading, setLoading] = useState(false); useEffect(() => { let timeout; const doSearch = async () => { setLoading(true); try { const res = await fetch('/api/search?' + new URLSearchParams({ q: search, ...filters })); if (!res.ok) throw new Error('Search failed'); setData(await res.json()); } catch (err) { console.error(err); } finally { setLoading(false); } }; timeout = setTimeout(doSearch, 300); return () => clearTimeout(timeout); }, [search, filters]); // ... resto do componente }
// Primeiro, definimos nossa API no servidor class PropertiesAPI extends Rpc { async searchProperties(filters: PropertyFilters) { const results = await this.db.properties.search(filters); // Cache automático por 5 minutos return this.createResponse(results, { cache: { ttl: 300, tags: ['properties'] } }); } async getPropertyDetails(ids: string[]) { return Promise.all( ids.map(id => this.db.properties.findById(id)) ); } } // Agora, no cliente, a mágica acontece const PropertySearch = () => { const [filters, setFilters] = useState<PropertyFilters>({ price: { min: 100000, max: 500000 }, bedrooms: 2 }); // Busca reativa com debounce inteligente const { data: searchResults, loading: searchLoading, error: searchError } = app.useFetch( async (ctx) => ctx.rpc.searchProperties(filters), { // Quando filters muda, refaz a busca deps: [filters], // Mas espera 300ms de 'silêncio' antes de buscar depsDebounce: { filters: 300 } } ); // Agora, vamos buscar os detalhes das propriedades encontradas const { data: propertyDetails, loading: detailsLoading, fetch: refreshDetails } = app.useFetch( async (ctx) => { if (!searchResults?.length) return null; // Isso parece fazer múltiplas chamadas, mas... return ctx.rpc.batch([ // Na verdade, tudo é multiplexado em uma única requisição! ...searchResults.map(result => ctx.rpc.getPropertyDetails(result.id) ) ]); }, { // Atualiza sempre que searchResults mudar deps: [searchResults] } ); // Interface bonita e responsiva return ( <div> <FiltersPanel value={filters} onChange={setFilters} disabled={searchLoading} /> {searchError && ( <Alert status='error'> Erro na busca: {searchError.message} </Alert> )} <PropertyGrid items={propertyDetails || []} loading={detailsLoading} onRefresh={() => refreshDetails()} /> </div> ); };
Null-Boilerplate
Standardmäßige Sicherheit
Schließen Sie die Eingabe ab
Nahtlose Integration
const PropertyListingPage = () => { const { data } = app.useFetch(async (ctx) => { // Mesmo que você faça 100 chamadas idênticas... return ctx.rpc.batch([ ctx.rpc.getProperty('123'), ctx.rpc.getProperty('123'), // mesma chamada ctx.rpc.getProperty('456'), ctx.rpc.getProperty('456'), // mesma chamada ]); }); // Mas na realidade: // 1. O batch agrupa todas as chamadas em UMA única requisição HTTP // 2. Chamadas idênticas são deduplicas automaticamente // 3. O resultado é distribuído corretamente para cada posição do array // 4. A tipagem é mantida para cada resultado individual! // Entao.. // 1. getProperty('123') // 2. getProperty('456') // E os resultados são distribuídos para todos os chamadores! };
Eine der leistungsstärksten Funktionen von React Edge ist die Fähigkeit, den Status sicher zwischen Arbeiter und Client zu teilen. Mal sehen, wie das funktioniert:
const ProductPage = ({ productId }: Props) => { const { data, loaded, loading, error } = app.useFetch( async (ctx) => ctx.rpc.getProduct(productId), { // Controle fino de quando executar shouldFetch: ({ worker, loaded }) => { // No worker (SSR): sempre busca if (worker) return true; // No cliente: só busca se não tiver dados return !loaded; } } ); // No servidor: // 1. useFetch faz a chamada RPC // 2. Dados são serializados e enviados ao cliente // 3. Componente renderiza com os dados // No cliente: // 1. Componente hidrata com os dados do servidor // 2. Não faz nova chamada (shouldFetch retorna false) // 3. Se necessário, pode refazer a chamada com data.fetch() return ( <Suspense fallback={<ProductSkeleton />}> <ProductView product={data} loading={loading} error={error} /> </Suspense> ); };
Das Routing-System von React Edge ist von Hono inspiriert, verfügt aber über Superkräfte für SSR:
// No servidor class UserAPI extends Rpc { async searchUsers(query: string, filters: UserFilters) { // Validação com Zod const validated = searchSchema.parse({ query, filters }); return this.db.users.search(validated); } } // No cliente const UserSearch = () => { const { rpc } = app.useContext<App.Context>(); // TypeScript sabe exatamente o que searchUsers aceita e retorna! const { data, loading, error, fetch: retry } = app.useFetch( async (ctx) => ctx.rpc.searchUsers('John', { age: '>18' }) ); };
React Edge verfügt über ein leistungsstarkes Caching-System, das sowohl für JSON-Daten als auch für ganze Seiten funktioniert:
// pages/api/search.ts export default async handler = (req, res) => { // Configurar CORS // Validar request // Tratar erros // Serializar resposta // ...100 linhas depois... } // app/search/page.tsx 'use client'; import { useEffect, useState } from 'react'; export default const SearchPage = () => { const [search, setSearch] = useState(''); const [filters, setFilters] = useState({}); const [data, setData] = useState(null); const [loading, setLoading] = useState(false); useEffect(() => { let timeout; const doSearch = async () => { setLoading(true); try { const res = await fetch('/api/search?' + new URLSearchParams({ q: search, ...filters })); if (!res.ok) throw new Error('Search failed'); setData(await res.json()); } catch (err) { console.error(err); } finally { setLoading(false); } }; timeout = setTimeout(doSearch, 300); return () => clearTimeout(timeout); }, [search, filters]); // ... resto do componente }
Die Link-Komponente ist eine intelligente und leistungsstarke Lösung zum Vorladen von Ressourcen auf der Clientseite, die eine flüssigere und schnellere Navigation für Benutzer gewährleistet. Die Prefetching-Funktionalität wird aktiviert, wenn Sie mit der Maus über den Link fahren und so den Moment der Inaktivität des Benutzers nutzen, um die Zieldaten im Voraus anzufordern.
Wie funktioniert es?
Bedingter Vorabruf: Das Prefetch-Attribut (standardmäßig aktiv) steuert, ob ein Vorabladen durchgeführt wird.
Smart Cache: Ein Set wird verwendet, um die bereits vorinstallierten Links zu speichern und so redundante Aufrufe zu vermeiden.
Mauseingabe: Wenn der Benutzer mit dem Cursor über den Link fährt, prüft die handleMouseEnter-Funktion, ob ein Vorladen erforderlich ist und initiiert in diesem Fall eine Abrufanforderung an das Ziel.
Fehlersicher: Jeder Fehler in der Anfrage wird unterdrückt, um sicherzustellen, dass das Verhalten der Komponente nicht durch vorübergehende Netzwerkfehler beeinträchtigt wird.
// No servidor class UserAPI extends Rpc { async searchUsers(query: string, filters: UserFilters) { // Validação com Zod const validated = searchSchema.parse({ query, filters }); return this.db.users.search(validated); } } // No cliente const UserSearch = () => { const { rpc } = app.useContext<App.Context>(); // TypeScript sabe exatamente o que searchUsers aceita e retorna! const { data, loading, error, fetch: retry } = app.useFetch( async (ctx) => ctx.rpc.searchUsers('John', { age: '>18' }) ); };
Wenn der Benutzer mit der Maus über den Link „Über uns“ fährt, beginnt die Komponente mit dem Vorladen von Daten von der Seite „/about“, was einen fast sofortigen Übergang ermöglicht. Geniale Idee, oder? Aber ich habe es in der React.dev-Dokumentation gesehen.
app.useContext ist der grundlegende Hook von React Edge und bietet Zugriff auf den gesamten Worker-Kontext:
// pages/api/search.ts export default async handler = (req, res) => { // Configurar CORS // Validar request // Tratar erros // Serializar resposta // ...100 linhas depois... } // app/search/page.tsx 'use client'; import { useEffect, useState } from 'react'; export default const SearchPage = () => { const [search, setSearch] = useState(''); const [filters, setFilters] = useState({}); const [data, setData] = useState(null); const [loading, setLoading] = useState(false); useEffect(() => { let timeout; const doSearch = async () => { setLoading(true); try { const res = await fetch('/api/search?' + new URLSearchParams({ q: search, ...filters })); if (!res.ok) throw new Error('Search failed'); setData(await res.json()); } catch (err) { console.error(err); } finally { setLoading(false); } }; timeout = setTimeout(doSearch, 300); return () => clearTimeout(timeout); }, [search, filters]); // ... resto do componente }
Der app.useContext-Hook schließt die Lücke zwischen dem Worker und dem Client. Sie können damit Funktionen erstellen, die auf gemeinsamem Status, sicherem Datenabruf und kontextbezogenem Rendering basieren, ohne sich wiederholenden Code. Dies vereinfacht komplexe Anwendungen, wodurch sie einfacher zu warten und schneller zu entwickeln sind.
Der app.useUrlState-Hook sorgt dafür, dass der Status Ihrer Anwendung mit URL-Parametern synchronisiert wird, sodass Sie präzise steuern können, was in der URL enthalten ist, wie der Status serialisiert wird und wann er aktualisiert wird.
// Primeiro, definimos nossa API no servidor class PropertiesAPI extends Rpc { async searchProperties(filters: PropertyFilters) { const results = await this.db.properties.search(filters); // Cache automático por 5 minutos return this.createResponse(results, { cache: { ttl: 300, tags: ['properties'] } }); } async getPropertyDetails(ids: string[]) { return Promise.all( ids.map(id => this.db.properties.findById(id)) ); } } // Agora, no cliente, a mágica acontece const PropertySearch = () => { const [filters, setFilters] = useState<PropertyFilters>({ price: { min: 100000, max: 500000 }, bedrooms: 2 }); // Busca reativa com debounce inteligente const { data: searchResults, loading: searchLoading, error: searchError } = app.useFetch( async (ctx) => ctx.rpc.searchProperties(filters), { // Quando filters muda, refaz a busca deps: [filters], // Mas espera 300ms de 'silêncio' antes de buscar depsDebounce: { filters: 300 } } ); // Agora, vamos buscar os detalhes das propriedades encontradas const { data: propertyDetails, loading: detailsLoading, fetch: refreshDetails } = app.useFetch( async (ctx) => { if (!searchResults?.length) return null; // Isso parece fazer múltiplas chamadas, mas... return ctx.rpc.batch([ // Na verdade, tudo é multiplexado em uma única requisição! ...searchResults.map(result => ctx.rpc.getPropertyDetails(result.id) ) ]); }, { // Atualiza sempre que searchResults mudar deps: [searchResults] } ); // Interface bonita e responsiva return ( <div> <FiltersPanel value={filters} onChange={setFilters} disabled={searchLoading} /> {searchError && ( <Alert status='error'> Erro na busca: {searchError.message} </Alert> )} <PropertyGrid items={propertyDetails || []} loading={detailsLoading} onRefresh={() => refreshDetails()} /> </div> ); };
Anfangszustand
Optionen:
Der app.useStorageState-Hook ermöglicht es Ihnen, den Status im Browser mithilfe von localStorage oder sessionStorage mit vollständiger Tippunterstützung beizubehalten.
// No servidor class UserAPI extends Rpc { async searchUsers(query: string, filters: UserFilters) { // Validação com Zod const validated = searchSchema.parse({ query, filters }); return this.db.users.search(validated); } } // No cliente const UserSearch = () => { const { rpc } = app.useContext<App.Context>(); // TypeScript sabe exatamente o que searchUsers aceita e retorna! const { data, loading, error, fetch: retry } = app.useFetch( async (ctx) => ctx.rpc.searchUsers('John', { age: '>18' }) ); };
Reaktive Werte mühelos entprellen:
// pages/api/search.ts export default async handler = (req, res) => { // Configurar CORS // Validar request // Tratar erros // Serializar resposta // ...100 linhas depois... } // app/search/page.tsx 'use client'; import { useEffect, useState } from 'react'; export default const SearchPage = () => { const [search, setSearch] = useState(''); const [filters, setFilters] = useState({}); const [data, setData] = useState(null); const [loading, setLoading] = useState(false); useEffect(() => { let timeout; const doSearch = async () => { setLoading(true); try { const res = await fetch('/api/search?' + new URLSearchParams({ q: search, ...filters })); if (!res.ok) throw new Error('Search failed'); setData(await res.json()); } catch (err) { console.error(err); } finally { setLoading(false); } }; timeout = setTimeout(doSearch, 300); return () => clearTimeout(timeout); }, [search, filters]); // ... resto do componente }
Behalten Sie Arrays mit eindeutigen Werten bei, indem Sie Folgendes eingeben:
app.useDistinct ist ein Hook, der darauf spezialisiert ist, zu erkennen, wann sich ein Wert tatsächlich geändert hat, mit Unterstützung für Tiefenvergleiche und Entprellen:
// No servidor class UserAPI extends Rpc { async searchUsers(query: string, filters: UserFilters) { // Validação com Zod const validated = searchSchema.parse({ query, filters }); return this.db.users.search(validated); } } // No cliente const UserSearch = () => { const { rpc } = app.useContext<App.Context>(); // TypeScript sabe exatamente o que searchUsers aceita e retorna! const { data, loading, error, fetch: retry } = app.useFetch( async (ctx) => ctx.rpc.searchUsers('John', { age: '>18' }) ); };
React Edge-Hooks wurden so konzipiert, dass sie harmonisch funktionieren und ein flüssiges und typisiertes Entwicklungserlebnis bieten. Durch die Kombination können Sie komplexe und reaktive Schnittstellen mit viel weniger Code erstellen.
Die React Edge CLI wurde entwickelt, um Entwicklern das Leben zu erleichtern, indem sie wichtige Tools in einer einzigen, intuitiven Oberfläche zusammenführt. Unabhängig davon, ob Sie Anfänger oder Experte sind, stellt die CLI sicher, dass Sie Projekte effizient und problemlos konfigurieren, entwickeln, testen und bereitstellen können.
Hauptfunktionen
Ich bin stolz, Ihnen mitteilen zu können, dass die erste Produktionsanwendung mit React Edge jetzt funktioniert! Hierbei handelt es sich um ein brasilianisches Immobilienunternehmen, Lopes Imóveis, das bereits die gesamte Leistung und Flexibilität des Frameworks nutzt.
Auf der Website der Immobilienagentur werden Immobilien in den Cache geladen, um die Suche zu optimieren und den Benutzern ein flüssigeres Erlebnis zu bieten. Da es sich um eine äußerst dynamische Website handelt, verwendet der Routen-Cache eine TTL von nur 10 Sekunden, kombiniert mit der Stale-While-Revalidate-Strategie. Dadurch wird sichergestellt, dass die Site auch bei erneuten Validierungen im Hintergrund aktuelle Daten mit außergewöhnlicher Leistung liefert.
Darüber hinaus werden Empfehlungen für ähnliche Eigenschaften effizient und gelegentlich im Hintergrund berechnet und mithilfe des in RPC integrierten Cache-Systems direkt im Cache von Cloudflare gespeichert. Dieser Ansatz verkürzt die Reaktionszeit bei nachfolgenden Anfragen und macht Abfrageempfehlungen nahezu augenblicklich. Darüber hinaus werden alle Bilder auf Cloudflare R2 gespeichert und bieten so skalierbaren und verteilten Speicher, ohne auf externe Anbieter angewiesen zu sein.
Und bald werden wir auch ein gigantisches automatisiertes Marketingprojekt für Easy Auth starten, das das Potenzial dieser Technologie weiter demonstriert.
Und so, liebe Leser, sind wir am Ende dieses Abenteuers durch das React Edge-Universum angelangt! Ich weiß, dass es immer noch eine Menge unglaublicher Dinge zu erforschen gibt, wie zum Beispiel die einfachsten Authentifizierungen wie Basic und Bearer und andere kleine Geheimnisse, die das tägliche Leben eines Entwicklers viel glücklicher machen. Aber beruhige dich! Die Idee besteht darin, in Zukunft ausführlichere Artikel zu veröffentlichen, die sich mit den einzelnen Funktionen befassen.
Und, Spoiler: Bald wird React Edge Open Source sein und ordnungsgemäß dokumentiert sein! Entwicklung, Arbeit, Schreiben und ein wenig soziales Leben unter einen Hut zu bringen, ist nicht einfach, aber die Aufregung, dieses Wunder in Aktion zu sehen, insbesondere mit der absurden Geschwindigkeit, die die Infrastruktur von Cloudflare bietet, ist der Treibstoff, der mich bewegt. Halten Sie also Ihre Ängste zurück, denn das Beste kommt noch! ?
Wenn Sie in der Zwischenzeit sofort mit der Erkundung und dem Testen beginnen möchten, ist das Paket jetzt auf NPM verfügbar: React Edge auf NPM.
Meine E-Mail lautet feliperohdee@gmail.com und ich bin immer offen für Feedback, das ist erst der Anfang dieser Reise, für Vorschläge und konstruktive Kritik. Wenn Ihnen das, was Sie gelesen haben, gefallen hat, teilen Sie es mit Ihren Freunden und Kollegen und halten Sie Ausschau nach neuen Dingen. Vielen Dank, dass Sie mir bis hierher gefolgt sind, und bis zum nächsten Mal! ???
Das obige ist der detaillierte Inhalt vonVon Next.js zu React Edge mit Cloudflare Workers: Eine Befreiungsgeschichte. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!