paint-brush
Design af et rent Python Web Frameworkved@reflexdev
274 aflæsninger

Design af et rent Python Web Framework

ved Reflex.dev7m2024/09/23
Read on Terminal Reader

For langt; At læse

Reflex er open source-rammeværket, der giver Python-udviklere mulighed for at bygge webapps hurtigere. Byg både din frontend og backend på et enkelt sprog, Python (pip-installationsrefleks), uden JavaScript eller webudviklingserfaring.
featured image - Design af et rent Python Web Framework
Reflex.dev HackerNoon profile picture
0-item


Webudvikling er en af de mest populære use cases til programmering. Python er et af de mest populære programmeringssprog i verden. Så hvorfor kan vi ikke bygge webapps i Python?


Det skal være enkelt at lave en brugergrænseflade, men selv du har gode ingeniører på dit team, var det at lære et nyt sprog og nye værktøjer en enorm barriere. Ofte kan det være sværere at lave en brugergrænseflade end det faktiske arbejde, man udfører! Vi byggede Reflex, en open source Python-webramme for at løse netop dette problem.

TLDR

Under hætten kompilerer Reflex-apps til en React- frontend-app og en FastAPI- backend-app. Kun brugergrænsefladen er kompileret til Javascript; al applogik og tilstandsstyring forbliver i Python og køres på serveren. Reflex bruger WebSockets til at sende hændelser fra frontend til backend og til at sende tilstandsopdateringer fra backend til frontend.

Eksisterende Python-løsninger

Der var allerede et par måder at bygge apps i Python på, men ingen af dem passede til vores behov.


På den ene side er der rammer som Django og Flask , der er gode til at bygge produktions-grade web-apps. Men de håndterer kun backend - du skal stadig bruge JavaScript og en frontend framework, samt skrive en masse boilerplate kode for at forbinde frontend og backend.


På den anden side kan rene Python-biblioteker som Dash og Streamlit være gode til små projekter, men de er begrænset til en specifik use case og har ikke funktionerne og ydeevnen til at bygge en fuld webapp. Efterhånden som din app vokser i funktioner og kompleksitet, kan du finde på at ramme rammens grænser, hvorefter du enten skal begrænse din idé, så den passer til rammeværket, eller skrotte dit projekt og genopbygge det ved hjælp af en "rigtig webramme".


Vi ønsker at bygge bro over dette hul ved at skabe en ramme, der er nem og intuitiv at komme i gang med, samtidig med at den forbliver fleksibel og kraftfuld til at understøtte enhver app.

Refleks mål

  • Pure Python : Brug ét sprog til alt.
  • Let at komme i gang : Byg nemt dine ideer uden at have brug for erfaring med webudvikling.
  • Fuld fleksibilitet : Webapps skal matche tilpasningsmulighederne og ydeevnen af traditionelle webrammer.
  • Batterier inkluderet : Håndter den fulde stak fra frontend til backend til implementering.


Lad os nu dykke ned i, hvordan vi byggede Reflex for at nå disse mål.

Refleksarkitekturen

Full-stack webapps består af en frontend og en backend. Frontenden er brugergrænsefladen, og serveres som en webside, der kører på brugerens browser. Backend'en håndterer logikken og tilstandsstyringen (såsom databaser og API'er) og køres på en server. I traditionel webudvikling er disse normalt to separate apps, og de er ofte skrevet i forskellige rammer eller sprog. For eksempel kan du kombinere en Flask-backend med en React-frontend. Med denne tilgang skal du vedligeholde to separate apps og ende med at skrive en masse boilerplate-kode for at forbinde frontend og backend.


Vi ønsker at forenkle denne proces i Reflex ved at definere både frontend og backend i en enkelt kodebase, mens vi bruger Python til alt. Udviklere bør kun bekymre sig om deres apps logik og ikke om implementeringsdetaljerne på lavt niveau.




Frontend

Vi ønsker, at Reflex-apps skal se ud og føles som en traditionel web-app for slutbrugeren, mens de stadig er nemme at bygge og vedligeholde for udvikleren. For at gøre dette byggede vi oven på modne og populære webteknologier.


Når du reflex run din app, kompilerer Reflex frontenden ned til en enkeltsidet Next.js -app og serverer den på en port (som standard 3000 ), som du kan få adgang til i din browser.


Frontendens opgave er at afspejle appens tilstand og sende hændelser til backend, når brugeren interagerer med brugergrænsefladen. Der køres ingen egentlig logik på frontend.

Komponenter

Reflex frontends er bygget ved hjælp af komponenter, der kan sammensættes for at skabe komplekse brugergrænseflader. I stedet for at bruge et skabelonsprog, der blander HTML og Python, bruger vi bare Python-funktioner til at definere brugergrænsefladen.


Under hætten samles komponenter ned til React-komponenter. Mange af vores kernekomponenter er baseret på Radix , et populært React-komponentbibliotek. Vi har også mange andre komponenter til graftegning, datatabeller og meget mere. Vi valgte React, fordi det er et populært bibliotek med et enormt økosystem. Vores mål er ikke at genskabe webøkosystemet, men at gøre det tilgængeligt for Python-udviklere.


