Project Templates
Social Network
Come costruire un social network con React
123 min
introduzione in questo tutorial, costruirai back4gram, una piattaforma di social network completa simile a instagram back4gram consente agli utenti di creare account, condividere post con immagini, interagire tramite like e commenti, cercare contenuti e comunicare tramite messaggi in tempo reale questo progetto dimostra come combinare le potenti capacità frontend di react con i robusti servizi backend di back4app per creare un'applicazione sociale moderna e ricca di funzionalità react è la scelta perfetta per il frontend di un social network grazie alla sua architettura basata su componenti, che consente elementi ui riutilizzabili e un rendering efficiente nel frattempo, back4app fornisce un backend gestito da parse server che gestisce l'autenticazione degli utenti, l'archiviazione dei dati, il caricamento di file e le funzionalità in tempo reale senza richiederti di costruire un'infrastruttura server complessa da zero completando questo tutorial, costruirai un social network completo con autenticazione utente (registrazione, accesso, reimpostazione password) gestione del profilo creazione di post con caricamenti di immagini interazioni sociali (mi piace, commenti) messaggistica in tempo reale con indicatori di digitazione funzionalità di ricerca dei contenuti impostazioni e preferenze utente lungo il cammino, acquisirai competenze preziose in sviluppo react con hooks e context sviluppo ui con chakra ui integrazione di parse server tramite back4app gestione dei dati in tempo reale con livequery flussi di autenticazione utente gestione del caricamento dei file implementazione del design reattivo che tu stia cercando di lanciare la tua piattaforma sociale o semplicemente voglia capire come sono costruite le moderne reti sociali, questo tutorial ti fornirà le conoscenze e l'esperienza pratica per raggiungere i tuoi obiettivi in qualsiasi momento puoi accedere al codice completo su github requisiti per completare questo tutorial, avrai bisogno di un account back4app registrati per un account gratuito su back4app com https //www back4app com/ utilizzerai questo per creare e gestire i tuoi servizi backend node js e npm installati sulla tua macchina locale installa node js (versione 14 x o successiva) e npm da nodejs org https //nodejs org/ verifica la tua installazione eseguendo node v e npm v nel tuo terminale comprensione di base di react familiarità con i componenti react, gli hook e jsx se hai bisogno di rinfrescare le tue conoscenze su react, dai un'occhiata alla documentazione ufficiale di react https //reactjs org/docs/getting started html editor di codice qualsiasi moderno editor di codice come visual studio code, sublime text o atom git (opzionale) per il controllo delle versioni e per seguire il repository risorse supplementari documentazione di back4app https //www back4app com/docs/get started/welcome guida javascript di parse https //docs parseplatform org/js/guide/ documentazione di chakra ui https //chakra ui com/docs/getting started documentazione di react router https //reactrouter com/en/main passo 1 — configurazione del tuo backend back4app in questo passaggio, creerai un nuovo progetto back4app e configurerai lo schema del database necessario per la tua applicazione di social network back4app fornisce un parse server gestito che si occuperà dell'autenticazione degli utenti, dell'archiviazione dei dati e delle funzionalità in tempo reale creazione di un nuovo progetto back4app accedi al tuo account back4app e vai alla dashboard clicca sul pulsante "crea una nuova app" inserisci "back4gram" come nome della tua app, seleziona la regione del server più vicina e clicca su "crea" una volta creata la tua app, verrai reindirizzato alla dashboard dell'app comprendere lo schema del database prima di creare classi nel tuo database, comprendiamo il modello di dati necessario per il nostro social network in base ai requisiti della nostra applicazione, avremo bisogno delle seguenti classi utente (esiste già per impostazione predefinita in parse) questa classe gestisce l'autenticazione dell'utente e le informazioni del profilo lo estenderemo con campi aggiuntivi come biografia e avatar posta memorizza i post degli utenti, inclusi contenuti testuali e immagini campi contenuto (stringa), autore (puntatore a utente), immagine (file), mi piace (numero), commenti (array), creato il (data) commento memorizza i commenti sui post campi contenuto (stringa), autore (puntatore a utente), post (puntatore a post), creato il (data) conversazione rappresenta una conversazione in chat tra utenti campi partecipanti (array di puntatori a utente), ultimomessaggio (stringa), aggiornatoil (data) messaggio messaggi individuali all'interno di una conversazione campi testo (stringa), mittente (puntatore a utente), conversazione (puntatore a conversazione), creato il (data) stato di digitazione (per indicatori di digitazione in tempo reale) traccia quando gli utenti digitano in una conversazione campi utente (puntatore all'utente), conversazione (puntatore alla conversazione), stascrivendo (booleano) creazione delle classi del database ora, creiamo queste classi nel tuo database back4app naviga alla sezione "database" nel tuo dashboard di back4app estendendo la classe utente clicca sulla classe "utente" che esiste già aggiungi le seguenti colonne bio (tipo stringa) avatar (tipo file) seguaci (tipo numero, predefinito 0) seguente (tipo numero, predefinito 0) creazione della classe post clicca su "crea una classe" inserisci "post" come nome della classe e seleziona "crea una classe vuota" aggiungi le seguenti colonne contenuto (tipo stringa) autore (tipo puntatore a utente) immagine (tipo file) mi piace (tipo numero, predefinito 0) commenti (tipo array) creato il (tipo data, aggiunto automaticamente) creazione della classe commento clicca su "crea una classe" inserisci "commento" come nome della classe e seleziona "crea una classe vuota" aggiungi le seguenti colonne contenuto (tipo stringa) autore (tipo puntatore a utente) post (tipo puntatore a post) creato il (tipo data, aggiunto automaticamente) creazione della classe conversation clicca su "crea una classe" inserisci "conversazione" come nome della classe e seleziona "crea una classe vuota" aggiungi le seguenti colonne partecipanti (tipo array) ultimomessaggio (tipo stringa) aggiornato il (tipo data, aggiunto automaticamente) creazione della classe message clicca su "crea una classe" inserisci "messaggio" come nome della classe e seleziona "crea una classe vuota" aggiungi le seguenti colonne testo (tipo stringa) mittente (tipo puntatore a utente) conversazione (tipo puntatore alla conversazione) creato il (tipo data, aggiunto automaticamente) creazione della classe typingstatus clicca su "crea una classe" inserisci "typingstatus" come nome della classe e seleziona "crea una classe vuota" aggiungi le seguenti colonne utente (tipo puntatore a utente) conversazione (tipo puntatore alla conversazione) stascrivendo (tipo booleano) impostazione delle autorizzazioni della classe (opzionale) per garantire la sicurezza dei dati, dobbiamo configurare elenchi di controllo accessi (acl) appropriati per ciascuna classe naviga alla sezione "sicurezza e chiavi" nel tuo dashboard di back4app sotto "sicurezza a livello di classe", configura i seguenti permessi classe utente accesso pubblico in lettura abilitato (in modo che gli utenti possano vedere i profili di altri utenti) accesso pubblico in scrittura disabilitato (gli utenti possono modificare solo i propri profili) post classe accesso pubblico in lettura abilitato (tutti possono vedere i post) accesso in scrittura pubblico abilitato (gli utenti autenticati possono creare post) aggiungi un clp per aggiornare/eliminare per limitare solo all'autore commento classe accesso pubblico in lettura abilitato (tutti possono vedere i commenti) accesso in scrittura pubblico abilitato (gli utenti autenticati possono creare commenti) aggiungi un clp per aggiornare/cancellare per limitare solo all'autore corso di conversazione accesso pubblico in lettura disabilitato (le conversazioni sono private) accesso in scrittura pubblico abilitato (gli utenti autenticati possono creare conversazioni) aggiungi un clp per limitare l'accesso in lettura/scrittura ai partecipanti alla conversazione classe di messaggio accesso pubblico in lettura disabilitato (i messaggi sono privati) accesso in scrittura pubblico abilitato (gli utenti autenticati possono inviare messaggi) aggiungi un clp per limitare l'accesso in lettura/scrittura ai partecipanti alla conversazione classe typingstatus accesso pubblico in lettura disabilitato (lo stato di digitazione è privato) accesso in scrittura pubblico abilitato (gli utenti autenticati possono aggiornare lo stato di digitazione) aggiungi un clp per limitare l'accesso in lettura/scrittura ai partecipanti alla conversazione impostare livequery per funzionalità in tempo reale per abilitare funzionalità in tempo reale come messaggistica e indicatori di digitazione, dobbiamo configurare livequery naviga nella sezione "impostazioni del server" nel tuo dashboard di back4app sotto "parse server", trova la sezione "livequery" e abilitala aggiungi le seguenti classi da monitorare tramite livequery messaggio stato di digitazione post (per aggiornamenti in tempo reale su mi piace e commenti) salva le tue modifiche ottenere le chiavi della tua applicazione avrai bisogno delle chiavi della tua applicazione back4app per collegare il tuo frontend react al backend naviga nella sezione "impostazioni app" > "sicurezza e chiavi" annota le seguenti chiavi id applicazione chiave javascript url del server url del server livequery (configurazione del sottodominio, per funzionalità in tempo reale) utilizzerai queste chiavi nella tua applicazione react per inizializzare parse passo 2 — creare un progetto frontend react in questo passo, configurerai un nuovo progetto react e lo configurerai per lavorare con il tuo backend back4app installerai le dipendenze necessarie, creerai la struttura del progetto e ti connetterai al tuo server parse impostare un nuovo progetto react iniziamo creando una nuova applicazione react utilizzando create react app, che fornisce una configurazione di build moderna senza necessità di configurazione apri il terminale e naviga nella directory in cui desideri creare il tuo progetto esegui il seguente comando per creare una nuova applicazione react npx create react app back4gram una volta creato il progetto, naviga nella directory del progetto cd back4gram avvia il server di sviluppo per assicurarti che tutto funzioni npm start questo aprirà la tua nuova applicazione react nel browser all'indirizzo http //localhost 3000 http //localhost 3000 installazione delle dipendenze necessarie ora, installiamo i pacchetti di cui avremo bisogno per la nostra applicazione di social network ferma il server di sviluppo (premi ctrl+c nel tuo terminale) installa il parse sdk per connetterti a back4app npm install parse installa react router per la navigazione npm install react router dom installa chakra ui per i nostri componenti dell'interfaccia utente npm install @chakra ui/react @emotion/react @emotion/styled framer motion installa utility ui aggiuntive e librerie di icone npm install react icons spiegazione della struttura del progetto organizziamo il nostro progetto con una struttura chiara crea le seguenti directory nella src cartella mkdir p src/components/ui src/pages src/contexts src/utils ecco a cosa serve ciascuna directory components componenti ui riutilizzabili ui componenti ui di base come pulsanti, moduli, modali altre cartelle di componenti per funzionalità specifiche (ad es , post, commenti) pages componenti di pagina completa che corrispondono a percorsi contexts provider di contesto react per la gestione dello stato utils funzioni di utilità e helper creare variabili di ambiente per memorizzare in modo sicuro le tue credenziali back4app, crea un env file nella radice del tuo progetto crea un nuovo file chiamato env local nella radice del progetto touch env local apri il file e aggiungi le tue credenziali back4app react app parse app id=your application id react app parse js key=your javascript key react app parse server url=https //parseapi back4app com react app parse live query url=wss\ //your app id back4app io sostituisci i valori segnaposto con le tue reali credenziali back4app dal passo 1 assicurati di aggiungere env local al tuo gitignore file per evitare di commettere informazioni sensibili configurare il parse sdk con le credenziali di back4app ora, configuriamo il parse sdk per connettersi al tuo backend di back4app crea un nuovo file src/utils/parseconfig js // src/utils/parseconfig js import parse from 'parse/dist/parse min js'; // inizializza parse parse initialize( process env react app parse app id, process env react app parse js key ); parse serverurl = process env react app parse server url; // inizializza le live queries if (process env react app parse live query url) { parse livequeryserverurl = process env react app parse live query url; } export default parse; aggiorna il tuo src/index js file per importare la configurazione di parse import react from 'react'; import reactdom from 'react dom/client'; import ' /index css'; import app from ' /app'; import reportwebvitals from ' /reportwebvitals'; import ' /utils/parseconfig'; // importa la configurazione di parse const root = reactdom createroot(document getelementbyid('root')); root render( \<react strictmode> \<app /> \</react strictmode> ); reportwebvitals(); impostare il componente app con il routing aggiorniamo il componente principale app per includere il routing e il provider chakra ui aggiorna src/app js import react from 'react'; import { browserrouter as router, routes, route } from 'react router dom'; import { chakraprovider, extendtheme } from '@chakra ui/react'; // importa le pagine (le creeremo dopo) import landingpage from ' /pages/landingpage'; import loginpage from ' /pages/loginpage'; import signuppage from ' /pages/signuppage'; import resetpasswordpage from ' /pages/resetpasswordpage'; import feedpage from ' /pages/feedpage'; import profilepage from ' /pages/profilepage'; import postdetailspage from ' /pages/postdetailspage'; import messagespage from ' /pages/messagespage'; import searchpage from ' /pages/searchpage'; import settingspage from ' /pages/settingspage'; // crea un tema personalizzato const theme = extendtheme({ config { initialcolormode 'dark', usesystemcolormode false, }, colors { brand { 50 '#e5f4ff', 100 '#b8dcff', 200 '#8ac5ff', 300 '#5cadff', 400 '#2e96ff', 500 '#147dff', 600 '#0061cc', 700 '#004799', 800 '#002d66', 900 '#001433', }, }, }); function app() { return ( \<chakraprovider theme={theme}> \<router> \<routes> \<route path="/" element={\<landingpage />} /> \<route path="/login" element={\<loginpage />} /> \<route path="/signup" element={\<signuppage />} /> \<route path="/reset password" element={\<resetpasswordpage />} /> \<route path="/feed" element={\<feedpage />} /> \<route path="/profile" element={\<profilepage />} /> \<route path="/post/\ id" element={\<postdetailspage />} /> \<route path="/messages" element={\<messagespage />} /> \<route path="/search" element={\<searchpage />} /> \<route path="/settings" element={\<settingspage />} /> \</routes> \</router> \</chakraprovider> ); } export default app; creare un componente route protetta per proteggere le route che richiedono autenticazione, creiamo un componente protectedroute per prima cosa, crea un authcontext per gestire lo stato di autenticazione dell'utente // src/contexts/authcontext js import react, { createcontext, usestate, usecontext, useeffect } from 'react'; import parse from 'parse/dist/parse min js'; const authcontext = createcontext(); export function useauth() { return usecontext(authcontext); } export function authprovider({ children }) { const \[currentuser, setcurrentuser] = usestate(null); const \[isloading, setisloading] = usestate(true); useeffect(() => { // check if user is already logged in const checkuser = async () => { try { const user = await parse user current(); setcurrentuser(user); } catch (error) { console error('error checking current user ', error); } finally { setisloading(false); } }; checkuser(); }, \[]); // login function const login = async (username, password) => { try { const user = await parse user login(username, password); setcurrentuser(user); return user; } catch (error) { throw error; } }; // signup function const signup = async (username, email, password) => { try { const user = new parse user(); user set('username', username); user set('email', email); user set('password', password); const result = await user signup(); setcurrentuser(result); return result; } catch (error) { throw error; } }; // logout function const logout = async () => { try { await parse user logout(); setcurrentuser(null); } catch (error) { throw error; } }; // reset password function const resetpassword = async (email) => { try { await parse user requestpasswordreset(email); } catch (error) { throw error; } }; const value = { currentuser, isloading, login, signup, logout, resetpassword, }; return \<authcontext provider value={value}>{children}\</authcontext provider>; } ora, crea il componente protectedroute // src/components/protectedroute js import react from 'react'; import { navigate } from 'react router dom'; import { useauth } from ' /contexts/authcontext'; import { flex, spinner } from '@chakra ui/react'; function protectedroute({ children }) { const { currentuser, isloading } = useauth(); if (isloading) { return ( \<flex justify="center" align="center" height="100vh"> \<spinner size="xl" /> \</flex> ); } if (!currentuser) { return \<navigate to="/login" />; } return children; } export default protectedroute; aggiorna il componente app per utilizzare authprovider e protectedroute // src/app js (updated) import react from 'react'; import { browserrouter as router, routes, route } from 'react router dom'; import { chakraprovider, extendtheme } from '@chakra ui/react'; import { authprovider } from ' /contexts/authcontext'; import protectedroute from ' /components/protectedroute'; // import pages import landingpage from ' /pages/landingpage'; import loginpage from ' /pages/loginpage'; import signuppage from ' /pages/signuppage'; import resetpasswordpage from ' /pages/resetpasswordpage'; import feedpage from ' /pages/feedpage'; import profilepage from ' /pages/profilepage'; import postdetailspage from ' /pages/postdetailspage'; import messagespage from ' /pages/messagespage'; import searchpage from ' /pages/searchpage'; import settingspage from ' /pages/settingspage'; // theme configuration (same as before) const theme = extendtheme({ // theme configuration }); function app() { return ( \<chakraprovider theme={theme}> \<authprovider> \<router> \<routes> \<route path="/" element={\<landingpage />} /> \<route path="/login" element={\<loginpage />} /> \<route path="/signup" element={\<signuppage />} /> \<route path="/reset password" element={\<resetpasswordpage />} /> \<route path="/feed" element={ \<protectedroute> \<feedpage /> \</protectedroute> } /> \<route path="/profile" element={ \<protectedroute> \<profilepage /> \</protectedroute> } /> \<route path="/post/\ id" element={ \<protectedroute> \<postdetailspage /> \</protectedroute> } /> \<route path="/messages" element={ \<protectedroute> \<messagespage /> \</protectedroute> } /> \<route path="/search" element={ \<protectedroute> \<searchpage /> \</protectedroute> } /> \<route path="/settings" element={ \<protectedroute> \<settingspage /> \</protectedroute> } /> \</routes> \</router> \</authprovider> \</chakraprovider> ); } export default app; creare una pagina di atterraggio di base creiamo una semplice pagina di atterraggio per testare la nostra configurazione // src/pages/landingpage js import react from 'react'; import { box, heading, text, button, vstack, flex } from '@chakra ui/react'; import { link as routerlink } from 'react router dom'; function landingpage() { return ( \<box bg="gray 900" minh="100vh" color="white"> \<flex direction="column" align="center" justify="center" textalign="center" py={20} \> \<heading size="2xl" mb={4}> back4gram \</heading> \<text fontsize="lg" maxw="600px" mb={8}> join a vibrant community where your voice matters share stories, ideas, and moments with friends and the world \</text> \<vstack spacing={4} maxw="md" mx="auto"> \<button as={routerlink} to="/signup" colorscheme="brand" size="lg" w="full" \> create account \</button> \<button as={routerlink} to="/login" variant="outline" size="lg" w="full" \> log in \</button> \</vstack> \</flex> \</box> ); } export default landingpage; testare la tua configurazione ora che hai impostato la struttura di base della tua applicazione react e l'hai collegata a back4app, testiamola avvia il server di sviluppo npm start apri il tuo browser e vai a http //localhost 3000 http //localhost 3000 dovresti vedere la pagina di atterraggio con i pulsanti per registrarti o accedere controlla la console del tuo browser per assicurarti che non ci siano errori relativi all'inizializzazione di parse passo 3 — implementazione delle funzionalità di autenticazione in questo passo, implementeremo le funzionalità di autenticazione degli utenti per la nostra applicazione di social network utilizzando il parse server di back4app esamineremo come funziona il sistema di autenticazione di parse e implementeremo le funzionalità di accesso, registrazione e reset della password comprendere il sistema di autenticazione degli utenti di parse il parse server di back4app fornisce un sistema di gestione degli utenti completo attraverso la parse user classe comprendiamo come funziona l'autenticazione di parse nella nostra applicazione la classe parse user il parse user è una sottoclasse speciale di parse object progettata specificamente per la gestione degli utenti nella nostra applicazione back4gram, lo usiamo per memorizzare le credenziali degli utenti (nome utente, email, password) gestire lo stato di autenticazione gestire automaticamente i token di sessione guardando la nostra implementazione, possiamo vedere come interagiamo con la classe parse user // from signuppage js const user = new parse user(); user set('username', username); user set('email', email); user set('password', password); await user signup(); questo codice crea un nuovo oggetto parse user, imposta i campi richiesti e chiama il metodo signup() per registrare l'utente in back4app flusso di autenticazione in parse esaminiamo come funziona l'autenticazione nella nostra applicazione flusso di registrazione nella nostra signuppage js, raccogliamo nome utente, email e password convalidiamo i dati di input (controllando campi vuoti, formato email valido, lunghezza della password) creiamo un nuovo oggetto parse user e impostiamo le credenziali chiamiamo signup() che invia i dati a back4app analizza le hash della password prima di memorizzarla in caso di successo, l'utente viene automaticamente connesso con un token di sessione flusso di accesso nella nostra loginpage js, raccogliamo nome utente e password chiamiamo parse user login() con queste credenziali parse verifica le credenziali rispetto ai dati memorizzati se valido, parse genera un token di sessione il token di sessione è automaticamente memorizzato nello storage del browser gestione della sessione parse include automaticamente il token di sessione in tutte le richieste api utilizziamo parse user current() per recuperare l'utente attualmente connesso le sessioni persistono attraverso i ricaricamenti della pagina implementazione della registrazione dell'utente esaminiamo il nostro componente signuppage per capire come è implementata la registrazione dell'utente validazione del modulo prima di inviare i dati a back4app, convalidiamo l'input dell'utente // from signuppage js const validateform = () => { const newerrors = {}; if (!username trim()) { newerrors username = 'username is required'; } if (!email trim()) { newerrors email = 'email is required'; } else if (!/\s+@\s+\\ \s+/ test(email)) { newerrors email = 'email is invalid'; } if (!password) { newerrors password = 'password is required'; } else if (password length < 6) { newerrors password = 'password must be at least 6 characters'; } if (password !== confirmpassword) { newerrors confirmpassword = 'passwords do not match'; } seterrors(newerrors); return object keys(newerrors) length === 0; }; questa convalida garantisce che il nome utente non è vuoto l'email è valida la password è di almeno 6 caratteri la password e la conferma corrispondono gestione degli errori di registrazione il nostro gestore di registrazione include la gestione degli errori specifici di parse // from signuppage js try { // create a new user const user = new parse user(); user set('username', username); user set('email', email); user set('password', password); await user signup(); toaster create({ title 'success', description 'account created successfully!', type 'success', }); navigate('/feed'); } catch (error) { toaster create({ title 'signup failed', description error message, type 'error', }); // handle specific parse errors if (error code === 202) { seterrors({ errors, username 'username already taken'}); } else if (error code === 203) { seterrors({ errors, email 'email already in use'}); } } back4app restituisce codici di errore specifici che possiamo utilizzare per fornire un feedback utile all'utente codice 202 nome utente già in uso codice 203 email già in uso il codice completo per la registrazione dell'utente può essere trovato qui implementazione del login utente il nostro componente loginpage gestisce l'autenticazione dell'utente utilizzando parse user login() modulo di accesso il modulo di accesso raccoglie nome utente e password // from loginpage js \<form onsubmit={handlelogin}> \<vstack spacing={4}> \<field label="username"> \<input type="text" value={username} onchange={(e) => setusername(e target value)} placeholder="your username" required /> \</field> \<field label="password" errortext={error} \> \<input type="password" value={password} onchange={(e) => setpassword(e target value)} placeholder="your password" required /> \</field> \<link as={routerlink} to="/reset password" alignself="flex end" fontsize="sm"> forgot password? \</link> \<button colorscheme="blue" width="full" type="submit" loading={isloading} \> log in \</button> \</vstack> \</form> verifica della sessione come mostrato in precedenza, controlliamo se esiste una sessione quando si carica la pagina di accesso // from loginpage js useeffect(() => { const checkcurrentuser = async () => { try { const user = await parse user current(); if (user) { setcurrentuser(user); navigate('/feed'); } } catch (error) { console error('error checking current user ', error); } }; checkcurrentuser(); }, \[navigate]); questa è una funzionalità chiave di parse gestisce automaticamente il token di sessione nello storage del browser, permettendoci di controllare facilmente se un utente è già connesso implementazione del ripristino della password back4app fornisce un flusso di ripristino della password integrato nella nostra applicazione, colleghiamo a una pagina di ripristino della password dal modulo di accesso // from loginpage js \<link as={routerlink} to="/reset password" alignself="flex end" fontsize="sm"> forgot password? \</link> il processo di ripristino della password in back4app funziona come segue l'utente richiede un ripristino della password con la propria email parse invia un link speciale di ripristino all'email dell'utente l'utente clicca sul link e imposta una nuova password parse aggiorna l'hash della password nel database per implementare questo nella nostra applicazione, utilizzeremmo // example password reset implementation try { await parse user requestpasswordreset(email); // show success message } catch (error) { // handle error } proteggere le rotte per gli utenti autenticati per proteggere alcune rotte nella nostra applicazione, utilizziamo un componente protectedroute che verifica se un utente è autenticato // from protectedroute js function protectedroute({ children }) { const { currentuser, isloading } = useauth(); if (isloading) { return ( \<flex justify="center" align="center" height="100vh"> \<spinner size="xl" /> \</flex> ); } if (!currentuser) { return \<navigate to="/login" />; } return children; } questo componente utilizza il nostro authcontext per controllare se un utente è connesso mostra un'animazione di caricamento mentre controlla reindirizza alla pagina di accesso se non viene trovato alcun utente renderizza il contenuto protetto se un utente è autenticato utilizziamo questo componente nella nostra configurazione di routing // from app js \<route path="/feed" element={ \<protectedroute> \<feedpage /> \</protectedroute> } /> configurazione dell'autenticazione back4app back4app offre diverse opzioni di configurazione per l'autenticazione nel dashboard verifica email puoi richiedere la verifica dell'email prima che gli utenti possano accedere configura questo in "impostazioni del server" > "parse server" > "autenticazione utente" politica delle password imposta la lunghezza minima della password e i requisiti di complessità configura questo in "impostazioni del server" > "parse server" > "autenticazione utente" durata della sessione controlla quanto a lungo le sessioni utente rimangono valide configura questo in "impostazioni del server" > "parse server" > "configurazione della sessione" modelli email personalizza le email di verifica e di reset della password configura questo in "impostazioni app" > "modelli email" testare la tua implementazione di autenticazione per garantire che il tuo sistema di autenticazione funzioni correttamente testare la registrazione dell'utente prova a registrarti con credenziali valide prova a registrarti con un nome utente esistente (dovrebbe mostrare un errore) controlla se l'utente appare nel tuo dashboard di back4app sotto la classe " user" testare il login dell'utente prova a effettuare il login con credenziali corrette (dovrebbe reindirizzare al feed) prova a effettuare il login con credenziali errate (dovrebbe mostrare un errore) testare la persistenza della sessione accedi e aggiorna la pagina (dovresti rimanere connesso) chiudi e riapri il browser (dovresti rimanere connesso se la sessione è valida) testare le rotte protette prova ad accedere a /feed quando sei disconnesso (dovrebbe reindirizzare al login) prova ad accedere a /feed quando sei connesso (dovrebbe mostrare la pagina del feed) il codice per il componente di login può essere trovato qui passo 4 — sviluppare la funzionalità feed in questo passo, implementerai la funzionalità principale del social networking il feed qui gli utenti creeranno post, visualizzeranno contenuti di altri e interagiranno tramite like e commenti utilizzeremo il parse server di back4app per memorizzare e recuperare i post, gestire i caricamenti di file per le immagini e implementare aggiornamenti in tempo reale comprendere la struttura della pagina feed la pagina feed nella nostra applicazione ha tre componenti principali una barra laterale per la navigazione l'area principale del feed con creazione e elenco dei post una sezione in tendenza (su schermi più grandi) esaminiamo come questo è implementato nel nostro feedpage js // from feedpage js main structure function feedpage() { // state and hooks return ( \<flex minh="100vh" bg="gray 800" color="white"> {/ left sidebar (navigation) /} \<box w={\['0px', '200px']} bg="gray 900" p={4} display={\['none', 'block']}> {/ navigation links /} \</box> {/ main content (feed) /} \<box flex="1" p={4} overflowy="auto"> {/ post creation form /} {/ posts list /} \</box> {/ right sidebar (trending) /} \<box w={\['0px', '250px']} bg="gray 700" p={4} display={\['none', 'block']}> {/ trending content /} \</box> \</flex> ); } questo layout reattivo si adatta a diverse dimensioni dello schermo, nascondendo le barre laterali sui dispositivi mobili creazione della classe post in back4app prima di implementare il frontend, assicuriamoci che il nostro database back4app sia correttamente configurato per i post la classe post dovrebbe avere i seguenti campi contenuto (stringa) il contenuto testuale del post immagine (file) allegato immagine opzionale autore (puntatore a utente) l'utente che ha creato il post mi piace (numero) conteggio dei mi piace sul post graditoda (array) array di id utente che hanno messo mi piace al post creatoil (data) aggiunto automaticamente da parse imposta le autorizzazioni appropriate per la classe post accesso in lettura pubblico tutti dovrebbero poter leggere i post accesso in scrittura pubblico gli utenti autenticati dovrebbero poter creare post autorizzazioni di aggiornamento/cancellazione solo l'autore dovrebbe poter modificare i propri post implementazione della creazione del post esaminiamo come è implementata la creazione di post nel nostro componente feedpage // from feedpage js post creation state const \[postcontent, setpostcontent] = usestate(''); const \[postimage, setpostimage] = usestate(null); const \[imagepreview, setimagepreview] = usestate(''); const \[issubmitting, setissubmitting] = usestate(false); // image selection handler const handleimageselect = (e) => { if (e target files && e target files\[0]) { const file = e target files\[0]; setpostimage(file); // create preview url const previewurl = url createobjecturl(file); setimagepreview(previewurl); } }; // post submission handler const handlesubmitpost = async () => { if (!postcontent trim() && !postimage) { toaster create({ title 'error', description 'please add some text or an image to your post', type 'error', }); return; } setissubmitting(true); try { // create a new post object const post = parse object extend('post'); const newpost = new post(); // set post content newpost set('content', postcontent); newpost set('author', parse user current()); newpost set('likes', 0); newpost set('likedby', \[]); // if there's an image, upload it if (postimage) { const parsefile = new parse file(postimage name, postimage); await parsefile save(); newpost set('image', parsefile); } // save the post await newpost save(); // reset form setpostcontent(''); setpostimage(null); setimagepreview(''); // refresh posts fetchposts(); toaster create({ title 'success', description 'your post has been published!', type 'success', }); } catch (error) { toaster create({ title 'error', description error message, type 'error', }); } finally { setissubmitting(false); } }; punti chiave sulla creazione di post gestione dei file in parse parse file viene utilizzato per caricare immagini nello storage di back4app il file viene prima salvato, poi allegato all'oggetto post back4app gestisce automaticamente lo storage dei file e genera url creazione di oggetti parse estendiamo la classe 'post' con parse object extend('post') creiamo una nuova istanza con new post() impostiamo le proprietà usando il set() metodo salviamo l'oggetto su back4app con save() associazione utente associamo il post con l'utente corrente usando parse user current() questo crea una relazione di puntatore nel database il modulo di creazione del post ui appare così {/ post creation form /} \<box mb={6} bg="gray 700" p={4} borderradius="md"> \<vstack align="stretch" spacing={4}> \<textarea placeholder="what's on your mind?" value={postcontent} onchange={(e) => setpostcontent(e target value)} minh="100px" /> {imagepreview && ( \<box position="relative"> \<image src={imagepreview} maxh="200px" borderradius="md" /> \<iconbutton icon={\<closeicon />} size="sm" position="absolute" top="2" right="2" onclick={() => { setpostimage(null); setimagepreview(''); }} /> \</box> )} \<hstack> \<button lefticon={\<attachmenticon />} onclick={() => document getelementbyid('image upload') click()} \> add image \</button> \<input id="image upload" type="file" accept="image/ " onchange={handleimageselect} display="none" /> \<button colorscheme="blue" ml="auto" isloading={issubmitting} onclick={handlesubmitpost} disabled={(!postcontent trim() && !postimage) || issubmitting} \> post \</button> \</hstack> \</vstack> \</box> recupero e visualizzazione dei post ora vediamo come recuperiamo e visualizziamo i post da back4app // from feedpage js fetching posts const \[posts, setposts] = usestate(\[]); const \[isloading, setisloading] = usestate(true); const \[page, setpage] = usestate(0); const \[hasmore, sethasmore] = usestate(true); const postsperpage = 10; const fetchposts = async (loadmore = false) => { try { const currentpage = loadmore ? page + 1 0; // create a query for the post class const post = parse object extend('post'); const query = new parse query(post); // include the author object (pointer) query include('author'); // sort by creation date, newest first query descending('createdat'); // pagination query limit(postsperpage); query skip(currentpage postsperpage); // execute the query const results = await query find(); // process the results const fetchedposts = results map(post => ({ id post id, content post get('content'), image post get('image') ? post get('image') url() null, likes post get('likes') || 0, likedby post get('likedby') || \[], createdat post get('createdat'), author { id post get('author') id, username post get('author') get('username'), avatar post get('author') get('avatar') ? post get('author') get('avatar') url() null } })); // update state if (loadmore) { setposts(prevposts => \[ prevposts, fetchedposts]); setpage(currentpage); } else { setposts(fetchedposts); setpage(0); } // check if there are more posts to load sethasmore(results length === postsperpage); } catch (error) { console error('error fetching posts ', error); toaster create({ title 'error', description 'failed to load posts please try again ', type 'error', }); } finally { setisloading(false); } }; // load posts when component mounts useeffect(() => { fetchposts(); }, \[]); punti chiave sul recupero dei post analizzare le query creiamo una query con new parse query(post) includiamo oggetti correlati con query include('author') ordiniamo con query descending('createdat') paginiamo con query limit() e query skip() eseguiamo la query con query find() elaborazione dei risultati gli oggetti parse hanno un get() metodo per accedere alle proprietà per i campi file, utilizziamo file url() per ottenere l'url trasformiamo gli oggetti parse in oggetti javascript semplici per lo stato di react paginazione implementiamo la funzionalità "carica di più" con il tracciamento delle pagine controlliamo se ci sono altri post da caricare prima di effettuare richieste aggiuntive i post sono visualizzati in un elenco {/ posts list /} {isloading ? ( \<center py={10}> \<spinner size="xl" /> \</center> ) posts length > 0 ? ( \<vstack spacing={4} align="stretch"> {posts map(post => ( \<box key={post id} p={4} bg="gray 700" borderradius="md"> {/ post header with author info /} \<hstack mb={2}> \<avatar root size="sm"> \<avatar fallback name={post author username} /> \<avatar image src={post author avatar} /> \</avatar root> \<text fontweight="bold">{post author username}\</text> \<text fontsize="sm" color="gray 400">• {formatdate(post createdat)}\</text> \</hstack> {/ post content /} \<text mb={4}>{post content}\</text> {/ post image if any /} {post image && ( \<image src={post image} maxh="400px" borderradius="md" mb={4} /> )} {/ post actions /} \<hstack> \<button variant="ghost" lefticon={\<likeicon />} onclick={() => handlelikepost(post id, post likedby)} color={post likedby includes(currentuser id) ? "blue 400" "white"} \> {post likes} likes \</button> \<button variant="ghost" lefticon={\<commenticon />} as={routerlink} to={`/post/${post id}`} \> comments \</button> \</hstack> \</box> ))} {/ load more button /} {hasmore && ( \<button onclick={() => fetchposts(true)} isloading={isloadingmore}> load more \</button> )} \</vstack> ) ( \<center py={10}> \<text>no posts yet be the first to post!\</text> \</center> )} implementazione della funzionalità mi piace esaminiamo come è implementata la funzionalità di like // from feedpage js like functionality const handlelikepost = async (postid, likedby) => { try { const currentuserid = parse user current() id; const isliked = likedby includes(currentuserid); // get the post object const post = parse object extend('post'); const query = new parse query(post); const post = await query get(postid); // update likes count and likedby array if (isliked) { // unlike remove user from likedby and decrement likes post set('likedby', likedby filter(id => id !== currentuserid)); post set('likes', (post get('likes') || 1) 1); } else { // like add user to likedby and increment likes post set('likedby', \[ likedby, currentuserid]); post set('likes', (post get('likes') || 0) + 1); } // save the updated post await post save(); // update local state setposts(prevposts => prevposts map(p => p id === postid ? { p, likes isliked ? p likes 1 p likes + 1, likedby isliked ? p likedby filter(id => id !== currentuserid) \[ p likedby, currentuserid] } p ) ); } catch (error) { console error('error liking post ', error); toaster create({ title 'error', description 'failed to like post please try again ', type 'error', }); } }; punti chiave sulla funzionalità di like aggiornamenti ottimisti aggiorniamo immediatamente l'interfaccia utente prima che il server confermi la modifica questo fa sentire l'app più reattiva aggiornamenti degli oggetti parse recuperiamo il post specifico con query get(postid) modifichiamo le sue proprietà con post set() salviamo le modifiche con post save() tracciamento dei like manteniamo sia un conteggio ( like ) che un elenco di utenti ( likedby ) questo ci consente di mostrare conteggi accurati e determinare se l'utente attuale ha messo like a un post implementazione di aggiornamenti in tempo reale con livequery (opzionale) per aggiornare il feed in tempo reale quando vengono creati nuovi post, possiamo utilizzare parse livequery // from feedpage js livequery setup const livequerysubscription = useref(null); useeffect(() => { // set up livequery for real time updates const setuplivequery = async () => { try { const post = parse object extend('post'); const query = new parse query(post); // subscribe to new posts livequerysubscription current = await query subscribe(); // when a new post is created livequerysubscription current on('create', (post) => { // only add to feed if it's not already there setposts(prevposts => { if (prevposts some(p => p id === post id)) return prevposts; const newpost = { id post id, content post get('content'), image post get('image') ? post get('image') url() null, likes post get('likes') || 0, likedby post get('likedby') || \[], createdat post get('createdat'), author { id post get('author') id, username post get('author') get('username'), avatar post get('author') get('avatar') ? post get('author') get('avatar') url() null } }; return \[newpost, prevposts]; }); }); // when a post is updated (e g , liked) livequerysubscription current on('update', (post) => { setposts(prevposts => prevposts map(p => p id === post id ? { p, content post get('content'), image post get('image') ? post get('image') url() p image, likes post get('likes') || 0, likedby post get('likedby') || \[] } p ) ); }); } catch (error) { console error('error setting up livequery ', error); } }; setuplivequery(); // clean up subscription when component unmounts return () => { if (livequerysubscription current) { livequerysubscription current unsubscribe(); } }; }, \[]); punti chiave su livequery impostazione dell'abbonamento creiamo una query e ci iscriviamo ad essa con query subscribe() questo stabilisce una connessione websocket al server livequery di back4app gestione degli eventi ascoltiamo gli eventi 'create' quando vengono creati nuovi post ascoltiamo gli eventi 'update' quando i post vengono modificati aggiorniamo il nostro stato locale di conseguenza pulizia ci disiscriveremo quando il componente viene smontato per prevenire perdite di memoria ottimizzazione del caricamento dei post con la paginazione abbiamo già implementato la paginazione di base con il pulsante "carica di più" miglioriamola con lo scrolling infinito // from feedpage js infinite scrolling const \[isloadingmore, setisloadingmore] = usestate(false); const feedref = useref(null); // intersection observer for infinite scrolling useeffect(() => { if (!hasmore) return; const observer = new intersectionobserver( (entries) => { if (entries\[0] isintersecting && !isloading && !isloadingmore) { loadmoreposts(); } }, { threshold 0 5 } ); const loadmoretrigger = document getelementbyid('load more trigger'); if (loadmoretrigger) { observer observe(loadmoretrigger); } return () => { if (loadmoretrigger) { observer unobserve(loadmoretrigger); } }; }, \[hasmore, isloading, isloadingmore]); const loadmoreposts = async () => { if (!hasmore || isloadingmore) return; setisloadingmore(true); try { await fetchposts(true); } finally { setisloadingmore(false); } }; e aggiungi questo alla fine dell'elenco dei post {/ infinite scroll trigger /} {hasmore && ( \<box id="load more trigger" h="20px" /> )} punti chiave sullo scrolling infinito intersection observer utilizziamo l'api intersection observer per rilevare quando l'utente scorre fino in fondo quando l'elemento di attivazione diventa visibile, carichiamo altri post stati di caricamento tracciamo stati di caricamento separati per il caricamento iniziale e "carica di più" questo previene richieste simultanee multiple considerazioni sulle prestazioni carichiamo solo un numero fisso di post alla volta (paginazione) controlliamo se ci sono altri post da caricare prima di effettuare richieste aggiuntive ottimizzazione delle prestazioni di back4app per ottimizzare le prestazioni quando si lavora con back4app usa gli indici aggiungi indici ai campi frequentemente interrogati nel tuo dashboard di back4app per la classe post, aggiungi indici su 'createdat' e 'author' query selettive usa query select() per recuperare solo i campi di cui hai bisogno questo riduce il trasferimento di dati e migliora le prestazioni ottimizzazione del conteggio invece di recuperare tutti i post per contarli, usa query count() questo è più efficiente per determinare i conteggi totali passo 6 — aggiungere interazioni sociali in questo passaggio, miglioreremo la nostra rete sociale implementando funzionalità chiave di interazione sociale commenti sui post, profili utente e impostazioni utente ci concentreremo su come queste funzionalità interagiscono con il backend di back4app e i meccanismi che le fanno funzionare implementazione dei commenti sui post i commenti sono una funzionalità fondamentale di interazione sociale che richiede una corretta modellazione dei dati in back4app esaminiamo come la nostra applicazione interagisce con il parse server per implementare i commenti modello dati di back4app per i commenti in back4app, i commenti sono implementati come una classe separata con relazioni sia con gli utenti che con i post struttura della classe commento contenuto (stringa) il contenuto testuale del commento autore (puntatore a utente) punta all'utente che ha creato il commento post (puntatore a post) punta al post su cui si commenta createdat (data) gestito automaticamente da parse tipi di relazione utente → commenti uno a molti (un utente può creare molti commenti) post → commenti uno a molti (un post può avere molti commenti) recupero dei commenti da back4app la nostra postdetailspage utilizza le query di parse per recuperare i commenti per un post specifico // from postdetailspage js comment fetching const fetchcomments = async () => { try { // create a query on the comment class const comment = parse object extend('comment'); const query = new parse query(comment); // find comments for this specific post using a pointer equality constraint query equalto('post', postobject); // include the author information query include('author'); // sort by creation date (newest first) query descending('createdat'); // execute the query const results = await query find(); // transform parse objects to plain objects for react state const commentslist = results map(comment => ({ id comment id, content comment get('content'), createdat comment get('createdat'), author { id comment get('author') id, username comment get('author') get('username'), avatar comment get('author') get('avatar') ? comment get('author') get('avatar') url() null } })); setcomments(commentslist); } catch (error) { console error('error fetching comments ', error); toaster create({ title 'error', description 'failed to load comments', type 'error', }); } }; meccanismi chiave di back4app parse object extend() crea un riferimento alla classe comment in back4app query equalto() crea un vincolo per trovare solo commenti per un post specifico query include() esegue un'operazione simile a un join per recuperare oggetti correlati in una singola query query descending() ordina i risultati per un campo specifico creazione di commenti in back4app quando un utente aggiunge un commento, creiamo un nuovo oggetto parse e stabilire le relazioni // from postdetailspage js adding a comment const handleaddcomment = async (e) => { e preventdefault(); if (!newcomment trim()) { return; } setiscommenting(true); try { // create a new comment object in back4app const comment = parse object extend('comment'); const comment = new comment(); // set comment data and relationships comment set('content', newcomment); comment set('author', parse user current()); // pointer to current user comment set('post', postobject); // pointer to current post // save the comment to back4app await comment save(); // clear the input setnewcomment(''); // refresh comments fetchcomments(); toaster create({ title 'success', description 'your comment has been added', type 'success', }); } catch (error) { toaster create({ title 'error', description error message, type 'error', }); } finally { setiscommenting(false); } }; meccanismi chiave di back4app new comment() crea una nuova istanza della classe comment comment set() imposta le proprietà sull'oggetto parse, inclusi i puntatori agli oggetti correlati comment save() invia l'oggetto a back4app per la memorizzazione parse user current() ottiene l'utente attualmente autenticato per stabilire la relazione dell'autore sicurezza di back4app per i commenti per proteggere correttamente i commenti in back4app configura le autorizzazioni a livello di classe (clp) leggi pubblico (tutti possono leggere i commenti) scrivi solo utenti autenticati (solo gli utenti con accesso possono commentare) aggiorna/elimina solo il creatore (solo l'autore del commento può modificare o eliminare) imposta queste autorizzazioni nel tuo dashboard di back4app { "find" { " " true }, "get" { " " true }, "create" { " " true }, "update" { "requiresauthentication" true }, "delete" { "requiresauthentication" true }, "addfield" { "requiresauthentication" true } } passo 7 creazione di profili utente con back4app i profili utente nella nostra applicazione sfruttano la classe user integrata di parse con campi personalizzati esaminiamo come profilepage js interagisce con back4app estensioni della classe user di back4app la classe user di parse è estesa con campi aggiuntivi per il nostro social network campi utente personalizzati avatar (file) immagine del profilo memorizzata nello storage di file di back4app bio (stringa) biografia dell'utente sito web (stringa) url del sito web dell'utente displayname (stringa) nome visualizzato dell'utente recupero dati utente e post la nostra profilepage recupera sia i dati dell'utente che i post dell'utente // from profilepage js profile data fetching const fetchuserdata = async () => { try { // get current user from parse session const currentuser = await parse user current(); if (!currentuser) { navigate('/login'); return; } setuser(currentuser); // create a query to find posts by this user const post = parse object extend('post'); const query = new parse query(post); query equalto('author', currentuser); query include('author'); query descending('createdat'); const results = await query find(); // transform parse objects to plain objects const postslist = results map(post => ({ id post id, content post get('content'), image post get('image') ? post get('image') url() null, likes post get('likes') || 0, createdat post get('createdat'), author { id post get('author') id, username post get('author') get('username'), avatar post get('author') get('avatar') ? post get('author') get('avatar') url() null } })); setposts(postslist); setstats(prevstats => ({ prevstats, posts postslist length })); } catch (error) { console error('error fetching user data ', error); toaster create({ title 'error', description 'failed to load profile data', type 'error', }); } finally { setisloading(false); } }; meccanismi chiave di back4app parse user current() recupera l'utente autenticato dal token di sessione query equalto('author', currentuser) crea un vincolo di uguaglianza del puntatore per trovare i post dell'utente corrente post get('image') url() accede all'url di un oggetto file di parse memorizzato in back4app implementazione delle impostazioni utente la settingspage consente agli utenti di aggiornare le informazioni del proprio profilo e gestire le impostazioni dell'account esaminiamo come interagisce con back4app // from settingspage js user settings implementation function settingspage() { const \[privacysettings, setprivacysettings] = usestate({ profilevisibility 'public', postprivacy 'friends' }); const \[twofactorauth, settwofactorauth] = usestate(false); const \[isopen, setisopen] = usestate(false); const cancelref = useref(); // save user settings to back4app const savesettings = async (settingstype, settingsdata) => { try { const currentuser = await parse user current(); if (!currentuser) { toaster create({ title 'error', description 'you must be logged in to save settings', type 'error', }); return; } // update the appropriate settings based on type switch (settingstype) { case 'privacy' currentuser set('privacysettings', settingsdata); break; case 'security' currentuser set('securitysettings', settingsdata); break; case 'notifications' currentuser set('notificationsettings', settingsdata); break; default break; } // save the user object await currentuser save(); toaster create({ title 'success', description 'your settings have been saved', type 'success', }); } catch (error) { toaster create({ title 'error', description error message, type 'error', }); } }; return ( \<box maxw="800px" mx="auto" p={4}> \<heading mb={6}>account settings\</heading> \<tabs root defaultvalue="profile"> \<tabs list> \<tabs trigger value="profile">profile\</tabs trigger> \<tabs trigger value="privacy">privacy\</tabs trigger> \<tabs trigger value="security">security\</tabs trigger> \<tabs trigger value="notifications">notifications\</tabs trigger> \<tabs indicator /> \</tabs list> {/ settings tabs content /} {/ /} \</tabs root> {/ account deactivation dialog /} \<dialog root open={isopen} onopenchange={setisopen}> {/ /} \</dialog root> \</box> ); } meccanismi chiave di back4app parse user current() ottiene l'utente attuale per aggiornare le proprie impostazioni currentuser set() aggiorna le proprietà dell'utente nell'oggetto parse user currentuser save() persiste le modifiche su back4app schema delle impostazioni utente di back4app per implementare le impostazioni in back4app aggiungi questi campi alla classe utente privacysettings (oggetto) oggetto json contenente le preferenze sulla privacy securitysettings (oggetto) oggetto json contenente le impostazioni di sicurezza notificationsettings (oggetto) oggetto json contenente le preferenze di notifica schema di esempio per questi oggetti // privacysettings { "profilevisibility" "public", // o "friends" o "private" "postprivacy" "friends", // o "public" o "private" "showactivity" true } // securitysettings { "twofactorauth" false, "loginalerts" true } // notificationsettings { "likes" true, "comments" true, "follows" true, "messages" true } funzioni cloud di back4app per interazioni sociali per interazioni sociali più complesse, puoi implementare cloud functions in back4app ad esempio, per tracciare le notifiche dei commenti // example cloud function for comment notifications parse cloud aftersave("comment", async (request) => { // only run for new comments, not updates if (request original) return; const comment = request object; const post = comment get("post"); const commenter = request user; // skip if user is commenting on their own post const postquery = new parse query("post"); const fullpost = await postquery get(post id, { usemasterkey true }); const postauthor = fullpost get("author"); if (postauthor id === commenter id) return; // create a notification const notification = parse object extend("notification"); const notification = new notification(); notification set("type", "comment"); notification set("fromuser", commenter); notification set("touser", postauthor); notification set("post", post); notification set("read", false); await notification save(null, { usemasterkey true }); }); per implementare questo vai al tuo dashboard di back4app naviga su "cloud code" > "cloud functions" crea una nuova funzione con il codice sopra distribuisci la funzione passo 8 — costruire messaggistica in tempo reale in questo passo, implementeremo la funzionalità di messaggistica in tempo reale utilizzando la funzione livequery di back4app questo permetterà agli utenti di scambiare messaggi istantaneamente senza ricaricare la pagina, creando un'esperienza di chat dinamica simile a quelle delle piattaforme di messaggistica più popolari comprendere back4app livequery prima di immergerci nell'implementazione, comprendiamo come funziona livequery di back4app che cos'è livequery? livequery è una funzionalità di parse server che consente ai client di iscriversi a query quando gli oggetti che corrispondono a queste query cambiano, il server invia automaticamente aggiornamenti ai client iscritti questo crea funzionalità in tempo reale senza dover implementare da solo una gestione complessa dei websocket come funziona livequery livequery stabilisce una connessione websocket tra il client e il server i clienti si iscrivono a query specifiche che vogliono monitorare quando i dati che corrispondono a queste query cambiano, il server invia eventi tramite websocket il cliente riceve questi eventi e aggiorna l'interfaccia utente di conseguenza eventi livequery crea attivato quando un nuovo oggetto che corrisponde alla query viene creato aggiornare attivato quando un oggetto esistente che corrisponde alla query viene aggiornato inserire attivato quando un oggetto inizia a corrispondere alla query lasciare attivato quando un oggetto non corrisponde più alla query elimina attivato quando un oggetto che corrisponde alla query viene eliminato impostare livequery in back4app per abilitare livequery per la tua applicazione, segui questi passaggi abilita il tuo sottodominio back4app accedi al tuo account back4app naviga su "impostazioni app" > "impostazioni server" trova il blocco "url del server e query live" e fai clic su "impostazioni" controlla l'opzione "attiva il tuo sottodominio back4app" questo sottodominio fungerà da server livequery attiva livequery nella stessa pagina delle impostazioni, seleziona l'opzione "attiva query in tempo reale" seleziona le classi che desideri monitorare con livequery messaggio (per i messaggi della chat) stato di digitazione (per indicatori di digitazione) conversazione (per aggiornamenti sulla conversazione) salva le tue modifiche nota il tuo url del server livequery l'url del tuo server livequery sarà nel formato wss\ //yourappname back4app io avrai bisogno di questo url per inizializzare il client livequery nella tua applicazione react configurare livequery nella tua app react per utilizzare livequery nella tua applicazione react, devi inizializzare un client livequery // from parseconfig js or app js parse initialize( process env react app parse app id, process env react app parse js key ); parse serverurl = process env react app parse server url; // initialize live queries with your subdomain parse livequeryserverurl = process env react app parse live query url; // e g , 'wss\ //yourappname back4app io' nel tuo env local file, assicurati di includere react app parse live query url=wss\ //yourappname back4app io creazione del modello dati per la messaggistica il nostro sistema di messaggistica richiede due classi principali in back4app classe di conversazione partecipanti (array) array di puntatori utente per gli utenti nella conversazione ultimomessaggio (stringa) il contenuto del messaggio più recente dataultimomessaggio (data) timestamp del messaggio più recente aggiornatoil (data) gestito automaticamente da parse classe di messaggio conversazione (puntatore) punta alla conversazione a cui appartiene questo messaggio mittente (puntatore) punta all'utente che ha inviato il messaggio contenuto (stringa) il contenuto testuale del messaggio leggere (booleano) se il messaggio è stato letto creato il (data) gestito automaticamente da parse classe typingstatus conversazione (puntatore) punta alla conversazione utente (puntatore) punta all'utente che sta digitando stascrivendo (booleano) se l'utente sta attualmente scrivendo implementazione dell'interfaccia di messaggistica esaminiamo come la nostra messagespage implementa la messaggistica in tempo reale // from messagespage js component structure function messagespage() { const \[conversations, setconversations] = usestate(\[]); const \[selectedconversation, setselectedconversation] = usestate(null); const \[messages, setmessages] = usestate(\[]); const \[newmessage, setnewmessage] = usestate(''); const \[isloading, setisloading] = usestate(true); const \[typingusers, settypingusers] = usestate(\[]); const messagesendref = useref(null); const messagesubscription = useref(null); const typingsubscription = useref(null); const conversationsubscription = useref(null); const typingtimeoutref = useref(null); // rest of the component } il componente mantiene diversi pezzi di stato conversations elenco delle conversazioni dell'utente selectedconversation la conversazione attualmente selezionata messages messaggi nella conversazione selezionata typingusers utenti che stanno attualmente digitando nella conversazione utilizza anche i riferimenti per memorizzare le sottoscrizioni livequery e gestire gli indicatori di digitazione iscriversi a livequery per i messaggi la chiave per la messaggistica in tempo reale è iscriversi a livequery per i messaggi nella conversazione attuale // from messagespage js livequery subscription for messages const subscribetomessages = async (conversation) => { try { // unsubscribe from previous subscription if exists if (messagesubscription current) { messagesubscription current unsubscribe(); } // create a query for messages in this conversation const message = parse object extend('message'); const query = new parse query(message); // create a pointer to the conversation const conversation = parse object extend('conversation'); const conversationpointer = new conversation(); conversationpointer id = conversation id; // find messages for this conversation query equalto('conversation', conversationpointer); // include the sender information query include('sender'); // subscribe to the query const subscription = await query subscribe(); messagesubscription current = subscription; // when a new message is created subscription on('create', (message) => { // add the new message to our state setmessages(prevmessages => { // check if message already exists in our list const exists = prevmessages some(m => m id === message id); if (exists) return prevmessages; // add the new message return \[ prevmessages, { id message id, content message get('content'), createdat message get('createdat'), sender { id message get('sender') id, username message get('sender') get('username'), avatar message get('sender') get('avatar') ? message get('sender') get('avatar') url() null }, read message get('read') }]; }); // scroll to bottom when new message arrives scrolltobottom(); // mark message as read if from other user if (message get('sender') id !== parse user current() id) { markmessageasread(message); } }); } catch (error) { console error('error subscribing to messages ', error); } }; meccanismi chiave di livequery creare una query creiamo una query per i messaggi nella conversazione attuale iscriversi alla query chiamiamo query subscribe() per iniziare ad ascoltare le modifiche gestire gli eventi utilizziamo subscription on('create', callback) per gestire i nuovi messaggi disiscriversi memorizziamo il riferimento all'iscrizione e ci disiscriviamo quando necessario implementazione degli indicatori di scrittura con livequery gli indicatori di scrittura sono un'altra funzionalità in tempo reale implementata con livequery // from messagespage js livequery for typing indicators const subscribetotypingstatus = async (conversation) => { try { // unsubscribe from previous subscription if exists if (typingsubscription current) { typingsubscription current unsubscribe(); } // create a query for typing status in this conversation const typingstatus = parse object extend('typingstatus'); const query = new parse query(typingstatus); // create a pointer to the conversation const conversation = parse object extend('conversation'); const conversationpointer = new conversation(); conversationpointer id = conversation id; // find typing status for this conversation query equalto('conversation', conversationpointer); // include the user information query include('user'); // subscribe to the query const subscription = await query subscribe(); typingsubscription current = subscription; // when a typing status is updated subscription on('update', (typingstatus) => { const user = typingstatus get('user'); const istyping = typingstatus get('istyping'); // update typing users list settypingusers(prevtypingusers => { // if user is typing, add them to the list if (istyping) { // check if user is already in the list const exists = prevtypingusers some(u => u id === user id); if (exists) return prevtypingusers; // add user to typing list return \[ prevtypingusers, { id user id, username user get('username') }]; } else { // if user stopped typing, remove them from the list return prevtypingusers filter(u => u id !== user id); } }); }); // when a new typing status is created subscription on('create', (typingstatus) => { const user = typingstatus get('user'); const istyping = typingstatus get('istyping'); // only add to typing users if they are actually typing if (istyping && user id !== parse user current() id) { settypingusers(prevtypingusers => { // check if user is already in the list const exists = prevtypingusers some(u => u id === user id); if (exists) return prevtypingusers; // add user to typing list return \[ prevtypingusers, { id user id, username user get('username') }]; }); } }); } catch (error) { console error('error subscribing to typing status ', error); } }; aggiornamento dello stato di digitazione quando un utente digita, aggiorniamo il suo stato di digitazione // from messagespage js updating typing status const updatetypingstatus = async (istyping) => { if (!selectedconversation) return; try { const currentuser = await parse user current(); // create a pointer to the conversation const conversation = parse object extend('conversation'); const conversationpointer = new conversation(); conversationpointer id = selectedconversation id; // check if a typing status already exists const typingstatus = parse object extend('typingstatus'); const query = new parse query(typingstatus); query equalto('conversation', conversationpointer); query equalto('user', currentuser); let typingstatus = await query first(); if (typingstatus) { // update existing typing status typingstatus set('istyping', istyping); } else { // create new typing status typingstatus = new typingstatus(); typingstatus set('conversation', conversationpointer); typingstatus set('user', currentuser); typingstatus set('istyping', istyping); } await typingstatus save(); } catch (error) { console error('error updating typing status ', error); } }; // handle typing indicator with debounce const handletyping = () => { updatetypingstatus(true); // clear previous timeout if (typingtimeoutref current) { cleartimeout(typingtimeoutref current); } // set typing to false after 3 seconds of inactivity typingtimeoutref current = settimeout(() => { updatetypingstatus(false); }, 3000); }; invio messaggi quando si inviano messaggi, creiamo un nuovo oggetto messaggio e lasciamo che livequery gestisca gli aggiornamenti // from messagespage js sending messages const sendmessage = async (e) => { e preventdefault(); if (!newmessage trim() || !selectedconversation) return; try { const currentuser = await parse user current(); // create a pointer to the conversation const conversation = parse object extend('conversation'); const conversationpointer = new conversation(); conversationpointer id = selectedconversation id; // create a new message const message = parse object extend('message'); const message = new message(); message set('content', newmessage); message set('sender', currentuser); message set('conversation', conversationpointer); message set('read', false); await message save(); // update the conversation with the last message const conversation = await new parse query(conversation) get(selectedconversation id); conversation set('lastmessage', newmessage); conversation set('lastmessagedate', new date()); await conversation save(); // clear the input setnewmessage(''); // set typing status to false updatetypingstatus(false); } catch (error) { console error('error sending message ', error); toast({ title 'error', description 'failed to send message', status 'error', duration 3000, isclosable true, }); } }; pulizia delle sottoscrizioni livequery è importante pulire le sottoscrizioni livequery quando non sono più necessarie // from messagespage js cleanup useeffect(() => { // fetch initial conversations fetchconversations(); subscribetoconversations(); // cleanup subscriptions when component unmounts return () => { if (messagesubscription current) { messagesubscription current unsubscribe(); } if (typingsubscription current) { typingsubscription current unsubscribe(); } if (conversationsubscription current) { conversationsubscription current unsubscribe(); } if (typingtimeoutref current) { cleartimeout(typingtimeoutref current); } }; }, \[]); considerazioni sulle prestazioni di back4app livequery quando implementi livequery, considera questi suggerimenti sulle prestazioni essere specifici con le query iscriviti solo ai dati di cui hai bisogno usa vincoli per limitare l'ambito delle iscrizioni ad esempio, iscriviti solo ai messaggi nella conversazione attuale gestire le iscrizioni con attenzione disiscriviti quando i dati non sono più necessari crea nuove iscrizioni quando il contesto cambia memorizza i riferimenti delle iscrizioni per disiscriversi in seguito usa acl per la sicurezza imposta acl appropriati sugli oggetti messaggio e conversazione assicurati che gli utenti possano accedere solo alle conversazioni di cui fanno parte livequery rispetta le acl, quindi gli utenti non autorizzati non riceveranno aggiornamenti ottimizza il server livequery nel dashboard di back4app, configura le classi che necessitano di livequery non abilitare livequery per classi che non necessitano di aggiornamenti in tempo reale passo 9 — implementazione della funzionalità di ricerca in questo passaggio, implementeremo una funzionalità di ricerca completa per il nostro social network gli utenti potranno cercare altri utenti, post per contenuto e hashtag questa funzione renderà più facile per gli utenti scoprire contenuti e connettersi con altri sulla piattaforma comprendere la ricerca in back4app prima di immergerci nell'implementazione, comprendiamo come funziona la ricerca in back4app sistema di query parse back4app utilizza il sistema di query di parse server per la ricerca le query possono essere eseguite su più classi puoi cercare per corrispondenza esatta, contiene, inizia con, ecc opzioni di ricerca testuale iniziacon trova stringhe che iniziano con una stringa specifica contiene trova stringhe che contengono una sottostringa specifica corrisponde utilizza espressioni regolari per una corrispondenza di pattern più complessa fulltext (funzione enterprise) fornisce capacità avanzate di ricerca full text considerazioni sulle prestazioni le ricerche testuali possono essere intensive in termini di risorse gli indici dovrebbero essere creati per i campi frequentemente cercati le query dovrebbero essere ottimizzate per limitare il numero di risultati costruire la pagina di ricerca il nostro componente searchpage gestirà diversi tipi di ricerche e visualizzerà i risultati esaminiamo la sua struttura // from searchpage js component structure function searchpage() { const \[searchquery, setsearchquery] = usestate(''); const \[searchtype, setsearchtype] = usestate('users'); // 'users', 'posts', 'hashtags' const \[searchresults, setsearchresults] = usestate(\[]); const \[isloading, setisloading] = usestate(false); const \[trendingtopics, settrendingtopics] = usestate(\[]); // rest of the component } il componente mantiene lo stato per la query di ricerca inserita dall'utente il tipo di ricerca in corso i risultati della ricerca stato di caricamento argomenti di tendenza implementazione della ricerca utenti vediamo come cerchiamo gli utenti in back4app // from searchpage js user search implementation const searchusers = async (query) => { setisloading(true); try { // create a query on the user class const userquery = new parse query(parse user); // search for usernames that contain the query string (case insensitive) userquery matches('username', new regexp(query, 'i')); // limit results to improve performance userquery limit(20); const users = await userquery find(); // transform parse objects to plain objects const userresults = users map(user => ({ id user id, username user get('username'), avatar user get('avatar') ? user get('avatar') url() null, bio user get('bio') || '' })); setsearchresults(userresults); } catch (error) { console error('error searching users ', error); toaster create({ title 'error', description 'failed to search users', type 'error', }); } finally { setisloading(false); } }; meccanismi chiave di back4app new parse query(parse user) crea una query sulla classe user userquery matches('username', new regexp(query, 'i')) esegue una corrispondenza regex case insensitive sui nomi utente userquery limit(20) limita i risultati per migliorare le prestazioni userquery find() esegue la query e restituisce gli utenti corrispondenti implementazione della ricerca contenuti post ora vediamo come cerchiamo i post per contenuto // from searchpage js post search implementation const searchposts = async (query) => { setisloading(true); try { // create a query on the post class const post = parse object extend('post'); const postquery = new parse query(post); // search for posts with content containing the query string postquery matches('content', new regexp(query, 'i')); // include the author information postquery include('author'); // sort by creation date (newest first) postquery descending('createdat'); // limit results postquery limit(20); const posts = await postquery find(); // transform parse objects to plain objects const postresults = posts map(post => ({ id post id, content post get('content'), image post get('image') ? post get('image') url() null, likes post get('likes') || 0, createdat post get('createdat'), author { id post get('author') id, username post get('author') get('username'), avatar post get('author') get('avatar') ? post get('author') get('avatar') url() null } })); setsearchresults(postresults); } catch (error) { console error('error searching posts ', error); toaster create({ title 'error', description 'failed to search posts', type 'error', }); } finally { setisloading(false); } }; meccanismi chiave di back4app parse object extend('post') riferisce alla classe post postquery matches('content', new regexp(query, 'i')) esegue una corrispondenza regex senza distinzione tra maiuscole e minuscole sul contenuto del post postquery include('author') include le informazioni sull'autore in un'unica query postquery descending('createdat') ordina i risultati per data di creazione implementazione della ricerca per hashtag la ricerca per hashtag richiede un approccio diverso cercheremo post che contengono hashtag // from searchpage js hashtag search implementation const searchhashtags = async (query) => { setisloading(true); try { // remove # if present at the beginning const hashtagquery = query startswith('#') ? query substring(1) query; // create a query on the post class const post = parse object extend('post'); const postquery = new parse query(post); // search for posts with content containing the hashtag // we use word boundaries to find actual hashtags postquery matches('content', new regexp(`#${hashtagquery}\\\b`, 'i')); // include the author information postquery include('author'); // sort by creation date (newest first) postquery descending('createdat'); // limit results postquery limit(20); const posts = await postquery find(); // transform parse objects to plain objects const hashtagresults = posts map(post => ({ id post id, content post get('content'), image post get('image') ? post get('image') url() null, likes post get('likes') || 0, createdat post get('createdat'), author { id post get('author') id, username post get('author') get('username'), avatar post get('author') get('avatar') ? post get('author') get('avatar') url() null } })); setsearchresults(hashtagresults); } catch (error) { console error('error searching hashtags ', error); toaster create({ title 'error', description 'failed to search hashtags', type 'error', }); } finally { setisloading(false); } }; meccanismi chiave di back4app utilizziamo una regex con confini di parola ( \\\b ) per trovare hashtag effettivi questo approccio trova post in cui il contenuto contiene l'hashtag specifico implementazione degli argomenti di tendenza per implementare gli argomenti di tendenza, dobbiamo analizzare i post recenti e contare le occorrenze degli hashtag // from searchpage js fetching trending topics const fetchtrendingtopics = async () => { try { // create a query on the post class const post = parse object extend('post'); const query = new parse query(post); // get posts from the last 7 days const oneweekago = new date(); oneweekago setdate(oneweekago getdate() 7); query greaterthan('createdat', oneweekago); // limit to a reasonable number for analysis query limit(500); const posts = await query find(); // extract hashtags from post content const hashtagcounts = {}; posts foreach(post => { const content = post get('content') || ''; // find all hashtags in the content const hashtags = content match(/#(\w+)/g) || \[]; // count occurrences of each hashtag hashtags foreach(hashtag => { const tag = hashtag tolowercase(); hashtagcounts\[tag] = (hashtagcounts\[tag] || 0) + 1; }); }); // convert to array and sort by count const trendingarray = object entries(hashtagcounts) map((\[hashtag, count]) => ({ hashtag, count })) sort((a, b) => b count a count) slice(0, 10); // get top 10 settrendingtopics(trendingarray); } catch (error) { console error('error fetching trending topics ', error); } }; meccanismi chiave di back4app interroghiamo i post degli ultimi 7 giorni utilizzando query greaterthan('createdat', oneweekago) analizziamo il contenuto per estrarre e contare gli hashtag ordiniamo per frequenza per trovare gli hashtag più popolari gestione dell'esecuzione della ricerca ora vediamo come gestiamo l'esecuzione della ricerca in base al tipo di ricerca // from searchpage js search execution const handlesearch = (e) => { e preventdefault(); if (!searchquery trim()) return; switch (searchtype) { case 'users' searchusers(searchquery); break; case 'posts' searchposts(searchquery); break; case 'hashtags' searchhashtags(searchquery); break; default searchusers(searchquery); } }; ottimizzazione della ricerca in back4app per ottimizzare le prestazioni di ricerca in back4app crea indici naviga al tuo dashboard di back4app vai a "database browser" > seleziona la classe (es utente, post) clicca sulla scheda "indici" crea indici per i campi frequentemente cercati per la classe utente crea un indice su username per la classe post crea un indice su content usa vincoli di query usa sempre limit() per limitare il numero di risultati usa select() per recuperare solo i campi di cui hai bisogno usa skip() per la paginazione quando gestisci grandi set di risultati considera le funzioni cloud per ricerche complesse per una logica di ricerca più complessa, implementa una funzione cloud questo ti consente di eseguire l'elaborazione lato server e restituire risultati ottimizzati esempio di cloud function per ricerca avanzata // example cloud function for advanced search parse cloud define("advancedsearch", async (request) => { const { query, type, limit = 20 } = request params; if (!query) { throw new error("search query is required"); } let results = \[]; switch (type) { case 'users' const userquery = new parse query(parse user); userquery matches('username', new regexp(query, 'i')); userquery limit(limit); results = await userquery find({ usemasterkey true }); break; case 'posts' const post = parse object extend('post'); const postquery = new parse query(post); postquery matches('content', new regexp(query, 'i')); postquery include('author'); postquery limit(limit); results = await postquery find({ usemasterkey true }); break; // add more search types as needed default throw new error("invalid search type"); } return results; }); passo 10 — testare e distribuire il tuo social network in questo passaggio finale, tratteremo come testare la tua applicazione, prepararla per la produzione, distribuirla a un servizio di hosting e monitorare e scalare il tuo backend di back4app questi passaggi sono cruciali per garantire che il tuo social network funzioni senza problemi in un ambiente di produzione testare la tua applicazione prima di distribuire, è importante testare a fondo la tua applicazione per individuare eventuali bug o problemi 1\ test manuali crea un piano di test che copra tutte le funzionalità chiave della tua applicazione autenticazione dell'utente registrazione di prova con input validi e non validi test di accesso con credenziali corrette e incorrette testare la funzionalità di reimpostazione della password testare la persistenza della sessione e il logout funzionalità del post testare la creazione di post con testo e immagini testare la visualizzazione dei post nel feed testare l'apprezzamento e il commento sui post testare l'eliminazione dei post interazioni sociali test di visualizzazione dei profili utente testare i commenti sui post test di messaggistica in tempo reale funzionalità di ricerca test di ricerca per utenti testa la ricerca di post testa la ricerca degli hashtag test cross browser test su chrome, firefox, safari e edge test su browser mobili 2\ test automatizzati per test più robusti, implementa test automatizzati // example jest test for the login component import react from 'react'; import { render, fireevent, waitfor } from '@testing library/react'; import loginpage from ' /src/pages/loginpage'; import parse from 'parse/dist/parse min js'; // mock parse jest mock('parse/dist/parse min js', () => ({ user { login jest fn() } })); test('login form submits with username and password', async () => { parse user login mockresolvedvalueonce({ id '123', get () => 'testuser' }); const { getbylabeltext, getbyrole } = render(\<loginpage />); // fill in the form fireevent change(getbylabeltext(/username/i), { target { value 'testuser' } }); fireevent change(getbylabeltext(/password/i), { target { value 'password123' } }); // submit the form fireevent click(getbyrole('button', { name /log in/i })); // check if parse user login was called with correct arguments await waitfor(() => { expect(parse user login) tohavebeencalledwith('testuser', 'password123'); }); }); 3\ test di back4app testa la tua configurazione di back4app funzioni cloud testa tutte le funzioni cloud con vari input sicurezza verifica che i permessi a livello di classe funzionino correttamente livequery testa la funzionalità in tempo reale con più client prepararsi per la produzione prima di distribuire, ottimizza la tua applicazione per la produzione 1\ configurazione dell'ambiente crea file di ambiente separati per lo sviluppo e la produzione \# env development react app parse app id=your dev app id react app parse js key=your dev js key react app parse server url=https //parseapi back4app com react app parse live query url=wss\ //your dev app back4app io \# env production react app parse app id=your production app id react app parse js key=your production js key react app parse server url=https //parseapi back4app com react app parse live query url=wss\ //your prod app back4app io 2\ ottimizzazione della build ottimizza la tua build di react // in your package json "scripts" { "analyze" "source map explorer 'build/static/js/ js'", "build" "generate sourcemap=false react scripts build" } installa il source map explorer per analizzare la dimensione del tuo bundle npm install save dev source map explorer 3\ ottimizzazione delle prestazioni implementa il code splitting per ridurre il tempo di caricamento iniziale // in app js, use react lazy for route components import react, { suspense, lazy } from 'react'; import { browserrouter as router, routes, route } from 'react router dom'; import { chakraprovider } from '@chakra ui/react'; import loadingspinner from ' /components/loadingspinner'; // lazy load pages const landingpage = lazy(() => import(' /pages/landingpage')); const loginpage = lazy(() => import(' /pages/loginpage')); const feedpage = lazy(() => import(' /pages/feedpage')); // other pages function app() { return ( \<chakraprovider> \<router> \<suspense fallback={\<loadingspinner />}> \<routes> \<route path="/" element={\<landingpage />} /> \<route path="/login" element={\<loginpage />} /> \<route path="/feed" element={\<feedpage />} /> {/ other routes /} \</routes> \</suspense> \</router> \</chakraprovider> ); } distribuzione su un servizio di hosting ci sono diverse opzioni per distribuire la tua applicazione react 1\ distribuzione su vercel vercel è una grande opzione per le applicazioni react installa il vercel cli npm install g vercel distribuisci la tua applicazione vercel per la distribuzione in produzione vercel prod 2\ distribuzione su netlify netlify è un'altra ottima opzione installa il netlify cli npm install g netlify cli costruisci la tua applicazione npm run build distribuisci su netlify netlify deploy per la distribuzione in produzione netlify deploy prod 3\ distribuzione su github pages per un'opzione di distribuzione semplice installa gh pages npm install save dev gh pages aggiungi a package json "homepage" "https //yourusername github io/your repo name", "scripts" { "predeploy" "npm run build", "deploy" "gh pages d build" } distribuisci npm run deploy monitoraggio e scalabilità del tuo backend back4app man mano che la tua rete sociale cresce, dovrai monitorare e scalare il tuo backend back4app 1\ monitoraggio delle prestazioni back4app fornisce diversi strumenti per monitorare la tua applicazione dashboard analytics monitora le richieste api, l'uso dello spazio di archiviazione e le operazioni sui file logs controlla i log del server per errori e problemi di prestazioni performance metrics monitora i tempi di risposta e identifica i colli di bottiglia per accedere a questi strumenti vai al tuo dashboard di back4app naviga su "analytics" per le statistiche di utilizzo controlla "logs" per i log dettagliati delle operazioni 2\ scalare il tuo backend quando la tua base utenti cresce, potresti dover scalare il tuo backend di back4app aggiorna il tuo piano passa a un piano di livello superiore con più risorse ottimizza le query usa indici e limita le query per migliorare le prestazioni implementa la cache usa la cache lato client per i dati frequentemente accessibili 3\ ottimizzazione del database ottimizza il tuo database per migliori prestazioni crea indici aggiungi indici ai campi frequentemente interrogati // esempio creazione di un indice sul campo 'username' nella classe user const schema = new parse schema(' user'); schema addindex('username index', { username 1 }); schema update(); usa aggregation pipeline per operazioni sui dati complesse // esempio conta i post per utente const pipeline = \[ { group { objectid '$author', count { $sum 1 } } } ]; const results = await parse cloud aggregate('post', pipeline); 4\ implementazione di un cdn per i media per una consegna più veloce di immagini e media configura un cdn come cloudflare o amazon cloudfront aggiorna le impostazioni di archiviazione file di back4app per utilizzare il cdn aggiorna gli url dei file nella tua applicazione per utilizzare il dominio cdn 5\ impostare gli avvisi di monitoraggio imposta avvisi per essere notificato di problemi vai al tuo dashboard di back4app naviga su "impostazioni app" > "avvisi" configura avvisi per alto utilizzo api picchi nel tasso di errore limiti delle dimensioni del database tempo di inattività del server riepilogo di ciò che è stato realizzato durante questo tutorial, hai imposta un'infrastruttura backend robusta hai creato un account back4app e configurato la tua applicazione progettato uno schema di database per utenti, post, commenti e messaggi impostazioni di sicurezza configurate e autorizzazioni a livello di classe imposta livequery per funzionalità in tempo reale costruito un moderno frontend react creato un'interfaccia utente reattiva con i componenti chakra ui implementato il routing lato client con react router sviluppato componenti riutilizzabili per post, commenti e profili utente collegato il tuo frontend a back4app utilizzando il parse javascript sdk implementate le funzionalità principali dei social network autenticazione utente (registrazione, accesso, reimpostazione della password) creazione e interazione dei post (mi piace, commenti) profili utente e impostazioni messaggistica in tempo reale tra utenti funzionalità di ricerca per utenti, post e hashtag ottimizzato per la produzione implementate ottimizzazioni delle prestazioni come il code splitting imposta le configurazioni dell'ambiente per lo sviluppo e la produzione imparato come distribuire la tua applicazione ai servizi di hosting esplorato strategie di monitoraggio e scalabilità per il tuo backend back4app ora hai una solida base per un'applicazione di social network che può essere estesa e personalizzata per soddisfare le tue esigenze specifiche prossimi passi per estendere l'applicazione ecco alcuni modi entusiasmanti per migliorare la tua applicazione di social network funzionalità multimediali avanzate aggiungi supporto per il caricamento e la riproduzione di video implementare filtri per immagini e strumenti di editing crea storie o funzionalità di contenuto effimero aggiungi supporto per gif e altri media ricchi interazioni sociali migliorate implementare un motore di raccomandazione per suggerimenti di amici aggiungi funzionalità di gruppi o comunità crea eventi con capacità di rsvp sviluppa un sistema di notifiche per tutte le interazioni degli utenti opzioni di monetizzazione implementare le funzionalità di abbonamento premium aggiungi acquisti in app per beni digitali crea un mercato per transazioni da utente a utente integra con i processori di pagamento come stripe esperienza mobile converti la tua applicazione in un'app web progressiva (pwa) sviluppa app mobili native utilizzando react native implementare le notifiche push per dispositivi mobili ottimizza l'interfaccia utente per diverse dimensioni e orientamenti dello schermo analisi e approfondimenti integra strumenti di analisi per monitorare il coinvolgimento degli utenti crea cruscotti per le prestazioni dei contenuti implementare test a/b per nuove funzionalità sviluppare intuizioni sul comportamento degli utenti per migliorare la piattaforma moderazione dei contenuti implementare il filtraggio automatico dei contenuti crea sistemi di reporting per contenuti inappropriati sviluppare strumenti di amministrazione per la moderazione dei contenuti utilizza l'apprendimento automatico per un'analisi intelligente dei contenuti risorse aggiuntive per l'apprendimento per continuare ad ampliare le tue conoscenze e abilità, ecco alcune risorse preziose documentazione e tutorial di back4app documentazione di back4app https //www back4app com/docs/get started/welcome guida javascript di parse https //docs parseplatform org/js/guide/ canale youtube di back4app https //www youtube com/c/back4app react e javascript moderno documentazione di react https //reactjs org/docs/getting started html javascript info https //javascript info/ corsi di react su egghead io https //egghead io/q/react design ui e ux documentazione di chakra ui https //chakra ui com/docs/getting started modelli di design ui https //ui patterns com/ ricerca ux del nielsen norman group https //www nngroup com/articles/ ottimizzazione delle prestazioni prestazioni web dev https //web dev/performance scoring/ ottimizzazione delle prestazioni di react https //reactjs org/docs/optimizing performance html google pagespeed insights https //developers google com/speed/pagespeed/insights/ comunità e supporto stack overflow https //stackoverflow\ com/questions/tagged/parse platform forum della comunità parse https //community parseplatform org/ comunità di sviluppatori react https //dev to/t/react ricorda che costruire una rete sociale di successo è un processo iterativo inizia con una base solida (che ora hai), raccogli feedback dagli utenti e migliora continuamente la tua applicazione in base ai modelli di utilizzo nel mondo reale speriamo che questo tutorial ti abbia fornito le conoscenze e la fiducia per costruire applicazioni straordinarie con react e back4app buon coding