R

ReflexKR

Reflex 官方文档

用 Reflex 入门 Python 全栈 Web 开发

·

课程概览

1

Reflex 简介与开发环境

2

用组件绘制界面

3

State 与 Var:让数据驱动界面

4

事件与表单:添加客户

5

数据处理与图表

6

总结、调试与下一步

·

附录

用 Reflex 入门 Python 全栈 Web 开发

附录

A. 完整代码(tutorial_261019/tutorial_261019.py)

import dataclasses
from collections import Counter

import reflex as rx


@dataclasses.dataclass
class User:
    name: str
    email: str
    gender: str


class State(rx.State):
    users: list[User] = [
        User(name="Danilo Sousa", email="danilo@example.com", gender="Male"),
        User(name="Zahra Ambessa", email="zahra@example.com", gender="Female"),
    ]
    users_for_graph: list[dict] = []
    add_user_dialog_open: bool = False

    @rx.event
    def set_add_user_dialog_open(self, open_: bool):
        self.add_user_dialog_open = open_

    @rx.event
    def add_user(self, form_data: dict):
        self.users.append(User(**form_data))
        self.transform_data()
        self.add_user_dialog_open = False

    @rx.event
    def transform_data(self):
        gender_counts = Counter(user.gender for user in self.users)

        self.users_for_graph = [
            {"name": gender_group, "value": count} for gender_group, count in gender_counts.items()
        ]


def show_user(user: User):
    return rx.table.row(
        rx.table.cell(user.name),
        rx.table.cell(user.email),
        rx.table.cell(user.gender),
        style={"_hover": {"bg": rx.color("gray", 3)}},
        align="center",
    )


def add_customer_button() -> rx.Component:
    return rx.dialog.root(
        rx.dialog.trigger(
            rx.button(
                rx.icon("plus", size=18),
                rx.text("Add User", size="2"),
            ),
        ),
        rx.dialog.content(
            rx.dialog.title(
                "Add New User",
            ),
            rx.dialog.description(
                "Fill the form with the user's info",
            ),
            rx.form(
                rx.flex(
                    rx.input(placeholder="User Name", name="name", required=True),
                    rx.input(
                        placeholder="user@reflex.dev",
                        name="email",
                    ),
                    rx.select(
                        ["Male", "Female"],
                        placeholder="Select gender",
                        default_value="Male",
                        name="gender",
                        custom_attrs={"aria-label": "Gender"},
                    ),
                    rx.flex(
                        rx.dialog.close(
                            rx.button(
                                "Cancel",
                                type="button",
                                variant="soft",
                                color_scheme="gray",
                            ),
                        ),
                        rx.button("Submit", type="submit"),
                        spacing="3",
                        justify="end",
                    ),
                    direction="column",
                    spacing="4",
                ),
                on_submit=State.add_user,
                reset_on_submit=False,
            ),
            max_width="450px",
        ),
        open=State.add_user_dialog_open,
        on_open_change=State.set_add_user_dialog_open,
    )


def graph():
    return rx.recharts.bar_chart(
        rx.recharts.cartesian_grid(
            horizontal=False, stroke_dasharray="3 3", stroke=rx.color("slate", 4)
        ),
        rx.recharts.x_axis(
            type_="number",
            allow_decimals=False,
            axis_line=False,
            tick_line=False,
            domain=[0, "dataMax + 1"],
        ),
        rx.recharts.y_axis(
            type_="category",
            data_key="name",
            axis_line=False,
            tick_line=False,
            width=64,
        ),
        rx.recharts.bar(
            data_key="value",
            fill=rx.color("accent", 9),
            radius=[0, 6, 6, 0],
            bar_size=28,
        ),
        data=State.users_for_graph,
        layout="vertical",
        width="100%",
        height=160,
        margin={"top": 0, "right": 16, "bottom": 0, "left": 0},
    )


