Semuanya bermula dengan invois Vercel. Tidak, sebenarnya, ia bermula lebih awal—dengan kekecewaan kecil bertimbun. Keperluan membayar untuk ciri asas seperti perlindungan DDoS, log yang lebih terperinci, atau juga tembok api yang baik, membina baris gilir, dll. Perasaan terperangkap dalam kunci masuk vendor yang semakin mahal.
"Dan yang paling teruk: pengepala SEO berharga kami hanya berhenti memaparkan pada pelayan dalam aplikasi menggunakan penghala halaman. Sakit kepala yang benar untuk mana-mana pembangun! ?"
Tetapi apa yang benar-benar membuatkan saya memikirkan semula segala-galanya ialah arah yang dituju Next.js. Pengenalan penggunaan klien, gunakan pelayan—arahan yang, secara teori, harus memudahkan pembangunan tetapi, dalam amalan, menambah satu lagi lapisan kerumitan untuk diurus. Ia seperti kembali ke zaman PHP, menandai fail dengan arahan untuk menentukan di mana ia harus dijalankan.
Dan ia tidak berhenti di situ. Penghala Apl—idea yang menarik tetapi dilaksanakan dengan cara yang mencipta rangka kerja yang hampir sepenuhnya baharu dalam Next.js. Tiba-tiba, terdapat dua cara yang sama sekali berbeza untuk melakukan perkara yang sama—'lama' dan 'baharu'—dengan gelagat yang berbeza secara halus dan perangkap tersembunyi.
Ketika itulah saya tersentuh: mengapa tidak memanfaatkan infrastruktur Cloudflare yang luar biasa dengan Pekerja yang berjalan di tepi, R2 untuk penyimpanan, KV untuk data yang diedarkan... Bersama-sama dengan, sudah tentu, perlindungan DDoS yang menakjubkan, CDN global, firewall , peraturan halaman dan penghalaan, dan segala-galanya yang Cloudflare tawarkan.
Dan bahagian yang terbaik: model penetapan harga yang adil di mana anda membayar untuk apa yang anda gunakan, tanpa kejutan.
Beginilah cara React Edge dilahirkan. Rangka kerja yang tidak bertujuan untuk mencipta semula roda sebaliknya memberikan pengalaman pembangunan yang benar-benar mudah dan moden.
Apabila saya mula membangunkan React Edge, saya mempunyai matlamat yang jelas: untuk mencipta rangka kerja yang masuk akal. Tiada lagi bergelut dengan arahan yang mengelirukan, tiada lagi bayaran yang terlalu tinggi untuk ciri asas, dan yang paling penting, tiada lagi berurusan dengan kerumitan buatan yang disebabkan oleh pemisahan pelanggan/pelayan. Saya mahukan kelajuan—rangka kerja yang memberikan prestasi tanpa mengorbankan kesederhanaan. Dengan memanfaatkan pengetahuan saya tentang API React dan pengalaman bertahun-tahun sebagai pembangun JavaScript dan Golang, saya tahu dengan tepat cara mengendalikan strim dan pemultipleksan untuk mengoptimumkan pemaparan dan pengurusan data.
Cloudflare Workers, dengan infrastruktur yang berkuasa dan kehadiran globalnya, menyediakan persekitaran yang sempurna untuk meneroka kemungkinan ini. Saya mahukan sesuatu yang benar-benar hibrid, dan gabungan alatan serta pengalaman ini menghidupkan React Edge: rangka kerja yang menyelesaikan masalah dunia sebenar dengan penyelesaian moden dan cekap.
React Edge memperkenalkan pendekatan revolusioner untuk pembangunan React. Bayangkan menulis kelas pada pelayan dan memanggilnya terus daripada klien, dengan keselamatan jenis penuh dan konfigurasi sifar. Bayangkan sistem caching yang diedarkan yang "hanya berfungsi", membenarkan ketidaksahihan oleh teg atau awalan. Bayangkan berkongsi keadaan dengan lancar dan selamat antara pelayan dan pelanggan. Tambahkan pengesahan yang dipermudahkan, sistem pengantarabangsaan yang cekap, CLI dan banyak lagi.
Komunikasi RPCnya terasa hampir ajaib—anda menulis kaedah dalam kelas dan memanggilnya daripada pelanggan seolah-olah kaedah itu tempatan. Sistem pemultipleksan pintar memastikan bahawa walaupun berbilang komponen membuat panggilan yang sama, hanya satu permintaan dihantar ke pelayan. Caching sementara mengelakkan permintaan berulang yang tidak perlu, dan semuanya berfungsi dengan lancar pada kedua-dua pelayan dan pelanggan.
Salah satu ciri yang paling berkuasa ialah app.useFetch hook, yang menyatukan pengalaman pengambilan data. Pada pelayan, ia pramuat data semasa SSR; pada pelanggan, ia secara automatik menghidrat dengan data tersebut dan menyokong kemas kini atas permintaan. Dengan sokongan untuk tinjauan automatik dan kereaktifan berasaskan pergantungan, mencipta antara muka dinamik tidak pernah semudah ini.
Tetapi bukan itu sahaja. Rangka kerja ini menawarkan sistem penghalaan yang berkuasa (diilhamkan oleh Hono yang hebat), pengurusan aset bersepadu dengan Cloudflare R2, dan cara yang elegan untuk menangani ralat melalui kelas HttpError. Middlewares boleh menghantar data dengan mudah kepada pelanggan melalui kedai kongsi dan semuanya dikaburkan secara automatik untuk keselamatan.
Bahagian yang paling mengagumkan? Hampir semua kod rangka kerja adalah hibrid. Tiada versi "pelanggan" dan versi "pelayan"—kod yang sama berfungsi dalam kedua-dua persekitaran, menyesuaikan secara automatik kepada konteks. Pelanggan hanya menerima apa yang diperlukan, menjadikan berkas terakhir sangat dioptimumkan.
Dan icing pada kek: semua ini berjalan pada infrastruktur tepi Cloudflare Workers, memberikan prestasi luar biasa pada kos yang berpatutan. Tiada invois mengejut, tiada ciri asas yang terkunci di sebalik rancangan perusahaan yang mahal—hanya rangka kerja yang kukuh yang membolehkan anda menumpukan pada perkara yang benar-benar penting: membina aplikasi yang menakjubkan. Selain itu, React Edge memanfaatkan ekosistem Cloudflare, termasuk Baris Gilir, Objek Tahan Lama, Storan KV dan banyak lagi, menyediakan asas yang teguh dan berskala untuk aplikasi anda.
Vite digunakan sebagai asas untuk persekitaran pembangunan, ujian dan proses binaan. Dengan kelajuan yang mengagumkan dan seni bina moden, Vite membolehkan aliran kerja yang tangkas dan cekap. Ia bukan sahaja mempercepatkan pembangunan tetapi juga mengoptimumkan proses binaan, memastikan penyusunan pantas dan tepat. Tidak syak lagi, Vite ialah pilihan yang tepat untuk React Edge.
Pernahkah anda terfikir bagaimana rasanya membangunkan aplikasi React tanpa perlu risau tentang halangan pelanggan/pelayan? Tanpa menghafal berpuluh-puluh arahan seperti menggunakan klien atau menggunakan pelayan? Lebih baik lagi: bagaimana jika anda boleh memanggil fungsi pelayan seolah-olah ia tempatan, dengan penaipan penuh dan konfigurasi sifar?
Dan bahagian yang terbaik: semua ini berfungsi dengan lancar pada kedua-dua pelayan dan pelanggan tanpa menandai apa-apa sebagai klien guna atau pelayan guna. Rangka kerja secara automatik mengetahui perkara yang perlu dilakukan berdasarkan konteks. Bolehkah kita menyelam?
Bayangkan boleh melakukan ini:
// On the server class UserAPI extends Rpc { async searchUsers(query: string, filters: UserFilters) { // Validation with Zod const validated = searchSchema.parse({ query, filters }); return this.db.users.search(validated); } } // On the client const UserSearch = () => { const { rpc } = app.useContext<App.Context>(); // TypeScript knows exactly what searchUsers accepts and returns! 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) => { // Configure CORS // Validate request // Handle errors // Serialize response // ...100 lines later... } // 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]); // ... rest of the component }
Lupakan semua yang anda ketahui tentang pengambilan data dalam React. Cangkuk app.useFetch daripada React Edge memperkenalkan pendekatan yang benar-benar baharu dan berkuasa. Bayangkan cangkuk yang:
Jom lihat aksinya:
// On the server class UserAPI extends Rpc { async searchUsers(query: string, filters: UserFilters) { // Validation with Zod const validated = searchSchema.parse({ query, filters }); return this.db.users.search(validated); } } // On the client const UserSearch = () => { const { rpc } = app.useContext<App.Context>(); // TypeScript knows exactly what searchUsers accepts and returns! const { data, loading, error, fetch: retry } = app.useFetch( async (ctx) => ctx.rpc.searchUsers('John', { age: '>18' }) ); };
Contoh di atas menyembunyikan ciri berkuasa: pemultipleksan pintar. Apabila anda menggunakan ctx.rpc.batch, React Edge bukan sahaja mengumpulkan panggilan—ia juga menyahduplikasi panggilan yang sama secara automatik:
// pages/api/search.ts export default async handler = (req, res) => { // Configure CORS // Validate request // Handle errors // Serialize response // ...100 lines later... } // 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]); // ... rest of the component }
// First, define your API on the server class PropertiesAPI extends Rpc { async searchProperties(filters: PropertyFilters) { const results = await this.db.properties.search(filters); // Automatic caching for 5 minutes return this.createResponse(results, { cache: { ttl: 300, tags: ['properties'] } }); } async getPropertyDetails(ids: string[]) { return Promise.all( ids.map(id => this.db.properties.findById(id)) ); } } // Now, on the client, the magic happens const PropertySearch = () => { const [filters, setFilters] = useState<PropertyFilters>({ price: { min: 100000, max: 500000 }, bedrooms: 2 }); // Reactive search with intelligent debounce const { data: searchResults, loading: searchLoading, error: searchError } = app.useFetch( async (ctx) => ctx.rpc.searchProperties(filters), { // Re-fetch when filters change deps: [filters], // Wait 300ms of "silence" before fetching depsDebounce: { filters: 300 } } ); // Fetch property details for the found results const { data: propertyDetails, loading: detailsLoading, fetch: refreshDetails } = app.useFetch( async (ctx) => { if (!searchResults?.length) return null; // This looks like multiple calls, but... return ctx.rpc.batch([ // Everything is multiplexed into a single request! ...searchResults.map(result => ctx.rpc.getPropertyDetails(result.id) ) ]); }, { // Refresh when searchResults change deps: [searchResults] } ); // A beautiful and responsive interface return ( <div> <FiltersPanel value={filters} onChange={setFilters} disabled={searchLoading} /> {searchError && ( <Alert status='error'> Search error: {searchError.message} </Alert> )} <PropertyGrid items={propertyDetails || []} loading={detailsLoading} onRefresh={() => refreshDetails()} /> </div> ); };
Sistem RPC dalam React Edge direka bentuk dengan mengambil kira keselamatan dan enkapsulasi. Tidak semua dalam kelas RPC didedahkan secara automatik kepada pelanggan:
const PropertyListingPage = () => { const { data } = app.useFetch(async (ctx) => { // Even if you make 100 identical calls... return ctx.rpc.batch([ ctx.rpc.getProperty('123'), ctx.rpc.getProperty('123'), // same call ctx.rpc.getProperty('456'), ctx.rpc.getProperty('456'), // same call ]); }); // Behind the scenes: // 1. The batch groups all calls into ONE single HTTP request. // 2. Identical calls are deduplicated automatically. // 3. Results are distributed correctly to each position in the array. // 4. Typing is maintained for each individual result! // Actual RPC calls: // 1. getProperty('123') // 2. getProperty('456') // Results are distributed correctly to all callers! };
Salah satu ciri RPC yang paling berkuasa ialah keupayaan untuk menyusun API ke dalam hierarki:
One of the most impressive parts is how useFetch handles SSR: const ProductPage = ({ productId }: Props) => { const { data, loaded, loading, error } = app.useFetch( async (ctx) => ctx.rpc.getProduct(productId), { // Fine-grained control over when to fetch shouldFetch: ({ worker, loaded }) => { // On the worker (SSR): always fetch if (worker) return true; // On the client: fetch only if no data is loaded return !loaded; } } ); // On the server: // 1. `useFetch` makes the RPC call. // 2. Data is serialized and sent to the client. // 3. Component renders with the data. // On the client: // 1. Component hydrates with server data. // 2. No new call is made (shouldFetch returns false). // 3. If necessary, you can re-fetch with `data.fetch()`. return ( <Suspense fallback={<ProductSkeleton />}> <ProductView product={data} loading={loading} error={error} /> </Suspense> ); };
Mengatur API ke dalam hierarki memberikan beberapa faedah:
Sistem RPC dalam React Edge menjadikan komunikasi pelanggan-pelayan sangat semula jadi sehingga anda hampir terlupa anda membuat panggilan jauh. Dengan keupayaan untuk menyusun API ke dalam hierarki, anda boleh membina struktur yang kompleks sambil memastikan kod anda bersih dan selamat.
React Edge memperkenalkan sistem pengantarabangsaan yang elegan dan fleksibel yang menyokong interpolasi pembolehubah dan pemformatan kompleks tanpa bergantung pada perpustakaan kelas berat.
class PaymentsAPI extends Rpc { // Properties are never exposed private stripe = new Stripe(process.env.STRIPE_KEY); // Methods starting with $ are private private async $validateCard(card: CardInfo) { return await this.stripe.cards.validate(card); } // Methods starting with _ are also private private async _processPayment(amount: number) { return await this.stripe.charges.create({ amount }); } // This method is public and accessible via RPC async createPayment(orderData: OrderData) { // Internal validation using a private method const validCard = await this.$validateCard(orderData.card); if (!validCard) { throw new HttpError(400, 'Invalid card'); } // Processing using another private method const payment = await this._processPayment(orderData.amount); return payment; } } // On the client: const PaymentForm = () => { const { rpc } = app.useContext<App.Context>(); // ✅ This works const handleSubmit = () => rpc.createPayment(data); // ❌ These do not work - private methods are not exposed const invalid1 = () => rpc.$validateCard(data); const invalid2 = () => rpc._processPayment(100); // ❌ This also does not work - properties are not exposed const invalid3 = () => rpc.stripe; };
Penggunaan dalam kod:
// Nested APIs for better organization class UsersAPI extends Rpc { // Subclass to manage preferences preferences = new UserPreferencesAPI(); // Subclass to manage notifications 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 { // Private methods remain private 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); } } } // On the client: const UserProfile = () => { const { rpc } = app.useContext<App.Context>(); const { data: profile } = app.useFetch( async (ctx) => { // Nested calls are fully typed const [user, theme, notificationSettings] = await ctx.rpc.batch([ // Method from the main class ctx.rpc.getProfile('123'), // Method from the preferences subclass ctx.rpc.preferences.getTheme('123'), // Method from the notifications subclass ctx.rpc.notifications.getSettings('123') ]); return { user, theme, notificationSettings }; } ); // ❌ Private methods remain inaccessible const invalid = () => rpc.notifications.$sendPush('123', 'hello'); };
React Edge mengesan dan memuatkan terjemahan anda secara automatik. Ia juga membolehkan menyimpan pilihan pengguna dalam kuki dengan mudah. Tetapi, sudah tentu, anda menjangkakan ini, bukan?
// translations/fr.ts export default { 'Good Morning, {name}!': 'Bonjour, {name}!', };
Pengesahan sentiasa menjadi masalah dalam aplikasi web. Menguruskan token JWT, kuki selamat dan pengesahan semula selalunya memerlukan banyak kod boilerplate. React Edge mengubah ini sepenuhnya.
Begini cara mudahnya untuk melaksanakan sistem pengesahan penuh:
const WelcomeMessage = () => { const userName = 'John'; return ( <div> {/* Output: Good Morning, John! */} <h1>{__('Good Morning, {name}!', { name: userName })}</h1> </div> ); };
// worker.ts const handler = { fetch: async (request: Request, env: types.Worker.Env, context: ExecutionContext) => { const url = new URL(request.url); const lang = (() => { const lang = url.searchParams.get('lang') || worker.cookies.get(request.headers, 'lang') || request.headers.get('accept-language') || ''; if (!lang || !i18n[lang]) { return 'en-us'; } return lang; })(); const workerApp = new AppWorkerEntry({ i18n: { en: await import('./translations/en'), pt: await import('./translations/pt'), es: await import('./translations/es') } }); const res = await workerApp.fetch(); if (url.searchParams.has('lang')) { return new Response(res.body, { headers: worker.cookies.set(res.headers, 'lang', lang) }); } return res; } };
Sifar Plat Dandang
Keselamatan secara Lalai
Taip Penuh
Penyatuan Lancar
// On the server class UserAPI extends Rpc { async searchUsers(query: string, filters: UserFilters) { // Validation with Zod const validated = searchSchema.parse({ query, filters }); return this.db.users.search(validated); } } // On the client const UserSearch = () => { const { rpc } = app.useContext<App.Context>(); // TypeScript knows exactly what searchUsers accepts and returns! const { data, loading, error, fetch: retry } = app.useFetch( async (ctx) => ctx.rpc.searchUsers('John', { age: '>18' }) ); };
Salah satu ciri React Edge yang paling berkuasa ialah keupayaannya untuk berkongsi keadaan dengan selamat antara pekerja dan pelanggan. Begini caranya:
// pages/api/search.ts export default async handler = (req, res) => { // Configure CORS // Validate request // Handle errors // Serialize response // ...100 lines later... } // 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]); // ... rest of the component }
Sistem penghalaan React Edge diilhamkan oleh Hono, tetapi dengan ciri yang dipertingkatkan untuk SSR:
// First, define your API on the server class PropertiesAPI extends Rpc { async searchProperties(filters: PropertyFilters) { const results = await this.db.properties.search(filters); // Automatic caching for 5 minutes return this.createResponse(results, { cache: { ttl: 300, tags: ['properties'] } }); } async getPropertyDetails(ids: string[]) { return Promise.all( ids.map(id => this.db.properties.findById(id)) ); } } // Now, on the client, the magic happens const PropertySearch = () => { const [filters, setFilters] = useState<PropertyFilters>({ price: { min: 100000, max: 500000 }, bedrooms: 2 }); // Reactive search with intelligent debounce const { data: searchResults, loading: searchLoading, error: searchError } = app.useFetch( async (ctx) => ctx.rpc.searchProperties(filters), { // Re-fetch when filters change deps: [filters], // Wait 300ms of "silence" before fetching depsDebounce: { filters: 300 } } ); // Fetch property details for the found results const { data: propertyDetails, loading: detailsLoading, fetch: refreshDetails } = app.useFetch( async (ctx) => { if (!searchResults?.length) return null; // This looks like multiple calls, but... return ctx.rpc.batch([ // Everything is multiplexed into a single request! ...searchResults.map(result => ctx.rpc.getPropertyDetails(result.id) ) ]); }, { // Refresh when searchResults change deps: [searchResults] } ); // A beautiful and responsive interface return ( <div> <FiltersPanel value={filters} onChange={setFilters} disabled={searchLoading} /> {searchError && ( <Alert status='error'> Search error: {searchError.message} </Alert> )} <PropertyGrid items={propertyDetails || []} loading={detailsLoading} onRefresh={() => refreshDetails()} /> </div> ); };
React Edge termasuk sistem caching berkuasa yang berfungsi dengan lancar untuk kedua-dua data JSON dan keseluruhan halaman. Sistem caching ini menyokong penandaan pintar dan ketidaksahihan berasaskan awalan, menjadikannya sesuai untuk pelbagai senario.
// On the server class UserAPI extends Rpc { async searchUsers(query: string, filters: UserFilters) { // Validation with Zod const validated = searchSchema.parse({ query, filters }); return this.db.users.search(validated); } } // On the client const UserSearch = () => { const { rpc } = app.useContext<App.Context>(); // TypeScript knows exactly what searchUsers accepts and returns! const { data, loading, error, fetch: retry } = app.useFetch( async (ctx) => ctx.rpc.searchUsers('John', { age: '>18' }) ); };
Komponen Pautan ialah penyelesaian pintar dan berorientasikan prestasi untuk pramuat sumber pihak pelanggan, memastikan pengalaman navigasi yang lebih lancar dan pantas untuk pengguna. Fungsi prefetchingnya dicetuskan apabila pengguna menuding pada pautan, mengambil kesempatan daripada detik terbiar untuk meminta data destinasi terlebih dahulu.
// pages/api/search.ts export default async handler = (req, res) => { // Configure CORS // Validate request // Handle errors // Serialize response // ...100 lines later... } // 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]); // ... rest of the component }
Apabila pengguna menuding pada pautan “Perihal Kami”, komponen akan mula memuatkan data untuk halaman /tentang, memastikan peralihan yang hampir serta-merta. Idea genius, bukan? Diilhamkan oleh dokumentasi react.dev.
Kait app.useContext ialah asas React Edge, memberikan akses lancar kepada keseluruhan konteks pekerja. Ia menyediakan antara muka yang berkuasa untuk mengurus penghalaan, keadaan, panggilan RPC dan banyak lagi.
// First, define your API on the server class PropertiesAPI extends Rpc { async searchProperties(filters: PropertyFilters) { const results = await this.db.properties.search(filters); // Automatic caching for 5 minutes return this.createResponse(results, { cache: { ttl: 300, tags: ['properties'] } }); } async getPropertyDetails(ids: string[]) { return Promise.all( ids.map(id => this.db.properties.findById(id)) ); } } // Now, on the client, the magic happens const PropertySearch = () => { const [filters, setFilters] = useState<PropertyFilters>({ price: { min: 100000, max: 500000 }, bedrooms: 2 }); // Reactive search with intelligent debounce const { data: searchResults, loading: searchLoading, error: searchError } = app.useFetch( async (ctx) => ctx.rpc.searchProperties(filters), { // Re-fetch when filters change deps: [filters], // Wait 300ms of "silence" before fetching depsDebounce: { filters: 300 } } ); // Fetch property details for the found results const { data: propertyDetails, loading: detailsLoading, fetch: refreshDetails } = app.useFetch( async (ctx) => { if (!searchResults?.length) return null; // This looks like multiple calls, but... return ctx.rpc.batch([ // Everything is multiplexed into a single request! ...searchResults.map(result => ctx.rpc.getPropertyDetails(result.id) ) ]); }, { // Refresh when searchResults change deps: [searchResults] } ); // A beautiful and responsive interface return ( <div> <FiltersPanel value={filters} onChange={setFilters} disabled={searchLoading} /> {searchError && ( <Alert status='error'> Search error: {searchError.message} </Alert> )} <PropertyGrid items={propertyDetails || []} loading={detailsLoading} onRefresh={() => refreshDetails()} /> </div> ); };
Ciri Utama app.useContext
Kait app.useContext merapatkan jurang antara pekerja dan pelanggan. Ia membolehkan anda membina ciri yang bergantung pada keadaan dikongsi, pengambilan data selamat dan pemaparan kontekstual tanpa boilerplate. Ini memudahkan aplikasi yang kompleks, menjadikannya lebih mudah untuk diselenggara dan lebih cepat untuk dibangunkan.
Cangkuk app.useUrlState memastikan keadaan aplikasi anda disegerakkan dengan parameter pertanyaan URL, menawarkan kawalan terperinci ke atas perkara yang disertakan dalam URL, cara keadaan disiri dan masa ia dikemas kini.
// On the server class UserAPI extends Rpc { async searchUsers(query: string, filters: UserFilters) { // Validation with Zod const validated = searchSchema.parse({ query, filters }); return this.db.users.search(validated); } } // On the client const UserSearch = () => { const { rpc } = app.useContext<App.Context>(); // TypeScript knows exactly what searchUsers accepts and returns! const { data, loading, error, fetch: retry } = app.useFetch( async (ctx) => ctx.rpc.searchUsers('John', { age: '>18' }) ); };
Keadaan Awal
Pilihan:
Kait app.useStorageState membolehkan anda mengekalkan keadaan dalam penyemak imbas menggunakan localStorage atau sessionStorage, dengan sokongan TypeScript penuh.
// On the server class UserAPI extends Rpc { async searchUsers(query: string, filters: UserFilters) { // Validation with Zod const validated = searchSchema.parse({ query, filters }); return this.db.users.search(validated); } } // On the client const UserSearch = () => { const { rpc } = app.useContext<App.Context>(); // TypeScript knows exactly what searchUsers accepts and returns! const { data, loading, error, fetch: retry } = app.useFetch( async (ctx) => ctx.rpc.searchUsers('John', { age: '>18' }) ); };
Nyahlantunkan nilai reaktif dengan mudah:
// pages/api/search.ts export default async handler = (req, res) => { // Configure CORS // Validate request // Handle errors // Serialize response // ...100 lines later... } // 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]); // ... rest of the component }
Simpan tatasusunan dengan nilai unik sambil mengekalkan keselamatan jenis.
Kait app.useDistinct mengkhusus dalam mengesan apabila nilai telah benar-benar berubah, dengan sokongan untuk perbandingan yang mendalam dan nyahlantun:
// On the server class UserAPI extends Rpc { async searchUsers(query: string, filters: UserFilters) { // Validation with Zod const validated = searchSchema.parse({ query, filters }); return this.db.users.search(validated); } } // On the client const UserSearch = () => { const { rpc } = app.useContext<App.Context>(); // TypeScript knows exactly what searchUsers accepts and returns! const { data, loading, error, fetch: retry } = app.useFetch( async (ctx) => ctx.rpc.searchUsers('John', { age: '>18' }) ); };
Kait dalam React Edge direka bentuk untuk berfungsi secara harmoni, memberikan pengalaman pembangunan yang lancar dan ditaip kuat. Gabungan mereka membolehkan untuk mencipta antara muka yang kompleks dan reaktif dengan kod yang lebih sedikit.
CLI untuk React Edge direka untuk memudahkan kehidupan pembangun dengan mengumpulkan alatan penting ke dalam satu antara muka yang intuitif. Sama ada anda seorang pemula atau pembangun berpengalaman, CLI memastikan anda boleh mengkonfigurasi, membangun, menguji dan menggunakan projek dengan cekap dan mudah.
Saya berbangga untuk berkongsi bahawa aplikasi pengeluaran pertama menggunakan React Edge sudah pun disiarkan! Ia ialah syarikat hartanah Brazil, Lopes Imóveis, yang sudah pun meraih manfaat daripada prestasi dan fleksibiliti rangka kerja.
Di tapak web mereka, sifat dimuatkan ke dalam cache untuk mengoptimumkan carian dan memberikan pengalaman pengguna yang lebih lancar. Memandangkan ia adalah tapak yang sangat dinamik, cache laluan menggunakan TTL selama 10 saat sahaja, digabungkan dengan strategi yang lapuk sementara pengesahan semula. Ini memastikan tapak menghantar data yang dikemas kini dengan prestasi luar biasa, walaupun semasa pengesahan semula latar belakang.
Selain itu, pengesyoran untuk sifat serupa dikira dengan cekap dan tidak segerak di latar belakang, kemudian disimpan terus ke cache Cloudflare menggunakan sistem caching RPC bersepadu. Pendekatan ini mengurangkan masa tindak balas untuk permintaan seterusnya dan membuat pengesyoran pertanyaan hampir serta-merta. Semua imej disimpan pada Cloudflare R2, menawarkan storan berskala dan teragih tanpa bergantung pada pembekal luaran.
Tidak lama lagi, kami juga akan melancarkan projek pemasaran automatik besar-besaran untuk Easy Auth, mempamerkan potensi teknologi ini dengan lebih jauh lagi.
Jadi, pembaca yang dihormati, kami telah sampai ke penghujung perjalanan ini melalui dunia React Edge! Saya tahu masih terdapat banyak ciri luar biasa untuk diterokai, seperti pilihan pengesahan yang lebih mudah seperti Asas dan Pembawa, dan helah lain yang menjadikan hari pembangun lebih bahagia. Tapi tahan! Ideanya ialah untuk membawa artikel yang lebih terperinci pada masa hadapan untuk menyelami setiap ciri ini.
Isyarat spoiler: tidak lama lagi, React Edge akan menjadi sumber terbuka dan didokumenkan dengan betul! Mengimbangi pembangunan, kerja, penulisan dan sedikit kehidupan sosial bukanlah mudah, tetapi keseronokan melihat keajaiban ini dalam tindakan, terutamanya dengan kelajuan tidak masuk akal yang disediakan oleh infrastruktur Cloudflare, adalah bahan api yang membuatkan saya terus maju. Jadi, nantikan, kerana yang terbaik akan datang! ?
Sementara itu, jika anda ingin mula meneroka dan mengujinya sekarang, pakej itu sudah tersedia di NPM: React Edge pada NPM..
E-mel saya ialah feliperohdee@gmail.com, dan saya sentiasa terbuka untuk menerima maklum balas—ini hanyalah permulaan perjalanan ini. Cadangan dan kritikan membina dialu-alukan. Jika anda menikmati apa yang anda baca, kongsikannya dengan rakan dan rakan sekerja anda, dan nantikan lebih banyak kemas kini. Terima kasih kerana membaca sejauh ini, dan jumpa lagi pada masa akan datang! ???
Atas ialah kandungan terperinci Dari Next.js untuk React Edge dengan Cloudflare Workers: A Story of Liberation. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!