实践reflex:项目架构解析

reflex 是一个使用纯Python构建全栈web应用的库,但是需要使用node,所以你懂的。

reflex安装:实践reflex:一个使用纯Python构建全栈web应用的库-CSDN博客

创建hello项目

(base) sky@ub:~$ mkdir hello
(base) sky@ub:~$ cd hello/
(base) sky@ub:~/hello$ reflex init
─────────────────────── Initializing hello ────────────────────────
[06:42:32] Initializing the web directory.           console.py:104
Warning: Failed to fetch templates. Falling back to default 
template.
[06:42:50] Initializing the app directory.           console.py:104
Success: Initialized hello

项目路径结构:

hello
├── .web
├── assets
├── hello
│   ├── __init__.py
│   └── hello.py
└── rxconfig.py

其中.web是隐藏目录,需要ls -la 才能看到。

.web

这是编译的Javascript文件的存储位置。您永远不需要触摸此目录,但它对调试很有用。

每个Reflex页面都将编译为.web/pages目录中相应的.js文件。

assets资产

目录是您可以存储任何您想要公开可用的静态资产的地方。这包括图像、字体和其他文件。

例如,如果您将图像保存到assets/image.png,您可以从应用程序中显示它,如下所示:

rx.image(src="image.png")

主目录hello

每个项目的主目录,都跟项目名字一样。

初始化您的项目会创建一个与应用程序同名的目录。您将在这里编写应用程序的逻辑。

Reflex在hello/hello.py文件中生成一个默认应用程序。您可以修改此文件来自定义您的应用程序。

配置

rxconfig.py文件

应用程序模块

Reflex根据config中的app_name导入主应用程序模块,该模块必须将模块级全局命名app定义为rx.App的实例

主应用程序模块负责导入构成应用程序的所有其他模块,并定义app = rx.App()

**包含页面、状态和模型的所有其他模块必须由主应用程序模块或软件包导入,**以便Reflex将它们包含在编译的输出中。

更详细的说明

页面包:example_big_app/pages

所有复杂的应用程序都将有多个页面,因此建议创建example_big_app/pages作为软件包。

  1. 此软件包应在应用程序中每页包含一个模块。
  2. 如果特定页面取决于状态,则该子状态应在与页面相同的模块中定义。
  3. 返回页面的函数应该用rx.page()进行装饰,以便将其作为路由添加到应用程序中。
python 复制代码
import reflex as rx

from ..state import AuthState


class LoginState(AuthState):
    def handle_submit(self, form_data):
        self.logged_in = authenticate(
            form_data["username"], form_data["password"]
        )


def login_field(name: str, **input_props):
    return rx.hstack(
        rx.text(name.capitalize()),
        rx.input(name=name, **input_props),
        width="100%",
        justify="between",
    )


@rx.page(route="/login")
def login():
    return rx.card(
        rx.form(
            rx.vstack(
                login_field("username"),
                login_field("password", type="password"),
                rx.button("Login"),
                width="100%",
                justify="center",
            ),
            on_submit=LoginState.handle_submit,
        ),
    )

模板:example_big_app/template.py

大多数应用程序在页面之间保持一致的布局和结构。在单独的模块中定义此通用结构有助于在构建单个页面时轻松共享和重复使用。

最佳实践

  1. 将常见的前端用户界面元素分解为返回组件的函数。

  2. 如果一个函数接受返回组件的函数,它可以用作装饰器,如下所示。

    python 复制代码
    from typing import Callable
    
    import reflex as rx
    
    from .components.menu import menu
    from .components.navbar import navbar
    
    
    def template(
        page: Callable[[], rx.Component]
    ) -> rx.Component:
        return rx.vstack(
            navbar(),
            rx.hstack(
                menu(),
                rx.container(page()),
            ),
            width="100%",
        )

状态管理

进入其它状态

在以前的版本中,如果应用程序想要将设置与用于修改的页面或组件一起存储在SettingsState中,则需要访问这些设置的任何其他具有事件处理程序的状态必须从SettingsState继承,即使其他状态大多是正交的。另一种状态现在也必须始终加载设置,即使对于不需要访问它们的事件处理程序也是如此。

更好的策略是仅从需要访问子状态的事件处理程序中按需加载所需状态。

设置组件

python 复制代码
example_big_app/components/settings.py
python 复制代码
import reflex as rx


class SettingsState(rx.State):
    refresh_interval: int = 15
    auto_update: bool = True
    prefer_plain_text: bool = True
    posts_per_page: int = 20


def settings_dialog():
    return rx.dialog(...)

帖子页面:example_big_app/pages/posts.py

此页面加载SettingsState,以确定每页显示多少个帖子以及刷新频率。

python 复制代码
import reflex as rx

from ..models import Post
from ..template import template
from ..components.settings import SettingsState