def index() -> rx.Component:
    return rx.box(
        rx.box(
            rx.box(
                rx.box(
                    rx.heading("Customers", size="6", as_="h3", margin="0"),
                    rx.badge(
                        State.users.length(),
                        variant="soft",
                        color_scheme="gray",
                        radius="full",
                    ),
                    display="flex",
                    align_items="center",
                    gap="12px",
                ),
                rx.text(
                    "Your customer directory and audience breakdown.",
                    size="2",
                    color=rx.color("slate", 11),
                    margin_top="6px",
                ),
            ),
            add_customer_button(),
            display="flex",
            align_items="center",
            justify_content="space-between",
            flex_wrap="wrap",
            gap="16px",
            padding="24px",
        ),
        rx.box(
            rx.table.root(
                rx.table.header(
                    rx.table.row(
                        rx.table.column_header_cell("Name"),
                        rx.table.column_header_cell("Email"),
                        rx.table.column_header_cell("Gender"),
                    )
                ),
                rx.table.body(rx.foreach(State.users, show_user)),
                variant="ghost",
                size="3",
                width="100%",
            ),
            overflow_x="auto",
            width="100%",
            border_top=f"1px solid {rx.color('slate', 4)}",
            border_bottom=f"1px solid {rx.color('slate', 4)}",
            padding_x="12px",
        ),
        rx.box(
            rx.text("Audience breakdown", size="3", weight="bold"),
            rx.text(
                "Customers by gender",
                size="2",
                color=rx.color("slate", 11),
                margin_top="4px",
                margin_bottom="20px",
            ),
            graph(),
            padding="24px",
        ),
        width="100%",
        min_width="0",
        max_width="64em",
        margin_x="auto",
        margin_y="24px",
        border=f"1px solid {rx.color('slate', 5)}",
        border_radius="16px",
        background=rx.color("slate", 1),
        overflow="hidden",
        box_shadow="0 4px 24px rgba(0, 0, 0, 0.03)",
        style={
            "& *": {"box_sizing": "border-box"},
            "& p, & h3": {"text_align": "left"},
        },
    )


app = rx.App()
app.add_page(
    index,
    title="Customer Data App",
    description="A simple app to manage customer data.",
    on_load=State.transform_data,
)

B. 术语表

术语含义
全栈同时涉及界面(前端)和服务器(后端)的开发
组件构成界面的片段。在 Reflex 中就是一次 rx.xxx(...) 函数调用
props传给组件的配置值,以关键字参数的形式传入
State存放应用数据和事件处理器的类,在服务器上按会话分别存在
Var在界面代码中指向 State 变量的表达式,其值在浏览器中填充
computed var用 @rx.var 声明、由其他状态计算得出的变量
事件处理器加上 @rx.event 的 State 方法,响应用户操作并在服务器上执行
受控组件由 State 决定界面元素的值或状态的方式(open=、value=)
编译时应用启动时,界面函数被转换为 React 代码的阶段
运行时应用运行、用户进行操作的阶段,事件处理器在此时执行
WebSocket浏览器与服务器保持连接、双向收发消息的通信方式
热重载保存代码后,无需停止服务器即可自动重新编译并生效的功能

C. 拆分成博客文章

把每一课写成一篇文章,就是一个 6 篇的系列。

篇标题示例对应课次
1只用 Python 做 Web 应用:Reflex 入门(附 Windows 报错解决)第1课
2不写 HTML 也能画界面:Reflex 组件与样式第2课
3为什么在 Reflex 里不能用 len():State 与 Var第3课
4用表单和对话框添加数据第4课
5用 Python 汇总数据并绘制图表第5课
6Reflex 初学者必踩的 8 个错误第6课 6-2
  • 讲师笔记 引用块可以改写成正文说明,或者换成“提示”框。
  • 在每篇文章末尾附上截至当时的完整代码和运行截图,读者就能从中间开始跟着做。
  • 图表截图要等动画结束后(2 秒以上)再截。
  • 在文章开头注明“基于 Reflex 0.10.0”。Reflex 的 API 变化很快,标明版本能大大减少读者踩坑。

D. 视频课程制作指南

  • 屏幕布局:左边 VS Code,右边浏览器,下面终端。让“保存 → 热重载 → 界面变化”的那一刻都出现在同一个画面里。
  • 字号:编辑器字体 18pt 以上,浏览器缩放 125% 以上。
  • 代码一点点敲:不要直接粘贴完整代码,按照每一课代码块的顺序边敲边保存,每次保存都展示界面效果。
  • 故意展示错误:第3课的 len() 错误、第4课 Cancel 按钮漏写 type、关不掉的对话框,都很值得故意复现一遍。
  • 章节标记:在视频简介里按课次和小节编号(1-1、1-2 …)加上时间戳,就能和本教程一一对应。
  • 控制时长:每个小节标注的(分钟)是按现场授课估算的,剪辑后的录播视频通常会缩短到一半左右。

← 上一课

第6课 · 总结、调试与下一步

订阅新课时和新课程通知

本课程新增课时或推出后续课程时,我们会发邮件通知你。

收集项目:电子邮箱 · 用途:本课程的新课时和上线通知 · 保存期限:直到你要求删除。不会用于其他用途,也不会提供给他人。回复任意通知邮件,我们会立即删除。

问答

关于本课有任何疑问都可以在这里留言。所有语言版本页面的提问会汇集在一起。

以纯文本显示,不支持链接和格式。

R

ReflexKR

动手学 Python 和 Reflex。所有示例均已在 Reflex 0.10.0 上实际运行验证。

ReflexKR 免费,没有横幅广告。如果对你有帮助,欢迎请我们喝杯咖啡。 ☕ 请我们喝杯咖啡

联盟链接与赞助说明
한국어English日本語简体中文EspañolDeutsch

© 2026 ReflexKR · Since 2026 · CC BY-NC 4.0

Built with Reflex