Next.js sunucu eylemlerinin gücünden ve ustalığından yararlanarak gerçek zamanlı bir uygulama oluşturma yolculuğuna çıkacağımız bu örnek olay çalışmasına benimle birlikte katılın. İster deneyimli bir geliştirici olun, ister yalnızca gerçek zamanlı uygulamalar dünyasına adım atın, sizi bekleyen bir bilgi hazinesi var.
Bunu perspektife koymak için her yerde bulunan bazı örnekleri göz önünde bulundurun:
Anlık Mesajlaşma Uygulamaları : WhatsApp ve Telegram gibi mesajların gecikmeden gönderildiği, alındığı ve görüldüğü platformlar.
Ortak Çalışma Araçları : Birden fazla kullanıcının bir dokümanı aynı anda düzenleyebildiği ve birbirlerinin değişikliklerini gerçek zamanlı olarak gözlemleyebildiği Google Dokümanlar'ı düşünün.
Canlı Hisse Senedi Göstergeleri : Piyasadaki dalgalanmalara göre anında güncellenen hisse senedi fiyatlarının görüntülendiği platformlar.
Çevrimiçi Çok Oyunculu Oyunlar : Oyuncuların birbirleriyle ve çevreyle sıfır gecikmeyle etkileşime girerek kusursuz bir oyun deneyimi sağladığı yer.
Peki gerçek zamanlı işlevsellik neden bu kadar aranıyor?
Gerçek zamanlı uygulamalar oluşturmanın engelleri yok değil:
Ölçeklenebilirlik Sorunları : Gerçek zamanlı uygulamaların çoğu zaman çok sayıda eşzamanlı bağlantıyı yönetmesi gerekir ve bu da sağlam bir altyapı gerektirir.
Veri Bütünlüğü : Gerçek zamanlı verilerin çeşitli kullanıcı arayüzlerinde tutarlı kalmasını sağlamak, özellikle birden fazla eşzamanlı düzenleme veya etkileşim söz konusu olduğunda zor olabilir.
Gecikme : Gerçek zamanlı bir uygulama yalnızca en yavaş bileşeni kadar iyidir. Minimum gecikmeyi sağlamak, dikkatli optimizasyonu ve kaynakların verimli kullanımını gerektirir.
React ekosistemindeki eylemler , hâlâ deneysel olmasına rağmen, geliştiricilerin kullanıcı etkileşimlerine yanıt olarak eşzamansız kod yürütmesine olanak tanıyarak bir paradigma değişikliğine yol açtı.
İlginç bir şekilde, Next.js veya React Sunucu Bileşenlerine özel olmasalar da bunların Next.js aracılığıyla kullanılması, React deneysel kanalında olduğunuz anlamına gelir.
HTML formlarına aşina olanlar için, URL'leri action
desteğine aktarmayı hatırlayabilirsiniz. Artık Eylemler ile bir işlevi doğrudan iletebilir, böylece etkileşimleri daha dinamik ve entegre hale getirebilirsiniz.
<button action={() => { /* async function logic here */ }}>Click me!</button>
Form Eylemleri, React Eylemlerinin standart <form>
API ile ustaca bir birleşimini temsil eder. HTML'deki ilkel formaction
özelliği ile rezonansa girerek geliştiricilerin aşamalı yükleme durumlarını ve diğer işlevleri kullanıma hazır geliştirmelerini mümkün kılar.
<!-- Traditional HTML approach --> <form action="/submit-url"> <!-- form elements --> </form> <!-- With Next.js 13.4 Form Actions --> <form action={asyncFunctionForSubmission}> <!-- form elements --> </form>
Sunucu İşlevleri esasen sunucu tarafında çalışan ancak istemciden çağrılabilen işlevlerdir. Bunlar Next.js'nin sunucu tarafı işleme yeteneklerini tamamen yeni bir seviyeye yükseltir.
Sunucu Eylemlerine Geçiş, bunlar Sunucu İşlevleri olarak anlaşılabilir, ancak bunlar özellikle bir eylem olarak tetiklenir. Özellikle action
desteği aracılığıyla form öğeleriyle entegrasyonu, istemci tarafı JavaScript yüklenmeden önce bile formun etkileşimli kalmasını sağlar. Bu, React hidrasyonunun form gönderimi için bir ön koşul olmaması sayesinde daha sorunsuz bir kullanıcı deneyimi anlamına gelir.
// A simple Server Action in Next.js 13.4 <form action={serverActionFunction}> <!-- form elements --> </form>
Son olarak, Sunucu Eylemlerinin bir alt kümesi olan Sunucu Mutasyonları var. Bunlar özellikle sunucudaki verileri değiştirmeniz ve ardından redirect
, revalidatePath
veya revalidateTag
gibi belirli yanıtları yürütmeniz gerektiğinde güçlüdür.
const serverMutationFunction = async () => { // Modify data logic here... // ... return { revalidatePath: '/updated-path' }; } <form action={serverMutationFunction}> <!-- form elements --> </form>
Notlar: Özetle, Next.js 13.4'ün Eylemler, Form Eylemleri, Sunucu İşlevleri ve Sunucu Mutasyonları tarafından desteklenen Sunucu Eylemleri çerçevesi, gerçek zamanlı web uygulamalarına yönelik dönüştürücü bir yaklaşımı bünyesinde barındırır.
Örnek olay incelememizde ilerledikçe, bu özelliklerin ortaya koyduğu beceriye ilk elden tanık olacaksınız. Öyleyse önümüzdeki heyecan verici yolculuğa hazırlanalım!
Öncelikle Next.js projenizde Sunucu Eylemlerini etkinleştirmeniz gerekir. Aşağıdaki kodu next.config.js
dosyanıza eklemeniz yeterlidir:
module.exports = { experimental: { serverActions: true, }, }
Sunucu Bileşenleri İçinde : Bir Sunucu Eylemi, bir Sunucu Bileşeni içinde şu şekilde kolayca tanımlanabilir:
export default function ServerComponent() { async function myAction() { 'use server' // ... } }
İstemci Bileşenleri ile : Bir İstemci Bileşeni içinde bir Sunucu Eylemi kullanırken, eylemi ayrı bir dosyada oluşturun ve ardından içe aktarın.
// app/actions.js 'use server' export async function myAction() { // ... }
İstemci Bileşenini içe aktarma ve kullanma:
// app/client-component.js import { myAction } from './actions' export default function ClientComponent() { return ( <form action={myAction}> <button type="submit">Add to Cart</button> </form> ) }
Özel Çağrı : Formların, düğmelerin veya girişlerin dışındaki Sunucu Eylemlerini çağırmak için startTransition
gibi özel yöntemleri kullanabilirsiniz.
// Example using startTransition 'use client' import { useTransition } from 'react' import { addItem } from '../actions' function ExampleClientComponent({ id }) { let [isPending, startTransition] = useTransition() return ( <button onClick={() => startTransition(() => addItem(id))}> Add To Cart </button> ) }
Next.js 13.4 ayrıca, <form>
öğesinin JavaScript olmadan çalışmasına olanak tanıyan Aşamalı Geliştirme de sunar. Sunucu Eylemleri doğrudan bir <form>
öğesine aktarılabilir, böylece JavaScript devre dışı bırakılsa bile form etkileşimli hale gelir.
// app/components/example-client-component.js 'use client' import { handleSubmit } from './actions.js' export default function ExampleClientComponent({ myAction }) { return ( <form action={handleSubmit}> {/* ... */} </form> ) }
Bir Sunucu Eylemine gönderilen maksimum istek gövdesi varsayılan olarak 1 MB'tır. Gerekirse, bu sınırı serverActionsBodySizeLimit
seçeneğini kullanarak yapılandırabilirsiniz:
module.exports = { experimental: { serverActions: true, serverActionsBodySizeLimit: '2mb', }, }
npx create-next-app@latest my-real-time-app
my-real-time-app
projeniz için istediğiniz adla değiştirin. Bu komut, varsayılan yapılandırmalarla yeni bir Next.js projesi oluşturur.
Next.js 13.4'ün kullanıma sunulmasıyla birlikte Uygulama Yönlendiricisi , geliştiricilerin paylaşılan düzenlerden, iç içe yönlendirmeden, hata işlemeden ve daha fazlasından yararlanmasına olanak tanıyan önemli bir özellik haline geldi. Mevcut pages
dizini ile birlikte çalışacak şekilde tasarlanmıştır, ancak app
adlı yeni bir dizinde yer almaktadır.
Projenizin kökünde bir app
dizini oluşturun.
Varsayılan olarak, app
dizini içindeki bileşenler Sunucu Bileşenleri'dir ; en iyi performansı sunar ve geliştiricilerin bunları kolayca benimsemesine olanak tanır.
my-real-time-app/ │ ├── app/ # Main directory for App Router components │ ├── _error.js # Custom error page │ ├── _layout.js # Shared layout for the app │ │ │ ├── dashboard/ # Nested route example │ │ ├── index.js # Dashboard main view │ │ └── settings.js # Dashboard settings view │ │ │ ├── index.js # Landing/Home page │ ├── profile.js # User profile page │ ├── login.js # Login page │ └── register.js # Registration page │ ├── public/ # Static assets go here (images, fonts, etc.) │ ├── images/ │ └── favicon.ico │ ├── styles/ # Global styles or variables │ └── global.css │ ├── package.json # Dependencies and scripts ├── next.config.js # Next.js configuration └── README.md # Project documentation
Sunucu Bileşenleri : Uygulamanızın etkileşimli olmayan kısımları için idealdir. Bu bileşenler sunucuda işlenir ve istemciye HTML olarak gönderilir. Buradaki avantaj, gelişmiş performans, azaltılmış istemci tarafı JavaScript ve veri getirme veya arka uç kaynaklarına doğrudan erişme yeteneğidir.
İstemci Bileşenleri : Etkileşimli kullanıcı arayüzü öğeleri için kullanılır. Bunlar sunucuda önceden oluşturulur ve daha sonra etkileşimi artırmak için istemcide "hidratlanır".
Next.js, bu bileşenleri birbirinden ayırmak için "use client"
yönergesini kullanıma sundu. Bu yönerge, bir bileşenin İstemci Bileşeni olarak ele alınması gerektiğini belirtir. Herhangi bir içe aktarma işleminden önce bileşen dosyasının en üstüne yerleştirilmelidir.
Örneğin, sağlanan kodda olduğu gibi etkileşimli bir sayacınız varsa, bunun istemci tarafı bir bileşen olduğunu belirtmek için "use client"
yönergesini kullanırsınız.
Sunucu Bileşenlerini varsayılan olarak kullanın ( app
dizininde oldukları gibi).
İstemci Bileşenlerini yalnızca etkileşim ekleme, yalnızca tarayıcı API'lerini kullanma veya duruma veya tarayıcı işlevlerine bağlı React kancalarından yararlanma gibi belirli kullanım durumlarınız olduğunda tercih edin.
Notlar: Bu yapıyı ve kurulumu takip ederek Next.js 13.4'ün Sunucu Eylemleri ile performanslı, gerçek zamanlı bir uygulama oluşturma yolunda ilerleyeceksiniz.
Next.js 13.4'ün gücü, gerçek zamanlı arka uç işlevlerini projemize entegre ederken parlıyor. my-real-time-app
uygulamamız için ilgili kod örnekleriyle adımları inceleyelim.
my-real-time-app
uygulamamız için sunucu eylemleri, ön uç ile arka uç arasında birincil köprü görevi görerek, ayrı API'lere ihtiyaç duymadan verimli veri işlemlerine olanak tanır.
// my-real-time-app/app/actions/index.js export * from './auth-action'; export * from './chat-action';
my-real-time-app
, kimlik doğrulama sürecini kolaylaştırmak için sunucu eylemlerinden yararlanırız.
// my-real-time-app/app/actions/auth-action.js export const login = async (credentials) => { // Logic for authenticating user with credentials // Return user details or error message }; export const logout = async (userId) => { // Logic for logging out the user // Return success or error message }; export const register = async (userInfo) => { // Logic for registering a new user // Store user in database and return success or error message };
// my-real-time-app/app/actions/chat-action.js export const sendMessage = async (messageDetails) => { // Logic to send a new message // Store message in database and inform other users via WebSocket or similar }; export const receiveMessage = async () => { // Logic to receive a message in real-time // Return the message details }; export const getRecentMessages = async (userId) => { // Logic to fetch recent messages for the user // Retrieve messages from the database };
// Initialize MongoDB connection const { MongoClient } = require('mongodb'); const client = new MongoClient(process.env.MONGODB_URI); await client.connect(); // Now, use this connection in server actions to interact with the database.
// my-real-time-app/app/actions/chat-action.js export const sendMessage = async (messageDetails) => { const messagesCollection = client.db('chatDB').collection('messages'); await messagesCollection.insertOne(messageDetails); // Inform other users via WebSocket or similar };
// Middleware for validating request data const validateRequest = (req) => { // Validation logic here return isValid; }; export const sendMessage = async (messageDetails) => { if (!validateRequest(messageDetails)) { throw new Error("Invalid request data"); } // Remaining logic... };
Bu bölümde, my-real-time-app
için sezgisel ve duyarlı bir sohbet arayüzü oluşturacağız. Next.js 13.4'ün sunucu bileşenlerinin entegrasyonu, sorunsuz bir kullanıcı deneyimi için gerçek zamanlı güncellemeleri mümkün kılacaktır.
// my-real-time-app/app/chat-interface.js import { useEffect, useState } from 'react'; import { getRecentMessages } from './actions/chat-action'; export default function ChatInterface() { const [messages, setMessages] = useState([]); useEffect(() => { async function loadMessages() { const recentMessages = await getRecentMessages(); setMessages(recentMessages); } loadMessages(); }, []); return ( <div className="chatBox"> {messages.map(msg => ( <p key={msg.id}>{msg.content}</p> ))} </div> ); }
// my-real-time-app/app/chat-interface.js const [socket, setSocket] = useState(null); useEffect(() => { const ws = new WebSocket("ws://your-backend-url/ws"); ws.onmessage = (event) => { const newMessage = JSON.parse(event.data); setMessages(prevMessages => [...prevMessages, newMessage]); }; setSocket(ws); return () => { ws.close(); }; }, []);
// my-real-time-app/app/chat-interface.js useEffect(() => { if (messages.length && "Notification" in window && Notification.permission === "granted") { const lastMessage = messages[messages.length - 1]; new Notification(`New message from ${lastMessage.sender}: ${lastMessage.content}`); } }, [messages]);
const HeavyComponent = React.lazy(() => import('./HeavyComponent')); function Chat() { return ( <React.Suspense fallback={<div>Loading...</div>}> <HeavyComponent /> </React.Suspense> ); }
React Server Components
kullanın:
Gerçek zamanlı uygulamamızın temel bileşenleri mevcut olduğundan, bunların beklendiği gibi çalışmasını, performanslı, ölçeklenebilir ve sağlam olmasını sağlamak çok önemlidir. Bu bölüm my-real-time-app
gibi gerçek zamanlı sistemler için uyarlanmış çeşitli test yaklaşımlarına ışık tutmaktadır.
// cypress/integration/chat.spec.js describe('Chat functionality', () => { it('should send and receive messages in real-time', () => { cy.visit('/chat'); cy.get('[data-cy=messageInput]').type('Hello, World!'); cy.get('[data-cy=sendButton]').click(); cy.contains('Hello, World!').should('exist'); }); });
# artillery-config.yml config: target: '//my-real-time-app.com' phases: - duration: 300 arrivalRate: 20 scenarios: - flow: - emit: channel: 'chat' payload: message: 'Hello, World!'
$ artillery run artillery-config.yml
Node.js, profil oluşturma için yerleşik araçlar sağlar ve --inspect
bayrağı, Node.js denetçisini etkinleştirmek için Next.js geliştirme sunucusuyla birlikte kullanılabilir. Chrome'un DevTools'unu kullanarak performans darboğazları hakkında bilgi edinebilirsiniz.
İstemci tarafında, Chrome DevTools'taki Performance
sekmesi gibi araçlar, oluşturma darboğazlarının belirlenmesine yardımcı olabilir. Özellikle gerçek zamanlı güncellemelerle gereksiz işlemelerin gerçekleşmediğinden emin olun.
SWR ile örnek:
// my-real-time-app/app/chat-interface.js import useSWR from 'swr'; function ChatInterface() { const { data: messages } = useSWR('/api/messages', fetcher); // ... rest of the component }
Notlar: Gerçek zamanlı uygulamaların test edilmesi, standart yazılım test tekniklerinin bir kombinasyonunu ve bazılarının gerçek zamanlı sistemlerin zorlukları ve özelliklerine göre özel olarak uyarlanmasını gerektirir. my-real-time-app
için sıkı bir test rejimi sağlayarak, kullanıcı trafiğinin veya veri akışının ölçeğinden bağımsız olarak sorunsuz ve duyarlı bir kullanıcı deneyimini garanti edebiliriz.
Gerçek zamanlı uygulamamızın temel mimarisi sağlam bir şekilde yerine oturduğundan, artık dikkatimiz uygulamanın özelliklerini ve performansını iyileştirmeye yöneliyor. Kullanıcı deneyimini geliştirmek ve my-real-time-app
optimize etmek için bazı stratejiler şunlardır:
// my-real-time-app/app/components/Message.js function Message({ content, status }) { return ( <div> <p>{content}</p> {status === 'read' && <span>✓ Read</span>} </div> ); }
// my-real-time-app/app/components/UserStatus.js function UserStatus({ isOnline }) { return ( <div> {isOnline ? <span className="online-indicator"></span> : <span className="offline-indicator"></span>} </div> ); }
// Example: Setting up compression with a WebSocket server const WebSocket = require('ws'); const wss = new WebSocket.Server({ perMessageDeflate: { zlibDeflateOptions: { // Add compression options here } } });
// Example: Simple retry logic with fetch let retries = 3; function fetchData(url) { fetch(url) .then(response => response.json()) .catch(error => { if (retries > 0) { retries--; fetchData(url); } else { console.error('Failed to fetch data after 3 retries'); } }); }
Notlar: my-real-time-app
devam eden başarısı, yalnızca temel işlevlerine değil, aynı zamanda sorunsuz bir kullanıcı deneyimi sağlayan ince geliştirmelere ve sürekli optimizasyonlara da bağlıdır. Yukarıda listelenen stratejileri birleştirerek kullanıcılarımıza güvenilir ve keyifli, üstün bir sohbet deneyimi sunmaya hazırız.
my-real-time-app
ile yolculuğumuz bizi Next.js 13.4 ile ilk kurulumdan, sunucu eylemleriyle arka uç oluşturmaya, kusursuz bir ön uç deneyimi tasarlamaya ve gerçek zamanlı yeteneklerin test edilip optimize edilmesini sağlamaya kadar götürdü. Sunucu tarafı işleme ile istemci tarafı etkileşimi arasında etkili bir denge sağlayarak sunucu ve istemci bileşenlerinin nüanslarını derinlemesine araştırdık.
my-real-time-app
uzun bir yol kat etmiş olsa da, gelecekteki geliştirmelerin potansiyeli hala çok büyük:
Öncelikle Next.js dünyasının bu karmaşık labirentinde benimle yolculuk yaptığınız için çok teşekkür ederim . Eğer bu noktaya kadar geldiyseniz, tebrikler! Eğer bazı kısımlara göz gezdirdiyseniz sizi suçlamıyorum; onları yazmayı atlamak istediğim zamanlar oldu!
Hiç bir sorunda hata ayıklamak için saatler harcayıp noktalı virgülü kaçırdığınızı fark ettiğiniz anlar oldu mu? Veya kodunuzun önemli bir bölümünü yanlışlıkla sildiğinizde ve hayatta Ctrl + Z'nin olmasını istediğinizde? Ah, programlamanın zevkleri!
Yani bir dahaki sefere kodunuz işbirliği yapmayı reddettiğinde, derin bir nefes alın, biraz kahve alın (ya da çay, yargılamıyorum, ben de bir matecocido hayranıyım) ve bu konuda yalnız olmadığınızı unutmayın.