Web razvoj jedan je od najpopularnijih slučajeva korištenja za programiranje. Python je jedan od najpopularnijih programskih jezika na svijetu. Pa zašto ne možemo graditi web aplikacije u Pythonu?
Izrada korisničkog sučelja trebala bi biti jednostavna, ali čak i ako imate izvrsne inženjere u svom timu, dodatni troškovi učenja novog jezika i alata bili su velika prepreka. Često bi izrada korisničkog sučelja mogla biti teža od stvarnog posla koji netko obavlja! Napravili smo Reflex, Python web framework otvorenog koda kako bismo riješili točno ovaj problem.
Ispod haube, Reflex aplikacije kompiliraju se u sučeljnu aplikaciju React i pozadinsku aplikaciju FastAPI . Samo je korisničko sučelje kompajlirano u Javascript; sva logika aplikacije i upravljanje stanjem ostaju u Pythonu i izvode se na poslužitelju. Reflex koristi WebSockets za slanje događaja iz sučelja u pozadinu i za slanje ažuriranja stanja iz pozadine u sučelje.
Već je postojalo nekoliko načina za izradu aplikacija u Pythonu, ali nijedan od njih nije odgovarao našim potrebama.
S jedne strane, postoje okviri kao što su Django i Flask koji su izvrsni za izgradnju web aplikacija proizvodne razine. Ali oni rukuju samo pozadinom - i dalje trebate koristiti JavaScript i okvir sučelja, kao i napisati puno standardnog koda za povezivanje sučelja i pozadine.
S druge strane, čiste Python biblioteke kao što su Dash i Streamlit mogu biti izvrsne za male projekte, ali su ograničene na određeni slučaj upotrebe i nemaju značajke i performanse za izgradnju pune web aplikacije. Kako vaša aplikacija raste u značajkama i složenosti, možda ćete doći do granica okvira, u kojem trenutku ili morate ograničiti svoju ideju da odgovara okviru, ili odbaciti svoj projekt i ponovno ga izgraditi pomoću "pravog web okvira".
Želimo premostiti ovaj jaz stvaranjem okvira s kojim je lako i intuitivno započeti, a da pritom ostane fleksibilan i snažan za podršku bilo kojoj aplikaciji.
Sada zaronimo u to kako smo izgradili Reflex kako bismo ispunili te ciljeve.
Full-stack web aplikacije sastoje se od frontenda i backenda. Sučelje je korisničko sučelje, a poslužuje se kao web stranica koja se pokreće u pregledniku korisnika. Pozadina upravlja logikom i upravljanjem stanjem (kao što su baze podataka i API-ji) i izvodi se na poslužitelju. U tradicionalnom razvoju weba, to su obično dvije zasebne aplikacije i često su napisane u različitim okvirima ili jezicima. Na primjer, možete kombinirati pozadinu Flaska s sučeljem Reacta. S ovim pristupom, morate održavati dvije odvojene aplikacije i na kraju napisati puno standardnog koda za povezivanje sučelja i pozadine.
Želimo pojednostaviti ovaj proces u Reflexu definiranjem i sučelja i pozadine u jednoj bazi koda, koristeći Python za sve. Programeri bi trebali brinuti samo o logici svoje aplikacije, a ne o detaljima implementacije niske razine.
Želimo da Reflex aplikacije krajnjem korisniku izgledaju i doimaju se poput tradicionalne web aplikacije, a da ih razvojni programer još uvijek lako izgradi i održava. Da bismo to učinili, nadogradili smo se na zrele i popularne web tehnologije.
Kada reflex run
svoju aplikaciju, Reflex kompilira sučelje u Next.js aplikaciju s jednom stranicom i poslužuje je na priključku (prema zadanim postavkama 3000
) kojemu možete pristupiti u svom pregledniku.
Posao sučelja je odražavati stanje aplikacije i slati događaje u pozadinu kada korisnik komunicira s korisničkim sučeljem. Na sučelju se ne izvodi stvarna logika.
Sučelja Reflexa izgrađena su pomoću komponenti koje se mogu sastaviti kako bi se stvorila složena sučelja. Umjesto da koristimo jezik za izradu predložaka koji miješa HTML i Python, koristimo samo Python funkcije za definiranje korisničkog sučelja.
Ispod haube, komponente se kompiliraju u React komponente. Mnoge naše osnovne komponente temelje se na Radixu , popularnoj biblioteci komponenti Reacta. Imamo i mnoge druge komponente za crtanje grafikona, podatkovne tablice i još mnogo toga. Izabrali smo React jer je to popularna knjižnica s ogromnim ekosustavom. Naš cilj nije ponovno stvoriti web ekosustav, već ga učiniti dostupnim Python programerima.
To također omogućuje našim korisnicima da donesu vlastite komponente ako mi nemamo komponentu koja im je potrebna. Korisnici mogu omotati svoje React komponente i zatim ih objaviti da ih drugi mogu koristiti. S vremenom ćemo izgraditi naš ekosustav komponenti treće strane kako bi korisnici mogli lako pronaći i koristiti komponente koje su drugi napravili.
Htjeli smo se pobrinuti da Reflex aplikacije izgledaju dobro odmah nakon otvaranja, a da pritom razvojnim programerima daju potpunu kontrolu nad izgledom svojih aplikacija.
Imamo temeljni sustav temiranja koji vam omogućuje postavljanje stilskih opcija na visokoj razini kao što su tamni način rada i boja naglašavanja u cijeloj aplikaciji kako biste joj dali jedinstveni izgled i dojam.
Osim toga, komponente Reflexa mogu se stilizirati korištenjem pune snage CSS-a. Koristimo biblioteku Emotion kako bismo omogućili "CSS-in-Python" stiliziranje, tako da možete proslijediti bilo koji CSS prop kao argument ključne riječi komponenti. To uključuje responzivne rekvizite prosljeđivanjem popisa vrijednosti.
U Reflexu se samo sučelje kompajlira u Javascript i izvodi u pregledniku korisnika, dok sva stanja i logika ostaju u Pythonu i izvode se na poslužitelju. Kada reflex run
, pokrećemo FastAPI poslužitelj (prema zadanim postavkama na portu 8000
) s kojim se sučelje povezuje putem websocketa.
Sva stanja i logika definirani su unutar klase State
. Stanje se sastoji od varijabli i rukovatelja događajima . Varijante su sve vrijednosti u vašoj aplikaciji koje se mogu mijenjati tijekom vremena. Oni su definirani kao atributi klase u vašoj klasi State
i mogu biti bilo koje vrste Pythona koji se mogu serijalizirati u JSON.
Rukovatelji događajima su metode u vašoj klasi State
koje se pozivaju kada korisnik komunicira s korisničkim sučeljem. Oni su jedini način na koji možemo modificirati varove u Reflexu i mogu se pozvati kao odgovor na radnje korisnika, kao što je klik na gumb ili upisivanje u tekstni okvir.
Budući da se rukovatelji događajima pokreću na pozadini, možete koristiti bilo koju Python biblioteku unutar njih.
Obično kada pišete web aplikacije, morate napisati puno standardnog koda za povezivanje frontenda i backenda. S Reflexom ne morate brinuti o tome - mi upravljamo komunikacijom između sučelja i pozadine umjesto vas. Programeri samo moraju napisati svoju logiku rukovatelja događajima, a kada se promjene ažuriraju, korisničko sučelje se automatski ažurira.
Korisnik može komunicirati s korisničkim sučeljem na mnoge načine, poput klikanja gumba, upisivanja u tekstni okvir ili lebdenja iznad elementa. U Reflexu te događaje nazivamo okidačima .
Na sučelju održavamo red čekanja svih događaja na čekanju. Događaj se sastoji od tri glavna podatka:
Kada se događaj pokrene, dodaje se u red čekanja. Imamo oznaku processing
koja osigurava da se samo jedan događaj obrađuje u isto vrijeme. To osigurava da je stanje uvijek dosljedno i da nema uvjeta utrke s dva rukovatelja događajima koji mijenjaju stanje u isto vrijeme. Postoje iznimke od toga, kao što su pozadinski događaji koji vam omogućuju pokretanje događaja u pozadini bez blokiranja korisničkog sučelja.
Nakon što je događaj spreman za obradu, šalje se u pozadinu putem WebSocket veze.
Nakon što se događaj primi, obrađuje se u pozadini. Reflex koristi upravitelja stanja koji održava mapiranje između klijentskih tokena i njihovog stanja. Prema zadanim postavkama, upravitelj stanja je samo rječnik u memoriji, ali se može proširiti za korištenje baze podataka ili predmemorije. U proizvodnji koristimo Redis kao upravitelja stanja.
Kada imamo stanje korisnika, sljedeći korak je pokretanje rukovatelja događajima s argumentima.
Svaki put kada se rukovatelj događajima vrati (ili preda), spremamo stanje u upravitelju stanja i šaljemo ažuriranja stanja sučelju da ažurira korisničko sučelje. Kako bi održao performanse kako vaše stanje raste, Reflex interno prati varijante koje su ažurirane tijekom rukovatelja događajima ( prljave varijante ).
Kada rukovatelj događajima završi s obradom, pronalazimo sve prljave varijante i stvaramo ažuriranje stanja za slanje sučelju.
Novo stanje pohranjujemo u našem upravitelju stanja, a zatim ažuriranje stanja šaljemo sučelju. Sučelje zatim ažurira korisničko sučelje kako bi odražavalo novo stanje.
Nadam se da ovo pruža dobar pregled kako Reflex radi ispod haube. Objavit ćemo još postova u kojima ćemo podijeliti kako smo Reflex učinili skalabilnim i učinkovitim pomoću značajki kao što su dijeljenje stanja i optimizacija prevoditelja.