Dette lader også vores brugere medbringe deres egne komponenter, hvis vi ikke har en komponent, de har brug for. Brugere kan indpakke deres egne React-komponenter og derefter udgive dem, så andre kan bruge dem. Over tid vil vi bygge vores tredjeparts komponentøkosystem ud, så brugerne nemt kan finde og bruge komponenter, som andre har bygget.

Styling

Vi ønskede at sikre os, at Reflex-apps ser godt ud, mens de stadig giver udviklere fuld kontrol over udseendet af deres app.


Vi har et kernetemasystem, der lader dig indstille stylingmuligheder på højt niveau, såsom mørk tilstand og accentfarve i hele din app for at give den et ensartet udseende og følelse.


Ud over dette kan Reflex-komponenter styles ved hjælp af CSS's fulde kraft. Vi udnytter Emotion- biblioteket til at tillade "CSS-in-Python"-styling, så du kan overføre enhver CSS-rekvisit som et nøgleordsargument til en komponent. Dette inkluderer responsive rekvisitter ved at sende en liste over værdier.

Backend

I Reflex kompilerer kun frontenden til Javascript og kører på brugerens browser, mens al tilstand og logik forbliver i Python og køres på serveren. Når du reflex run , starter vi en FastAPI-server (som standard på port 8000 ), som frontenden opretter forbindelse til via en websocket.


Al tilstand og logik er defineret inden for en State . Staten består af vars og hændelseshandlere . Vars er alle værdier i din app, der kan ændre sig over tid. De er defineret som klasseattributter på din State klasse og kan være en hvilken som helst Python-type, der kan serialiseres til JSON.


Hændelseshandlere er metoder i din State klasse, der kaldes, når brugeren interagerer med brugergrænsefladen. De er den eneste måde, hvorpå vi kan ændre vars i Reflex, og kan kaldes som svar på brugerhandlinger, såsom at klikke på en knap eller skrive i en tekstboks.

Da hændelseshandlere køres på backend, kan du bruge et hvilket som helst Python-bibliotek i dem.


Begivenhedsbehandling

Normalt, når du skriver webapps, skal du skrive en masse boilerplate-kode for at forbinde frontend og backend. Med Reflex behøver du ikke bekymre dig om det – vi klarer kommunikationen mellem frontend og backend for dig. Udviklere skal bare skrive deres hændelseshåndteringslogik, og når vars er opdateret, opdateres brugergrænsefladen automatisk.

Hændelsesudløsere

Brugeren kan interagere med brugergrænsefladen på mange måder, såsom at klikke på en knap, skrive i en tekstboks eller holde musen over et element. I Reflex kalder vi disse hændelsestriggere .

Begivenhedskø

På frontenden opretholder vi en begivenhedskø med alle afventende begivenheder. En hændelse består af tre store datastykker:

  • klienttoken : Hver klient (browserfane) har et unikt token til at identificere det. Dette lader backend vide, hvilken tilstand der skal opdateres.
  • hændelseshandler : Hændelseshandleren, der skal køre på staten.
  • arguments : Argumenterne, der skal sendes til hændelseshandleren.


Når en hændelse udløses, tilføjes den til køen. Vi har et processing for at sikre, at kun én begivenhed behandles ad gangen. Dette sikrer, at tilstanden altid er konsistent, og at der ikke er nogen løbsforhold med to hændelseshandlere, der ændrer tilstanden på samme tid. Der er undtagelser til dette, såsom baggrundsbegivenheder, som giver dig mulighed for at køre begivenheder i baggrunden uden at blokere brugergrænsefladen.


Når begivenheden er klar til at blive behandlet, sendes den til backend via en WebSocket-forbindelse.

Statsleder

Når begivenheden er modtaget, behandles den på backend. Reflex bruger en tilstandsadministrator , som vedligeholder en kortlægning mellem klienttokens og deres tilstand. Som standard er tilstandsadministratoren kun en ordbog i hukommelsen, men den kan udvides til at bruge en database eller cache. I produktionen bruger vi Redis som vores statschef.

Begivenhedshåndtering

Når vi har brugerens tilstand, er næste trin at køre hændelseshandleren med argumenterne.

Statsopdateringer

Hver gang en hændelseshandler vender tilbage (eller giver efter), gemmer vi tilstanden i tilstandsadministratoren og sender tilstandsopdateringerne til frontenden for at opdatere brugergrænsefladen. For at opretholde ydeevnen, efterhånden som din tilstand vokser, holder Reflex internt styr på vars, der blev opdateret under hændelseshåndteringen ( dirty vars ).


Når hændelseshandleren er færdig med at behandle, finder vi alle de beskidte vars og opretter en tilstandsopdatering til at sende til frontend.

Vi gemmer den nye tilstand i vores state manager, og sender derefter tilstandsopdateringen til frontend. Frontenden opdaterer derefter brugergrænsefladen for at afspejle den nye tilstand.

Konklusion

Jeg håber dette giver et godt overblik over hvordan Reflex virker under hætten. Vi vil have flere indlæg, der kommer ud for at dele, hvordan vi gjorde Reflex skalerbar og effektiv gennem funktioner såsom state sharding og compiler-optimeringer.