- 本筆演講內容在此連結 https://hackmd.io/dk91-0LyTqe0MaqA8TeoKg - Miro 報到的連結 https://miro.com/app/board/uXjVM2ue-oE=/?share_link_id=483117069515 - 另設有共同編輯區,大家可點此進去編輯打字 https://hackmd.io/@milochen0418/Hy9Byz3Fn - 以前分享過Pynecone (Reflex) 的[雜記](https://hackmd.io/@milochen0418/pynecone-post-python-taiwan) ------- 本篇架構 ------- [TOC] # Reflex 的開發 可以使用 VSCode+conda, VSCode+poetry 等方式來開發,或是初次使用的話可以考慮 [Pynecone (Reflex) Colab-IDE](https://github.com/milochen0418/pynecone-colab-ide) [Pynecone](https://pynecone.app/) 改名至 [Reflex](https://reflex.dev/) ## What is pynecone(Reflex) Pinecone -> Pynecone 松果 -> Reflex pure pyhon 寫 全端網站 (後端、前端) - ReactJS PyQt flask + jQuery MySQL , PostgreSQL ReactJS .NET framework 前端 React 2023 Q1 top 1 最熱的開源新創項目 YC seed https://github.com/pynecone-io/pynecone ## Reflex 架構簡介 很簡化的來說,就是透過開發團隊的努力,將FastAPI + React 的集成,最終包出一個簡易好用的WebApp框架給你用, 希望讓人們可以用純python 來發展WebApp ### 後端的部份 簡單來說就是 FastAPI ,但是實戰app開發中,它框架已經作了簡化,因此你不用處理到中間複雜的溝通過程。 ### 前端的部份 前端使用 ReactJS (或是說NextJS) 在作畫面調整的部份部份,支持 [TailwindCSS](https://tailwindcss.com/), [ChakraUI](https://chakra-ui.com/),而且另一個特好用的優點,你可以自己使用其它React元件 來擴充。Reflex願景是希望可以盡可能將React的優點拿來使用。 目前,Netflix、Uber、星巴克或 Twitch 使用了NextJS NextJS是基於React的框架,差別在於 ReactJS 是在 browser 上直接作了渲染,而 Next.js將渲染方面的工作移到了服務器上,因此客戶端不需要處理這些信息。這提高了性能和SEO,因為服務器預先渲染頁面,然後將最終的HTML發送給客戶端,從而使JS最小化,也就是說加載的代碼更少。不僅用戶可以期待一個更快的網站,而且爬蟲也可以更容易地看到你的網站,並對其進行相應的索引。 Pynecone的渲染到底是 NextJS的哪種呢? NextJS它支持三種渲染方式包括 客戶端渲染 BSR (Broswer Side Render), 靜態頁面生成 SSG (Static Site Generation), 服務端渲染 SSR (Server Side Render) 而 [pynecone 使用 SSG](https://news.ycombinator.com/item?id=35137402) 靠 [bun-runtime](https://bun.sh/) 來作,因為渲染效能比 NodeJS 快約三倍以上。 [其餘渲染知識請見此](https://zhuanlan.zhihu.com/p/341229054) # 實戰開發 ## 申請 key 的部份 ### ngrok auth token ### openai key ## 開始開發 讓我們來寫寫看一個 DALL-E ### 建立初始專案 ```bash= mkdir mydalle cd mydalle reflex init reflex run ``` ### 新增requirments.txt (Optional) ```txt= reflex==0.2.0 openai ``` ### 簡化成 HelloWorld ```python= """Welcome to Reflex! This file outlines the steps to create a basic app.""" import reflex as rx import openai import os class State(rx.State): """The app state.""" pass def index() -> rx.Component: return rx.center( rx.heading("Hello World"), ) # Add state and page to the app. app = rx.App(state=State) app.add_page(index, title="MyDALL-E") app.compile() ``` ### Layout ```python= import reflex as rx import openai import os openai.api_key = os.environ.get("OPENAI_KEY","YOUR_OPENAI_KEY") class State(rx.State): """The app state.""" image_url = "" image_processing = False image_made = False def get_dalle_result(self, form_data: dict[str, str]): pass pass def index() -> rx.Component: return rx.center( rx.vstack( rx.heading("DALL-E", font_size="1.5em"), rx.form( rx.input(id="prompt_text", placeholder="Enter a prompt.."), rx.button( "Generate Image", type_="submit", width="100%", ), on_submit=State.get_dalle_result, ), rx.divider(), rx.cond( State.image_processing, rx.circular_progress(is_indeterminate=True), rx.cond( State.image_made, rx.image( src=State.image_url, height="25em", width="25em", ), ), ), bg="white", padding="2em", shadow="lg", border_radius="lg", ), width="100%", height="100vh", background="radial-gradient(circle at 22% 11%,rgba(62, 180, 137,.20),hsla(0,0%,100%,0) 19%),radial-gradient(circle at 82% 25%,rgba(33,150,243,.18),hsla(0,0%,100%,0) 35%),radial-gradient(circle at 25% 61%,rgba(250, 128, 114, .28),hsla(0,0%,100%,0) 55%)", ) # Add state and page to the app. app = rx.App(state=State) app.add_page(index, title="MyDALL-E App") app.compile() ``` ### 完成 ```python= import reflex as rx import openai import os openai.api_key = os.environ.get("OPENAI_KEY","YOUR_OPENAI_KEY") class State(rx.State): """The app state.""" image_url = "" image_processing = False image_made = False def get_dalle_result(self, form_data: dict[str, str]): prompt_text:str = form_data['prompt_text'] self.image_processing = True yield try: response = openai.Image.create(prompt=prompt_text, n=1, size="1024x1024") self.image_url = response["data"][0]["url"] self.image_processing = False self.image_made = True yield except: self.image_processing = False yield rx.window_alert("Error to call openAI API") pass def index() -> rx.Component: return rx.center( rx.vstack( rx.heading("DALL-E", font_size="1.5em"), rx.form( rx.input(id="prompt_text", placeholder="Enter a prompt.."), rx.button( "Generate Image", type_="submit", width="100%", ), on_submit=State.get_dalle_result, ), rx.divider(), ## rx.cond( Treu/False, True:作啥layout, False:作啥layout ) rx.cond( State.image_processing, rx.circular_progress(is_indeterminate=True), rx.cond( State.image_made, rx.image( src=State.image_url, height="25em", width="25em", ), ), ), bg="white", padding="2em", shadow="lg", border_radius="lg", ), width="100%", height="100vh", background="radial-gradient(circle at 22% 11%,rgba(62, 180, 137,.20),hsla(0,0%,100%,0) 19%),radial-gradient(circle at 82% 25%,rgba(33,150,243,.18),hsla(0,0%,100%,0) 35%),radial-gradient(circle at 25% 61%,rgba(250, 128, 114, .28),hsla(0,0%,100%,0) 55%)", ) # Add state and page to the app. app = rx.App(state=State) app.add_page(index, title="MyDALL-E App") app.compile() ``` # 聽眾 碰過 Django, flask, FastAPI, 想評估 reflex