class PostsState(rx.State):
    refresh_tick: int
    page: int
    posts: list[Post]

    async def on_load(self):
        settings = await self.get_state(SettingsState)
        if settings.auto_update:
            self.refresh_tick = (
                settings.refresh_interval * 1000
            )
        else:
            self.refresh_tick = 0

    async def tick(self, _):
        settings = await self.get_state(SettingsState)
        with rx.session() as session:
            q = (
                Post.select()
                .offset(self.page * settings.posts_per_page)
                .limit(settings.posts_per_page)
            )
            self.posts = q.all()

    def go_to_previous(self):
        if self.page > 0:
            self.page = self.page - 1

    def go_to_next(self):
        if self.posts:
            self.page = self.page + 1


@rx.page(route="/posts", on_load=PostsState.on_load)
@template
def posts():
    return rx.vstack(
        rx.foreach(PostsState.posts, post_view),
        rx.hstack(
            rx.button(
                "< Prev", on_click=PostsState.go_to_previous
            ),
            rx.button(
                "Next >", on_click=PostsState.go_to_next
            ),
            justify="between",
        ),
        rx.moment(
            interval=PostsState.refresh_tick,
            on_change=PostsState.tick,
            display="none",
        ),
        width="100%",
    )

共同状态:example_big_app/state.py

由多个页面或组件共享的通用状态和子状态应在单独的模块中实现,以避免循环导入。此模块不应在应用程序中导入其他模块。

组件的可重复使用性

在Reflex中重复使用组件的主要机制是定义一个返回组件的函数,然后在需要该功能的地方简单地调用它。

组件函数通常不应将任何状态类作为参数,而是倾向于导入所需的状态并直接访问类上的变量。

记忆功能以提高性能

在大型应用程序中,如果一个组件有许多子组件或在大量位置使用,它可以提高编译和运行时性能,使用@lru_cache装饰器对函数进行内啮。

要回忆foo组件以避免多次重新创建,只需将@lru_cache添加到函数定义中,该组件将仅每组唯一参数创建一次。

python 复制代码
from functools import lru_cache

import reflex as rx


class State(rx.State):
    v: str = "foo"


@lru_cache
def foo():
    return rx.text(State.v)


def index():
    return rx.flex(
        rx.button(
            "Change",
            on_click=State.set_v(
                rx.cond(State.v != "bar", "bar", "foo")
            ),
        ),
        *[foo() for _ in range(100)],
        direction="row",
        wrap="wrap",
    )

示例_大_应用程序/组件

此软件包包含应用程序的可重复使用部分,例如页眉、页脚和菜单。如果特定组件需要状态,则子状态可以在同一区域模块中定义。组件模块中定义的任何子状态应仅包含与该单个组件相关的字段和事件处理程序。

外部组件

Reflex 0.4.3引入了对reflex componentCLI命令的支持,这使得它可以轻松捆绑通用功能,作为独立的Python软件包在PyPI上发布,可以在任何Reflex应用程序中安装和使用。

在包装npm组件或其他自包含的功能片段时,将此复杂性移出应用程序本身可能会有所帮助,以便于在其他应用程序中更易于维护和重复使用。

数据库模型:example_big_app/models.py

建议在单个文件中实现所有数据库模型,以便更容易定义关系和理解整个模式。

然而,如果模式非常大,则有一个models包,在自己的模块中定义了单个模型,这可能是有意义的。

无论如何,单独定义模型允许任何页面或组件导入和使用它们,而无需循环导入。

顶级套餐:example_big_app/init.py

这是一个导入所有状态、模型和页面的好地方,这些状态、模型和页面应该是应用程序的一部分。通常,组件和助手不需要导入,因为它们将由使用它们的页面导入(或者它们将未使用)。

python 复制代码
from . import state, models
from .pages import (
    index,
    login,
    post,
    product,
    profile,
    schedule,
)

__all__ = [
    "state",
    "models",
    "index",
    "login",
    "post",
    "product",
    "profile",
    "schedule",
]
相关推荐
厨 神1 分钟前
vmware中的ubuntu系统扩容分区
linux·运维·ubuntu
Karoku0666 分钟前
【网站架构部署与优化】web服务与http协议
linux·运维·服务器·数据库·http·架构
geek_Chen018 分钟前
虚拟机共享文件夹开启后mnt/hgfs/下无sharefiles? --已解决
linux·运维·服务器
(⊙o⊙)~哦39 分钟前
linux 解压缩
linux·运维·服务器
牧小七2 小时前
Linux命令---查看端口是否被占用
linux
最新小梦2 小时前
Docker日志管理
运维·docker·容器
鸡鸭扣3 小时前
虚拟机:3、(待更)WSL2安装Ubuntu系统+实现GPU直通
linux·运维·ubuntu
友友马3 小时前
『 Linux 』HTTP(一)
linux·运维·服务器·网络·c++·tcp/ip·http
千禧年@3 小时前
微服务以及注册中心
java·运维·微服务
重生之我在20年代敲代码3 小时前
HTML讲解(一)body部分
服务器·前端·html