文章目录
- 一、多页面应用概述
-
- [1. 两种构建方式概览](#1. 两种构建方式概览)
-
- [1.1 st.Page 与 st.navigation(推荐)](#1.1 st.Page 与 st.navigation(推荐))
- [1.2 pages/ 目录(快速方案)](#1.2 pages/ 目录(快速方案))
- [2. 页面核心术语解析](#2. 页面核心术语解析)
- [3. 自动标签与URL命名规则](#3. 自动标签与URL命名规则)
-
- [3.1 文件名解析规则](#3.1 文件名解析规则)
- [4. 页面导航方式](#4. 页面导航方式)
- 二、页面与导航配置
-
- [1. 应用结构与入口文件](#1. 应用结构与入口文件)
-
- [1.1 基础项目结构示例](#1.1 基础项目结构示例)
- [1.2 入口文件基础配置](#1.2 入口文件基础配置)
- [2. 定义页面](#2. 定义页面)
-
- [2.1 基础用法](#2.1 基础用法)
- [2.2 全局页面配置](#2.2 全局页面配置)
- [3. 导航菜单自定义](#3. 导航菜单自定义)
-
- [3.1 页面分组与分类](#3.1 页面分组与分类)
- [3.2 动态导航菜单(基于用户角色)](#3.2 动态导航菜单(基于用户角色))
- [3.3 导航位置调整](#3.3 导航位置调整)
- [4. 编程式页面跳转](#4. 编程式页面跳转)
-
- [4.1 直接跳转页面](#4.1 直接跳转页面)
- [4.2 自定义页面链接](#4.2 自定义页面链接)
- [5. 关键注意事项](#5. 关键注意事项)
- 三、页面目录结构规范
-
- [1. 基础目录结构](#1. 基础目录结构)
- [2. 页面排序与命名规范](#2. 页面排序与命名规范)
-
- [2.1 自动标签解析](#2.1 自动标签解析)
- [2.2 排序规则](#2.2 排序规则)
- [3. 核心特性与限制](#3. 核心特性与限制)
-
- [3.1 页面更新与重运行](#3.1 页面更新与重运行)
- [3.2 页面配置](#3.2 页面配置)
- [3.3 全局模块与会话状态共享](#3.3 全局模块与会话状态共享)
- [4. 最佳实践与注意事项](#4. 最佳实践与注意事项)
- 四、多页面下的组件状态管理
-
- [1. 核心问题:多页面组件状态重置的原因](#1. 核心问题:多页面组件状态重置的原因)
- [2. 方案1:在入口文件中定义全局组件(推荐)](#2. 方案1:在入口文件中定义全局组件(推荐))
- [3. 方案2:通过独立的会话状态变量保存组件值](#3. 方案2:通过独立的会话状态变量保存组件值)
- [4. 方案3:中断组件清理过程](#4. 方案3:中断组件清理过程)
- [5. 三种方案对比](#5. 三种方案对比)
- [6. 最佳实践建议](#6. 最佳实践建议)
一、多页面应用概述
Streamlit 提供了两种主流方式来构建多页面应用,一种是简单易用的 pages/ 目录结构,另一种是更灵活、可定制性更强的 st.navigation 方式。本节将带你了解这两种方案的核心概念与使用场景。
1. 两种构建方式概览
1.1 st.Page 与 st.navigation(推荐)
这是 Streamlit 推荐的、更灵活的多页面构建方式,能实现高度自定义的导航与页面逻辑。
- 高度自定义:可以将任意 Python 文件或可调用函数声明为页面,甚至支持自定义页面图标、URL 路径。
- 共享公共元素:可以在入口文件中定义跨页面的公共组件(如侧边栏、顶部导航栏),这些元素会被所有页面共享,避免重复代码。
- 中心化路由 :通过
st.navigation配置导航菜单,入口文件同时充当应用的路由器,统一管理页面跳转。
1.2 pages/ 目录(快速方案)
如果希望快速搭建一个多页面应用,pages/ 目录是最简单的选择。
- 零配置 :在入口文件同级创建
pages/目录,放入 Python 文件即可自动生成页面。 - 自动导航:Streamlit 会根据文件名自动生成侧边栏导航菜单,无需额外配置。
-
目录结构示例 :
textyour_project/ ├── your_homepage.py # 入口文件(首页) └── pages/ ├── a_page.py # 自动生成的页面 └── another_page.py
2. 页面核心术语解析
在 Streamlit 中,一个页面包含以下关键标识信息:
| 术语 | 说明 |
|---|---|
| Page source | 页面的源代码,可以是 Python 文件或可调用函数 |
| Page label | 导航菜单中显示的页面名称 |
| Page title | 浏览器标签页的标题(HTML <title>) |
| Page URL pathname | 页面在应用根 URL 下的相对路径 |
| Page favicon | 浏览器标签页上显示的图标 |
| Page icon | 导航菜单中页面标签旁的图标 |
3. 自动标签与URL命名规则
当使用 pages/ 目录或未显式配置 st.Page 时,Streamlit 会根据文件名自动生成页面标签、标题和 URL 路径。
3.1 文件名解析规则
文件名由以下几部分组成,Streamlit 会自动处理:
- 数字前缀 :用于控制导航菜单中的页面顺序(如
01_首页.py、02_数据.py) - 分隔符 :下划线
_、短横线-、空格等符号 - 标识符:文件名的主体部分,Streamlit 会将其中的下划线自动转换为空格,作为导航菜单中的标签名
例如,以下文件名都会被解析为导航菜单中的 Awesome page:
Awesome page.pyAwesome_page.py02Awesome_page.py1_Awesome_page.py
对应的 URL 路径则会被处理为 awesome_page(将连续的空格和下划线合并为单个下划线)。
4. 页面导航方式
用户在多页面应用中可以通过以下方式跳转页面:
- 侧边栏导航菜单:默认方式,点击菜单中的页面名称即可跳转
- 自定义导航链接 :通过
st.page_link手动构建导航菜单 - 编程式跳转 :使用
st.switch_page在代码中实现页面跳转(如点击按钮后跳转到结果页) - 直接访问URL:通过浏览器地址栏直接访问页面的 URL 路径
注意 :通过 URL 直接访问页面会创建新的浏览器会话,可能导致
st.session_state状态丢失。如需保留会话状态,建议使用 Streamlit 内置的导航方式。
二、页面与导航配置
在 Streamlit 中,使用 st.Page 和 st.navigation 是构建多页面应用的推荐方式,它提供了高度灵活的项目组织和导航菜单自定义能力。
1. 应用结构与入口文件
当使用 st.navigation 时,入口文件(通过 streamlit run 运行的文件)扮演着页面路由器的角色。
- 可以将任意 Python 文件或可调用函数声明为页面。
- 入口文件中的组件(如
st.sidebar、全局变量)会成为所有页面共享的公共元素,就像一个"画框",包裹着每一个页面。 - 每个应用只能调用一次
st.navigation,且必须在入口文件中调用。
1.1 基础项目结构示例
text
your-repository/
├── page_1.py # 页面1代码
├── page_2.py # 页面2代码
└── streamlit_app.py # 入口文件/路由器
1.2 入口文件基础配置
python
# streamlit_app.py
import streamlit as st
# 1. 定义页面对象
pg = st.navigation([
st.Page("page_1.py"),
st.Page("page_2.py")
])
# 2. 运行导航
pg.run()
2. 定义页面
st.Page 是声明页面的核心命令,它支持多种配置方式:
2.1 基础用法
python
# 从Python文件创建页面(路径必须相对于入口文件)
create_page = st.Page("create.py")
# 配置页面标题、图标和URL路径
create_page = st.Page(
"create.py",
title="创建数据", # 导航菜单显示的名称
icon=":material/add_circle:", # 导航菜单图标
url_path="create-entry" # 自定义URL路径
)
配置页面元信息:
title:同时设置导航菜单标签和浏览器标签页标题icon:设置导航菜单中的图标,支持 Material 图标(:material/name:)url_path:自定义页面的URL路径,默认从文件名自动解析default=True:设置为应用的默认首页(访问根URL时加载的页面)
2.2 全局页面配置
在入口文件中使用 st.set_page_config 配置全局页面属性。
python
# streamlit_app.py(全局配置)
st.set_page_config(
page_title="数据管理系统",
page_icon=":material/edit:"
)
在单个页面中重写配置。
python
# 在create.py页面中(重写配置)
st.set_page_config(page_title="创建数据 - 数据管理系统")
3. 导航菜单自定义
st.navigation 提供了丰富的导航配置选项,支持分组、动态显示和位置调整。
3.1 页面分组与分类
可以将页面组织成多个分组,在侧边栏中显示为不同的部分。
python
pg = st.navigation({
"仪表盘": [page1],
"数据操作": [page2, page3],
"工具": [page4, page5]
})

3.2 动态导航菜单(基于用户角色)
通过会话状态动态控制导航菜单的显示内容,实现权限控制。
python
import streamlit as st
if "logged_in" not in st.session_state:
st.session_state.logged_in = False
def login():
if st.button("登录"):
st.session_state.logged_in = True
st.rerun()
def logout():
if st.button("退出登录"):
st.session_state.logged_in = False
st.rerun()
login_page = st.Page(login, title="登录", icon=":material/login:")
logout_page = st.Page(logout, title="退出", icon=":material/logout:")
dashboard_page = st.Page("page_1.py", title="仪表盘", icon=":material/dashboard:", default=True)
page2 = st.Page("page_2.py", title="Page 2", icon="❄️")
page3 = st.Page("page_3.py", title="Page 3", icon="🎉")
page4 = st.Page("page_4.py", title="Page 4", icon="🎉")
page5 = st.Page("page_5.py", title="Page 5", icon="🎉")
# 根据登录状态显示不同的导航菜单
if st.session_state.logged_in:
pg = st.navigation({
"账户": [logout_page],
"主功能": [dashboard_page, page2, page3],
"工具": [page4, page5]
})
else:
pg = st.navigation([login_page])
pg.run()

3.3 导航位置调整
通过 position 参数调整导航菜单的显示位置。
python
# 在顶部显示导航栏
pg = st.navigation([page1, page2], position="top")
# 在侧边栏显示(默认)
pg = st.navigation([page1, page2], position="sidebar")
4. 编程式页面跳转
除了用户手动点击导航菜单,你也可以通过代码控制页面跳转。
4.1 直接跳转页面
通过st.switch_page()直接跳转到指定页面。
python
# 在按钮点击后跳转到仪表盘页面
if st.button("前往仪表盘"):
st.switch_page("dashboard.py")
4.2 自定义页面链接
可以隐藏默认导航菜单,通过 st.page_link 构建完全自定义的导航。
python
# 隐藏默认导航菜单
st.set_page_config(menu_items={"About": None, "Get help": None, "Report a bug": None})
# 自定义导航链接
st.page_link("dashboard.py", label="仪表盘", icon=":material/dashboard:")
st.page_link("create.py", label="创建数据", icon=":material/add_circle:")
5. 关键注意事项
- 页面路径问题 :
st.Page的路径必须相对于入口文件,不能使用绝对路径 - URL访问限制 :如果页面未被包含在
st.navigation中,直接通过URL访问会提示"Page not found" - 状态持久化 :通过URL直接访问页面会创建新会话,导致
st.session_state丢失,建议使用内置导航方式 - 动态导航的限制:隐藏的页面仍可通过URL直接访问,无法通过导航菜单隐藏实现真正的权限控制
三、页面目录结构规范
pages/ 目录是 Streamlit 提供的一种快速构建多页面应用的方式,无需复杂配置,只需遵循特定的目录和命名规范,即可自动生成侧边栏导航菜单。
1. 基础目录结构
使用 pages/ 目录时,项目结构必须遵循以下规范:
text
your_working_directory/
├── your_homepage.py # 入口文件(应用首页)
└── pages/ # 必须创建的子目录,所有子页面必须放在这里
├── a_page.py # 子页面1
└── another_page.py # 子页面2
关键规则:
-
仅
pages/目录下的.py文件会被识别为页面,子目录、非.py文件会被忽略。 -
入口文件
your_homepage.py为应用的首页,会始终显示在导航菜单的首位。 -
启动方式与单页应用相同:
bashstreamlit run your_homepage.py
重要提示 :一旦应用中调用了
st.navigation,Streamlit 会切换到新的多页面架构,pages/目录将被忽略,且无法回退,必须重启应用才能恢复。
2. 页面排序与命名规范
Streamlit 会根据文件名自动解析导航菜单的标签和排序,遵循以下规则:
2.1 自动标签解析
文件名中的下划线 _、短横线 - 和空格会被自动转换为空格,作为导航菜单的显示标签。
| 文件名 | 导航菜单显示标签 |
|---|---|
1 - first page.py |
first page |
12 monkeys.py |
monkeys |
123.py |
123 |
123_hello_dear_world.py |
hello dear world |
2.2 排序规则
- 带数字前缀的文件会排在无数字前缀的文件之前。
- 数字按实际数值排序(如
03视为数字3,排在12之前)。 - 可使用 Emoji 美化页面名称,如
🏠_Home.py会显示为🏠 Home,建议配合数字前缀使用,避免终端自动补全异常。
3. 核心特性与限制
3.1 页面更新与重运行
- 运行时修改当前正在查看的页面文件,会立即触发该页面的重运行。
- 修改其他页面的文件,不会触发当前页面的重运行,用户不会受到影响。
- 运行时新增/删除
pages/目录下的文件,侧边栏导航菜单会自动更新。
3.2 页面配置
st.set_page_config 仅对当前页面生效:
page_title和page_icon会设置当前页面的浏览器标签信息。layout等布局设置会持续到被其他页面的st.set_page_config覆盖,建议在所有页面中统一配置。
3.3 全局模块与会话状态共享
-
模块共享:所有页面共享同一个 Python 模块环境,一个页面中对模块变量的修改会影响其他页面。
-
会话状态共享 :所有页面共享同一个
st.session_state,在一个页面中设置的状态变量,可在其他页面中读取。python# page1.py(设置共享状态) import streamlit as st if "shared" not in st.session_state: st.session_state["shared"] = True # page2.py(读取共享状态) import streamlit as st st.write(st.session_state["shared"]) # 输出 True
4. 最佳实践与注意事项
- 统一前缀命名 :使用数字前缀(如
01_、02_)控制页面顺序,避免使用复杂符号。 - 避免直接修改共享模块 :不同页面修改同一模块变量可能导致不可预期的行为,建议优先使用
st.session_state管理共享数据。 - 统一页面配置 :在所有页面中调用
st.set_page_config,确保布局和标题风格一致。 - Emoji 使用建议:添加 Emoji 时配合数字前缀,避免终端自动补全异常。
四、多页面下的组件状态管理
在多页面应用中,Streamlit 会根据组件所在页面生成唯一的 widget ID,切换页面时,非当前页面的组件状态会被重置。本节将介绍三种主流方案,让你实现跨页面的组件状态持久化。
1. 核心问题:多页面组件状态重置的原因
- Streamlit 组件的
widget ID与创建它的页面强绑定。 - 当你在不同页面使用同名组件时,切换页面会被视为创建了新组件,导致状态重置为默认值。
- 若组件仅在单个页面显示,离开该页面后,其状态也会被清理。
2. 方案1:在入口文件中定义全局组件(推荐)
当使用 st.Page + st.navigation 架构时,入口文件会作为所有页面的"公共画框",在其中定义的组件会在所有页面保持状态。
示例:全局侧边栏组件
python
# streamlit_app.py(入口文件)
import streamlit as st
# 定义导航
pg = st.navigation([
st.Page("page_1.py"),
st.Page("page_2.py")
])
# 在入口文件中定义全局组件,所有页面共享状态
st.sidebar.selectbox("分组", ["A", "B", "C"], key="global_group")
st.sidebar.slider("数值", 1, 5, key="global_size")
# 运行导航
pg.run()

特点:
- ✅ 状态在所有页面保持不变,无需额外代码
- ✅ 组件始终可见,适合全局筛选器、设置等场景
- ❌ 不适用于
pages/目录架构,仅支持st.navigation方式
3. 方案2:通过独立的会话状态变量保存组件值
如果组件仅在特定页面显示,但需要在离开后保留状态,可以使用一个独立的会话状态变量,将组件值复制到其中持久化。
基础示例:
python
import streamlit as st
def store_value():
# 将组件值复制到独立的持久化变量
st.session_state["saved_filter"] = st.session_state["_filter_temp"]
# 初始化持久化变量
if "saved_filter" not in st.session_state:
st.session_state["saved_filter"] = 0
# 初始化临时变量(用于组件)
st.session_state["_filter_temp"] = st.session_state["saved_filter"]
# 定义组件,使用临时key,并绑定回调保存值
st.number_input("筛选数量", key="_filter_temp", on_change=store_value)
封装成通用函数(支持多组件):
python
import streamlit as st
def store_widget_value(key):
"""将组件值保存到持久化变量"""
st.session_state[f"saved_{key}"] = st.session_state[f"_{key}"]
def load_widget_value(key):
"""加载持久化值到组件临时变量"""
if f"saved_{key}" in st.session_state:
st.session_state[f"_{key}"] = st.session_state[f"saved_{key}"]
# 使用示例:筛选数量组件
load_widget_value("filter_count")
st.number_input("筛选数量", key="_filter_count", on_change=store_widget_value, args=["filter_count"])
特点:
- ✅ 组件可仅在单个页面显示,状态仍能跨页面保留
- ✅ 兼容
pages/目录和st.navigation两种架构 - ✅ 支持同一组件在多个页面复用,状态同步
4. 方案3:中断组件清理过程
Streamlit 会在脚本运行结束时,清理当前页面未渲染组件的状态。通过重新赋值会话状态变量,可以阻止清理过程,实现状态保留。
基础示例:
python
import streamlit as st
# 在每个页面的顶部添加以下代码,或在入口文件中添加一次(仅st.navigation架构)
if "my_widget_key" in st.session_state:
st.session_state.my_widget_key = st.session_state.my_widget_key
# 定义组件,使用同一个key
st.number_input("数值输入", key="my_widget_key")
特点:
- ✅ 实现简单,无需额外回调或临时变量
- ✅ 同一组件可在多个页面复用,状态自动同步
- ⚠️ 需注意组件key的唯一性,避免冲突
5. 三种方案对比
| 方案 | 适用场景 | 优势 | 限制 |
|---|---|---|---|
| 方案1:入口文件全局组件 | 全局可见的筛选器、设置 | 零代码实现,状态自动同步 | 仅支持 st.navigation 架构 |
| 方案2:会话状态独立变量 | 单页面显示但需跨页保留的组件 | 灵活可控,兼容所有架构 | 需要额外的回调和临时变量 |
| 方案3:中断清理过程 | 多页面复用同一组件 | 实现简单,无需额外逻辑 | 需注意key冲突,可能影响组件行为 |
6. 最佳实践建议
- 优先使用方案1:对于全局设置、筛选器等场景,直接在入口文件定义组件是最简洁的方式。
- 单页面组件用方案2:若组件仅在特定页面显示,推荐使用独立会话状态变量的方式,避免状态意外清理。
- 避免方案3的滥用:该方式可能导致组件状态异常,仅作为临时解决方案使用。
- 统一命名规范 :为持久化变量使用统一前缀(如
saved_),避免与会话状态中的其他变量冲突。