8.7 KiB
8.7 KiB
🏗️ 前端重构说明
重构目标
将原来的单体 frontend.py(280+ 行)拆分为模块化、可维护的架构,参考后端的分层设计模式。
📁 新架构
frontend/
├── __init__.py # 包初始化
├── frontend.py # 主入口(50 行,仅负责组装)
├── config.py # 配置管理(数据类 + 环境变量)
├── state.py # 状态管理(统一 Session State 操作)
├── api_client.py # API 客户端(封装所有后端通信)
├── utils.py # 工具函数(通用辅助函数)
└── components/ # UI 组件
├── __init__.py
├── sidebar.py # 左侧栏:用户登录 + 历史列表
├── chat_area.py # 中间栏:聊天区域 + 流式响应
└── info_panel.py # 右侧栏:信息面板
🎯 核心模块说明
1. 配置管理 (config.py)
设计理念:使用 Python dataclass 集中管理所有配置,支持环境变量覆盖。
@dataclass
class FrontendConfig:
api_base: str = ""
page_title: str = "AI 个人助手"
default_model: str = "zhipu"
history_limit: int = 50
# ... 其他配置
# 全局配置实例
config = FrontendConfig()
优势:
- ✅ 类型安全(dataclass 自动类型检查)
- ✅ 集中管理(所有配置在一处)
- ✅ 易于测试(可轻松 mock 配置)
- ✅ 环境变量支持(
__post_init__中加载)
2. 状态管理 (state.py)
设计理念:封装所有 st.session_state 操作,提供统一的 API。
class AppState:
@staticmethod
def init():
"""初始化所有状态"""
if "user_id" not in st.session_state:
st.session_state.user_id = config.default_user_id
# ...
@staticmethod
def login(username: str):
"""用户登录"""
st.session_state.user_id = username.strip()
st.session_state.logged_in = True
@staticmethod
def get_messages() -> List[Dict[str, str]]:
"""获取消息列表"""
return st.session_state.messages
优势:
- ✅ 统一接口(所有状态操作通过 AppState)
- ✅ 类型提示(IDE 自动补全)
- ✅ 易于维护(状态逻辑集中)
- ✅ 避免魔法字符串(不再直接使用
st.session_state["xxx"])
3. API 客户端 (api_client.py)
设计理念:封装所有与后端的通信,支持流式响应。
class APIClient:
def get_user_threads(self, user_id: str, limit: int) -> List[Dict]:
"""获取用户历史列表"""
resp = requests.get(f"{self.base_url}/threads", ...)
return resp.json().get("threads", [])
def chat_stream(self, message: str, ...) -> AsyncGenerator[Dict, None]:
"""流式对话"""
with requests.post(..., stream=True) as response:
for line in response.iter_lines():
yield json.loads(line)
优势:
- ✅ 职责单一(仅负责 API 通信)
- ✅ 错误处理集中(统一的异常捕获)
- ✅ 易于测试(可 mock APIClient)
- ✅ 流式支持(Generator 逐行 yield)
4. UI 组件 (components/)
设计理念:每个组件独立渲染,通过 State 和 API Client 交互。
sidebar.py - 左侧栏
def render_sidebar():
"""渲染左侧栏"""
with st.sidebar:
_render_user_section() # 用户登录
_render_history_section() # 历史列表
chat_area.py - 中间聊天区
def render_chat_area():
"""渲染中间聊天区域"""
_render_model_selector() # 模型选择
_render_chat_container() # 消息显示
_render_input_box() # 输入框 + 流式响应
info_panel.py - 右侧信息面板
def render_info_panel():
"""渲染右侧信息面板"""
_render_thread_info() # 当前线程
_render_message_stats() # 消息统计
_render_tips() # 使用提示
优势:
- ✅ 组件独立(每个文件 < 150 行)
- ✅ 职责清晰(一个组件一个文件)
- ✅ 易于复用(可在其他页面复用组件)
- ✅ 易于测试(可独立测试每个组件)
5. 主入口 (frontend.py)
设计理念:仅负责组装各模块,代码量 < 50 行。
from .config import config
from .state import AppState
from .components.sidebar import render_sidebar
from .components.chat_area import render_chat_area
from .components.info_panel import render_info_panel
st.set_page_config(...)
AppState.init()
def main():
st.title("🤖 个人生活与数据分析助手")
col_sidebar, col_chat, col_info = st.columns([1, 3, 1])
with col_sidebar:
render_sidebar()
with col_chat:
render_chat_area()
with col_info:
render_info_panel()
if __name__ == "__main__":
main()
优势:
- ✅ 极简主义(< 50 行)
- ✅ 清晰结构(一眼看懂整体架构)
- ✅ 易于维护(修改功能只需改对应组件)
重构对比
| 指标 | 重构前 | 重构后 | 改进 |
|---|---|---|---|
| 主文件行数 | 280+ 行 | 48 行 | ✅ -83% |
| 代码结构 | 单体文件 | 模块化架构 | ✅ 分层清晰 |
| 组件独立性 | 耦合严重 | 独立组件 | ✅ 可复用 |
| 测试友好性 | 难以测试 | 易于 Mock | ✅ 可测试 |
| 维护成本 | 高(改一处影响全局) | 低(改组件不影响其他) | ✅ 易维护 |
| 代码可读性 | 差(滚动查找) | 优(模块化) | ✅ 易读 |
🎨 架构设计模式
1. 分层架构
┌─────────────────────────────────────┐
│ 表现层 (Components) │
│ sidebar.py, chat_area.py, ... │
├─────────────────────────────────────┤
│ 业务层 (State) │
│ state.py - 状态管理 │
├─────────────────────────────────────┤
│ 数据层 (API Client) │
│ api_client.py - 后端通信 │
├─────────────────────────────────────┤
│ 配置层 (Config) │
│ config.py - 配置管理 │
└─────────────────────────────────────┘
2. 依赖方向
Components → State → API Client → Config
↑ ↓
└────────────────────────┘
(全局单例实例)
规则:
- ✅ 上层依赖下层
- ✅ 禁止循环依赖
- ✅ 配置和客户端为全局单例
🚀 使用示例
扩展新功能:添加对话导出按钮
只需修改 components/sidebar.py:
def _render_history_actions():
"""渲染历史操作按钮"""
if st.button("🔄 刷新列表", use_container_width=True):
_refresh_threads()
if st.button("➕ 新对话", type="primary", use_container_width=True):
AppState.start_new_thread()
st.rerun()
# 新增:导出对话按钮
if st.button("📤 导出对话", use_container_width=True):
_export_current_thread()
def _export_current_thread():
"""导出当前对话为 Markdown"""
messages = AppState.get_messages()
content = "\n\n".join([f"**{m['role']}**: {m['content']}" for m in messages])
st.download_button("下载", content, "conversation.md")
优势:修改仅影响 sidebar.py,不影响其他模块!
✅ 重构优势总结
- 模块化:每个文件职责单一,易于理解和维护
- 可扩展:添加新功能只需修改对应模块
- 可测试:各模块独立,便于编写单元测试
- 可复用:组件可在其他项目中复用
- 类型安全:使用 dataclass 和类型提示
- 代码质量:遵循 SOLID 原则和 Clean Architecture
📝 后续优化建议
- 添加单元测试:为
state.py和api_client.py编写测试 - 错误边界:在组件中添加 try-except,避免单个组件崩溃影响全局
- 性能优化:使用
st.cache_data缓存 API 响应 - 国际化:提取所有文本到
i18n.py,支持多语言 - 主题支持:添加暗色/亮色主题切换
🎉 前端重构完成!代码结构更清晰,维护成本大幅降低!