Веб-розробка є одним із найпопулярніших випадків використання програмування. Python є однією з найпопулярніших мов програмування у світі. Тож чому ми не можемо створювати веб-програми на Python?
Створення інтерфейсу має бути простим, але навіть у вашій команді є чудові інженери, накладні витрати на вивчення нової мови та інструментів були величезною перешкодою. Часто створення інтерфейсу може бути складнішим, ніж фактична робота! Ми створили Reflex, веб-платформу Python з відкритим кодом, щоб вирішити саме цю проблему.
Під капотом додатки Reflex компілюються до інтерфейсної програми React і бекендової програми FastAPI . Лише інтерфейс користувача скомпільований у Javascript; уся логіка програми та керування станом залишаються в Python і виконуються на сервері. Reflex використовує WebSockets для надсилання подій із інтерфейсу до сервера та надсилання оновлень стану з сервера до інтерфейсу.
Уже було кілька способів створювати програми на Python, але жоден із них не відповідав нашим потребам.
З одного боку, є такі фреймворки, як Django та Flask , які чудово підходять для створення веб-додатків продуктивного рівня. Але вони обробляють лише бекенд – вам все одно потрібно використовувати JavaScript і фреймворк інтерфейсу, а також написати багато шаблонного коду для з’єднання інтерфейсу та бекенду.
З іншого боку, чисті бібліотеки Python, такі як Dash і Streamlit, можуть бути чудовими для невеликих проектів, але вони обмежені конкретним сценарієм використання та не мають функцій і продуктивності для створення повноцінної веб-програми. У міру збільшення функцій і складності вашої програми ви можете виявити, що досягаєте меж фреймворку, і тоді вам доведеться або обмежити свою ідею, щоб відповідати фреймворку, або відмовитися від проекту та перебудувати його за допомогою «справжнього веб-фреймворку».
Ми хочемо подолати цю прогалину, створивши структуру, яка є легкою та інтуїтивно зрозумілою для початку, залишаючись при цьому гнучкою та потужною для підтримки будь-якої програми.
Тепер розглянемо, як ми створили Reflex для досягнення цих цілей.
Повноцінні веб-програми складаються з інтерфейсу та серверної частини. Інтерфейс — це інтерфейс користувача, який подається як веб-сторінка, яка запускається в браузері користувача. Сервер керує логікою та керуванням станом (наприклад, базами даних і API) і виконується на сервері. У традиційній веб-розробці це, як правило, дві окремі програми, які часто написані на різних платформах або мовах. Наприклад, ви можете поєднати бекенд Flask із інтерфейсом React. За такого підходу вам доведеться підтримувати дві окремі програми, і в кінцевому підсумку ви повинні написати багато шаблонного коду для з’єднання інтерфейсу та сервера.
Ми хочемо спростити цей процес у Reflex, визначивши інтерфейс, і серверну частину в одній кодовій базі, використовуючи Python для всього. Розробники повинні турбуватися лише про логіку своєї програми, а не про деталі низькорівневої реалізації.
Ми хочемо, щоб програми Reflex виглядали та відчували себе як традиційні веб-програми для кінцевого користувача, але при цьому їх легко створювати та підтримувати для розробників. Для цього ми створили сучасні та популярні веб-технології.
Коли ви reflex run
, Reflex компілює інтерфейс до односторінкової програми Next.js і обслуговує її на порту (за замовчуванням 3000
), до якого ви можете отримати доступ у своєму браузері.
Завдання інтерфейсу — відображати стан програми та надсилати події на сервер, коли користувач взаємодіє з інтерфейсом користувача. Жодна фактична логіка не виконується на інтерфейсі.
Інтерфейси Reflex побудовані з використанням компонентів, які можна комбінувати разом для створення складних інтерфейсів користувача. Замість використання мови шаблонів, яка поєднує HTML і Python, ми просто використовуємо функції Python для визначення інтерфейсу користувача.
Під капотом компоненти компілюються до компонентів React. Багато наших основних компонентів базуються на Radix , популярній бібліотеці компонентів React. У нас також є багато інших компонентів для побудови графіків, таблиць даних тощо. Ми обрали React, тому що це популярна бібліотека з величезною екосистемою. Наша мета — не відтворити веб-екосистему, а зробити її доступною для розробників Python.
Це також дозволяє нашим користувачам приносити власні компоненти, якщо у нас немає потрібного їм компонента. Користувачі можуть обернути власні компоненти React, а потім опублікувати їх для використання іншими. З часом ми створимо нашу екосистему сторонніх компонентів, щоб користувачі могли легко знаходити та використовувати компоненти, створені іншими.
Ми хотіли переконатися, що додатки Reflex добре виглядають із коробки, водночас даючи розробникам повний контроль над зовнішнім виглядом своїх додатків.
Ми маємо основну систему оформлення тем, яка дає змогу встановлювати параметри стилю високого рівня, як-от темний режим і акцентний колір у вашій програмі, щоб надати їй єдиного вигляду та відчуття.
Окрім цього, компоненти Reflex можна стилізувати за допомогою повної потужності CSS. Ми використовуємо бібліотеку Emotion , щоб дозволити стилі "CSS-in-Python", тож ви можете передати компоненту будь-який атрибут CSS як аргумент ключового слова. Це включає адаптивні властивості шляхом передачі списку значень.
У Reflex лише інтерфейс компілюється в Javascript і запускається в браузері користувача, тоді як увесь стан і логіка залишаються в Python і запускаються на сервері. Коли ви reflex run
, ми запускаємо сервер FastAPI (за замовчуванням на порту 8000
), до якого інтерфейс підключається через веб-сокет.
Усі стани та логіка визначені в класі State
. Стан складається зі змінних і обробників подій . Змінні – це будь-які значення у вашій програмі, які можуть змінюватися з часом. Вони визначаються як атрибути класу у вашому класі State
і можуть бути будь-яким типом Python, який можна серіалізувати в JSON.
Обробники подій — це методи у вашому класі State
, які викликаються, коли користувач взаємодіє з інтерфейсом користувача. Це єдиний спосіб, за допомогою якого ми можемо змінювати змінні в Reflex, і їх можна викликати у відповідь на дії користувача, такі як натискання кнопки або введення тексту в текстовому полі.
Оскільки обробники подій запускаються на сервері, у них можна використовувати будь-яку бібліотеку Python.
Зазвичай під час написання веб-додатків вам доводиться писати багато шаблонного коду, щоб з’єднати інтерфейс і серверну частину. З Reflex вам не доведеться про це турбуватися – ми обслуговуємо зв’язок між інтерфейсом і сервером за вас. Розробники просто повинні написати свою логіку обробки подій, і коли змінні оновляться, інтерфейс користувача оновиться автоматично.
Користувач може взаємодіяти з інтерфейсом користувача багатьма способами, наприклад натисканням кнопки, введенням тексту в текстовому полі або наведенням курсора на елемент. У Reflex ми називаємо ці тригери подій .
На інтерфейсі ми підтримуємо чергу подій усіх незавершених подій. Подія складається з трьох основних частин даних:
Коли подія запускається, вона додається до черги. У нас є прапор processing
, щоб переконатися, що одночасно обробляється лише одна подія. Це гарантує, що стан завжди є узгодженим і немає жодних умов змагання з двома обробниками подій, які змінюють стан одночасно. Існують винятки, наприклад фонові події, які дозволяють запускати події у фоновому режимі, не блокуючи інтерфейс користувача.
Коли подія готова до обробки, вона надсилається на сервер через з’єднання WebSocket.
Отримавши подію, вона обробляється на сервері. Reflex використовує менеджер стану , який підтримує відображення між маркерами клієнта та їхнім станом. За замовчуванням менеджер стану — це просто словник у пам’яті, але його можна розширити, щоб використовувати базу даних або кеш. У виробництві ми використовуємо Redis як менеджер стану.
Коли ми маємо стан користувача, наступним кроком є запуск обробника події з аргументами.
Кожного разу, коли обробник подій повертається (або поступається), ми зберігаємо стан у диспетчері станів і надсилаємо оновлення стану на інтерфейс для оновлення інтерфейсу користувача. Щоб підтримувати продуктивність у міру зростання вашого стану, Reflex внутрішньо відстежує змінні, оновлені під час обробника подій ( брудні змінні ).
Коли обробник подій завершує обробку, ми знаходимо всі брудні змінні та створюємо оновлення стану для надсилання на інтерфейс.
Ми зберігаємо новий стан у нашому диспетчері станів, а потім надсилаємо оновлення стану на інтерфейс. Потім інтерфейс оновлює інтерфейс, щоб відобразити новий стан.
Я сподіваюся, що це дає гарний огляд того, як Reflex працює під капотом. У нас буде більше дописів, щоб поділитися тим, як ми зробили Reflex масштабованим і продуктивним за допомогою таких функцій, як сегментування стану та оптимізація компілятора.