关于 understanding-streamlit-architecture
帮助开发者理解 Streamlit 架构并快速定位实现细节的文档技能,涵盖 Python 后端、React 前端及其通信协议。
主要功能
- 01梳理后端与前端组件
- 02说明 WebSocket 与 Protobuf 通信
- 03解释脚本重运行和会话状态
- 04提供功能实现导航
适用场景
- 01熟悉 Streamlit 的执行模型
- 02定位界面元素的前后端代码
- 03开发 Streamlit 新功能
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ streamlit/ streamlit --skill understanding-streamlit-architecture安装后在 Claude Code 中输入/understanding-streamlit-architecture确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
references/backend.mdcommunication.mdelement-identity.mdfrontend.mdlayout.md详细介绍
理解 Streamlit 架构
Streamlit 是一个客户端-服务应用,使用 Protocol Buffers 进行双向 WebSocket 通信。
将此文件用作快速的心智模型和导航索引;实现细节请使用 references/backend.md、references/frontend.md 和 references/communication.md。
概念术语表
| 概念 | 描述 | 关键文件 |
|---|---|---|
| 命令 | 在 Streamlit 的公共 API(st.* 命名空间)中暴露的任何函数。命令可以创建元素/组件、控制执行流程或配置应用行为。 | lib/streamlit/delta_generator.py、lib/streamlit/elements/、lib/streamlit/commands/ |
| 元素 | Streamlit 中所有 UI 组件的统称:组件、容器和显示元素。在 Element.proto 中表示为约 50+ 种类型的 oneof 联合。 | proto/streamlit/proto/Element.proto、lib/streamlit/elements/ |
| 组件 | 交互式元素(按钮、滑块、text_input),通常在用户交互时触发重运行。值可通过返回值或 st.session_state 访问。支持 on_change="ignore" 的组件在浏览器中更新而不会重运行,直到其他内容触发。某些元素会条件性地变为组件(例如带有 on_select 的 dataframe/chart)。 | lib/streamlit/elements/widgets/、frontend/lib/src/components/widgets/ |
| 显示元素 | 非交互式元素(文本、markdown、图片、图表),渲染内容而本身不会触发重运行。 | lib/streamlit/elements/、frontend/lib/src/components/elements/ |
| 容器 | 在空间上对元素进行分组的布局块(侧边栏、列、展开器、标签页、表单)。在元素树中表示为 BlockNode。 | lib/streamlit/elements/layouts.py、Block.proto |
| DeltaGenerator | st 对象;将 UI 增量排入队列的 API 入口点。使用 mixin 模式组合所有 st.* 命令。 | lib/streamlit/delta_generator.py |
| 会话状态 | 每个会话的字典(st.session_state),在重运行之间持久保存数据。存储组件值和用户变量。 | lib/streamlit/runtime/state/session_state.py |
| 重运行 | 为当前页面重新执行用户脚本。由组件交互、st.rerun()、文件更改或 fragment 定时器触发。在保留会话状态的同时重建元素树。 | lib/streamlit/runtime/scriptrunner/script_runner.py、lib/streamlit/commands/execution_control.py |
| 表单 | 容器(st.form),批处理组件输入,将重运行推迟到表单提交时。 | lib/streamlit/elements/form.py、WidgetStateManager.ts |
| Fragment | 装饰器(@st.fragment),允许特定 UI 部分部分重运行而无需完整重新执行脚本。 | lib/streamlit/runtime/fragment.py |
| 缓存 | 装饰器(@st.cache_data、@st.cache_resource),对函数结果进行记忆化以避免冗余计算。 | lib/streamlit/runtime/caching/ |
| 页面 | 多页面应用系统,使用 st.navigation() 和 st.Page() 或从 pages/ 目录自动发现。 | lib/streamlit/navigation/、lib/streamlit/runtime/pages_manager.py |
| 配置 | 通过 .streamlit/config.toml 进行应用配置,控制服务器、客户端和主题设置。 | lib/streamlit/config.py、lib/streamlit/config_option.py |
| 主题 | 可自定义的 UI 主题(浅色/深色/自定义),在配置中或通过主题编辑器定义。 | lib/streamlit/runtime/theme_util.py、frontend/lib/src/theme/ |
| 密钥 | 通过 .streamlit/secrets.toml(本地)或平台设置(已部署)进行安全凭据存储。通过 st.secrets 访问。 | lib/streamlit/runtime/secrets.py |
| 连接 | 数据库/服务抽象(st.connection),内置缓存和密钥集成。 | lib/streamlit/connections/ |
| 自定义组件 | 使用 React/iframe 的用户构建扩展。v1(旧版):declare_component() API。v2(当前):具有改进状态管理的双向组件。 | frontend/component-lib/、frontend/component-v2-lib/、lib/streamlit/components/v1/、lib/streamlit/components/v2/ |
| 静态文件服务 | static/ 目录中的文件通过 /app/static/* 直接提供(当启用静态服务时)。 | lib/streamlit/web/server/server.py、lib/streamlit/web/server/starlette/starlette_routes.py |
| 应用测试 | 测试框架(AppTest),用于模拟用户交互和检查渲染输出。 | lib/streamlit/testing/ |
核心心智模型
flowchart TB
subgraph Backend["Backend (Python)"]
Script[User Script]
DG[DeltaGenerator]
Runtime[Runtime/AppSession]
end
subgraph Protocol["WebSocket + Protobuf"]
FM[ForwardMsg]
BM[BackMsg]
end
subgraph Frontend["Frontend (React)"]
App[App.tsx]
Tree[AppRoot Tree]
Renderer[ElementNodeRenderer]
WSM[WidgetStateManager]
end
Script --> DG
DG --> FM
FM --> App
App --> Tree
Tree --> Renderer
Renderer --> WSM
WSM -->|User interaction| BM
BM --> Runtime
Runtime -->|Rerun| Script
关键洞见:脚本执行由重跑驱动:大多数组件交互都会触发重跑(整个应用或片段范围)。状态通过 st.session_state 和缓存装饰器持久化。
执行模型
Streamlit 的执行模型与传统 Web 框架不同:
重跑触发条件:
- 组件交互:用户点击按钮、移动滑块等(带有
on_change="ignore"的组件更新时不会重跑) - 源代码更改:文件监视器检测到脚本修改
st.rerun():显式的程序化重跑- 片段定时器:
@st.fragment(run_every=...)周期性重跑
执行顺序的细微之处:
- 每次重跑时脚本都自上而下执行
- 回调优先:
on_change/on_click处理程序在主脚本主体之前运行 - 片段通常会隔离重跑:
@st.fragment内部的组件交互通常只重跑该片段 - 带键的片段重跑:从组件回调中调用
st.rerun("<key>")只会重跑指定的@st.fragment(key=...)片段 - 控制流异常:
st.stop()、st.rerun()、st.switch_page()会抛出异常以中止或重定向执行
会话隔离:
- 每个浏览器标签页 = 单独的
AppSession,拥有自己的SessionState - 刷新页面会创建新会话(除非在 TTL 内重新连接)
- 会话之间不共享状态(多用户状态请使用外部存储)
跨重跑持久化的内容(同一会话内):
st.session_state的值- 缓存函数结果(
@st.cache_data、@st.cache_resource) - 上传的文件
- 片段注册信息
每次重跑都会重置的内容:
- 脚本中的局部变量
- 组件的返回值(从
SessionState重新读取) - 元素树(从头重建,然后进行差异比较)
架构分层
后端(Python)
| 组件 | 文件 | 用途 |
|---|---|---|
| Runtime | lib/streamlit/runtime/runtime.py | 管理应用生命周期和会话的单例 |
| AppSession | lib/streamlit/runtime/app_session.py | 每个浏览器标签页:ScriptRunner + SessionState + ForwardMsgQueue |
| ScriptRunner | lib/streamlit/runtime/scriptrunner/script_runner.py | 在独立线程中执行用户脚本 |
| DeltaGenerator | lib/streamlit/delta_generator.py | 使用 mixin 模式的 API 入口点 |
| SessionState | lib/streamlit/runtime/state/session_state.py | 组件值和用户变量 |
| Elements | lib/streamlit/elements/ | st.* 命令的后端实现 |
后端深入解析:参见 references/backend.md
前端(TypeScript/React)
| 组件 | 文件 | 用途 |
|---|---|---|
| App | frontend/app/src/App.tsx | 中央协调器 |
| AppRoot | frontend/lib/src/render-tree/AppRoot.ts | 包含 4 个容器的不可变元素树 |
| ElementNodeRenderer | frontend/lib/src/components/core/Block/ElementNodeRenderer.tsx | 将 proto 映射到 React 组件 |
| WidgetStateManager | frontend/lib/src/WidgetStateManager.ts | 组件状态、表单、查询参数 |
| ConnectionManager | frontend/connection/src/ConnectionManager.ts | WebSocket 状态机 |
前端深入解析:参见 references/frontend.md
通信(Protobuf)
| Proto | 用途 |
|---|---|
ForwardMsg.proto | 服务器到客户端:增量、会话事件、导航 |
BackMsg.proto | 客户端到服务器:带有组件状态的重跑请求 |
Element.proto | oneof type 联合中约 50+ 种元素类型 |
WidgetStates.proto | 组件值:trigger_value、string_value、bool_value 等 |
位置:proto/streamlit/proto/
协议深入解析:参见 references/communication.md
核心概念(快速导览)
- 布局系统:元素的宽度/高度参数、flexbox 容器以及响应式尺寸。
- 深入阅读:references/layout.md
- 脚本重跑模型:组件交互 ->
BackMsg(ClientState)-> 后端更新SessionState-> 脚本重跑 ->ForwardMsg增量。- 深入阅读:
references/communication.md#widget-interaction-to-script-rerun
- 深入阅读:
- 增量路径系统:元素通过增量路径(例如
[0, 2, 3])寻址,以支持高效的树更新。- 深入阅读:
references/communication.md#delta-ui-changes
- 深入阅读:
active_script_hash语义:ForwardMsg.metadata.active_script_hash在多页面和 fragment 重跑之间界定节点归属。- 深入阅读:
references/communication.md#forwardmsg-metadata-active_script_hash
- 深入阅读:
- 元素树结构:前端
AppRoot维护main、sidebar、event和bottom容器,其中包含BlockNode/ElementNode。- 深入阅读:
references/frontend.md#element-tree-frontendlibsrcrender-tree
- 深入阅读:
- 元素标识语义:增量路径决定节点所在位置;元素 ID 决定有状态元素在重新挂载后能否重新连接到先前的状态。
- 深入阅读:
references/element-identity.md
- 深入阅读:
- Fragment(
@st.fragment):fragment 交互通常会触发 fragment 范围内的重跑,并只更新受影响的区域。当 fragment 使用@st.fragment(key=...)声明时,组件回调也可以通过st.rerun("<key>")定向到指定的 fragment。
元素标识
- 增量路径控制节点在渲染树中的放置位置。
- 元素 ID控制有状态元素在重新挂载后能否重新连接到先前的状态。
- 组件使用 ID 来连接前端
WidgetStateManager状态、BackMsg.WidgetStates、后端SessionState、回调以及st.session_state。 - 某些非组件元素也使用 ID 进行仅前端重建(例如图表视图状态或媒体自动播放守卫)。
- 增量路径变化可能导致元素重新挂载,但稳定的
element.id仍可让 React 在叶子节点保持在同一渲染父列表下时保留它。
深入阅读:references/element-identity.md
关键实现模式
- 后端 mixin 组合:
DeltaGenerator通过 mixin 组合st.*API。- 深入阅读:
references/backend.md#deltagenerator-libstreamlitdelta_generatorpy
- 深入阅读:
- 前端访问者模式:渲染树更新/过期清理使用访问者。
- 深入阅读:
references/frontend.md#visitor-pattern
- 深入阅读:
- 消息去重:
ForwardMsg.hash+ref_hash配合前端ForwardMsgCache可减少带宽。- 深入阅读:
references/communication.md#message-caching
- 深入阅读:
快速参考:添加功能
- Proto 定义:
proto/streamlit/proto/<Element>.proto - 在 Element.proto 中注册:添加到
oneof type - 后端 mixin:
lib/streamlit/elements/<element>.py - 前端组件:
frontend/lib/src/components/elements/<Element>/或frontend/lib/src/components/widgets/<Element>/(取决于元素还是组件) - 在 ElementNodeRenderer 中注册:向 switch 语句添加 case
- 编译 proto:
make protobuf
详细的实现指南请参见 wiki/new-feature-guide.md。
维护本文档
当进行会影响此处记录的任何概念(Runtime、AppSession、DeltaGenerator、元素树、通信协议、元素标识等)的更改时,请更新本技能文件中相关章节(SKILL.md、references/backend.md、references/frontend.md、references/communication.md、references/element-identity.md),以保持其准确。