PanWatch 前端工程结构:React 18 + pnpm workspace 三包分离设计完整指南
【免费下载链接】PanWatchPanWatch — AI stock monitoring for A-shares, HK & US markets, powered by TradingAgents. Portfolio insights, real-time alerts & automated reports.|盯盘侠:覆盖 A股/港股/美股的 AI 盯盘、持仓分析、实时提醒与自动报告。项目地址: https://gitcode.com/GitHub_Trending/pa/PanWatch
PanWatch 是一款覆盖 A股、港股、美股的 AI 盯盘工具,提供持仓分析、实时价格提醒与自动化报告。它的 Web 前端基于React 18 + TypeScript + Vite,并通过pnpm workspace将代码拆分为@panwatch/api、@panwatch/base-ui、@panwatch/biz-ui三个包。这篇文章带你快速读懂这套「三包分离」的前端工程结构是如何设计的,以及为什么这样拆。
为什么要把前端拆成三个包?
PanWatch 的前端功能密度很高:K线图表、盯盘提醒、模拟交易、AI 助手对话……如果全部塞进一个src目录,组件复用、职责边界、测试维护都会逐渐失控。
团队的思路是按「职责层次」拆包,而不是按页面拆:
| 包名 | 定位 | 典型内容 |
|---|---|---|
@panwatch/api | 统一数据层 | HTTP 客户端、SSE 流、各领域的接口函数与类型定义 |
@panwatch/base-ui | 基础 UI 层 | 按钮、弹窗、标签、Switch 等 13 个通用组件 +cn样式工具 |
@panwatch/biz-ui | 业务 UI 层 | K线图、价格提醒面板、加仓计算器、深度分析弹窗等业务组件 |
包清单与职责说明见 frontend/packages/README.md,其中明确约定:
当前前端页面已统一从
@panwatch/api发起接口请求,避免在页面中直接调用fetch。
这条约定正是三层结构的核心收益:页面只消费业务组件,业务组件只调用领域 API,所有网络请求收敛在 api 包里,想改接口地址、加鉴权头,只动一处。
三个包各自负责什么?
1️⃣ @panwatch/api:收敛所有网络请求
frontend/packages/api/src/index.ts 按领域导出了 21 个模块:stocks(行情)、insight(洞察)、portfolio(持仓)、paper-trading(模拟交易)、chat(AI 助手)、tradingagents(多智能体分析)、sse(实时推送)等。
它把「接口长什么样」和「页面怎么用」彻底解耦——你在页面上看到的实时提醒与盯盘推送:
背后就是sse与price-alerts相关模块在支撑,页面代码完全不直接触碰fetch。
2️⃣ @panwatch/base-ui:与业务无关的通用组件
base-ui内是 13 个纯展示型组件(dialog、select、tabs、toast、popover 等),入口文件 frontend/packages/base-ui/src/index.ts 只导出cn样式合并工具,其余组件按需深路径引入。它不依赖任何业务概念,理论上可以单独抽出复用。
3️⃣ @panwatch/biz-ui:懂股票的业务组件
这是最能体现「业务分层」的一层。打开frontend/packages/biz-ui/src/components/,能看到一整套盯盘专属组件:
InteractiveKline.tsx/KlineModal.tsx:交互式 K 线图与指标(MA/MACD 等)price-alert-form-dialog.tsx:创建股价提醒的表单弹窗add-position-calculator.tsx:加仓仓位计算器deep-analysis-modal.tsx:AI 深度分析弹窗onboarding.tsx:新手引导
这些组件既懂 React,也懂「股票」——入口 frontend/packages/biz-ui/src/index.ts 同时导出market市场配色逻辑与analysis-sections分析区块定义。个股详情页的丰富内容正是它们的组合产物:
pnpm workspace 如何把三包「缝」在一起?
很多读者最关心:三包是本地链接还是发布到 registry?答案是源码级直连,零发布流程。
① workspace 声明。frontend/pnpm-workspace.yaml 只有一行packages/*,即frontend/packages/下的三个目录都是工作区成员,共享根目录的依赖解析与锁文件 frontend/pnpm-lock.yaml。
② Vite 别名指向源码。frontend/vite.config.ts 中把包名直接 alias 到各包src目录:
@panwatch/api→./packages/api/src@panwatch/base-ui→./packages/base-ui/src@panwatch/biz-ui→./packages/biz-ui/src
开发时 Vite 直接编译 TS 源码,改一行代码立即热更新,无需先 build 包。
③ TypeScript 同样跟进。frontend/tsconfig.json 用paths做了一模一样的映射,且include覆盖了packages/*/src,三个包的类型检查、strict与noUnusedLocals规则统一生效——三包在 IDE 里享受与src完全一致的跳转、补全体验。
这套「Vite alias + tsconfig paths 双映射」是 pnpm workspace 前端项目的经典组合拳:运行时和类型系统各走一套,但指向同一份源码。
应用主体 src 目录的组织方式
包拆出去后,frontend/src/ 里剩下的就是「装配层」:
- pages/:14 个路由页面(Dashboard、Stocks、Opportunities、PaperTrading、PriceAlerts、Settings……),由 frontend/src/router/page-loaders.ts 做路由级代码分割,进哪个页面才加载哪个页面的 chunk
- components/:页面级组合组件(AI 助手侧栏、分享卡片等),业务复用的部分已下沉到 biz-ui
- hooks/ + lib/:
use-market-colors、K线评分器、组合页数据加工等横切逻辑 - i18n/:i18next 的中英文案资源,中英双语文档见 docs/
- tests/:vitest + Testing Library 单测,与 src 目录镜像组织
机会发现页这类高频使用的功能,就是pages/Opportunities.tsx+ biz-ui 组件 + api 层三方协作的结果:
如何跑起来这套前端工程?
环境要求:Node.js 24.14.0 / pnpm 9.15.9(由根 frontend/package.json 的packageManager字段锁定版本)。
| 命令 | 作用 |
|---|---|
make dev-web | 一键启动前端(自动 pnpm install,监听 :5183) |
cd frontend && pnpm install && pnpm dev | 手动方式启动,Vite 会把/api代理到本地 8000 端口的后端 |
pnpm test | vitest 单测 |
pnpm check:i18n/pnpm check:market-colors | 工程脚本:检查硬编码文案与市场配色规范 |
其中check:i18n这类脚本(frontend/scripts/)是「三包分离」之外的第二个工程亮点:用 CI 可执行的方式守住国际化与视觉规范,而不是靠人工 review。
小结:这套结构适合谁借鉴?
PanWatch 的 React 18 + pnpm workspace 方案,本质是用「数据 / 基础 UI / 业务 UI」三层切面替代「按页面堆文件」:
- api 包保证网络请求单一入口,改接口只动一处;
- base-ui 包沉淀与领域无关的组件,保持纯净可复用;
- biz-ui 包让股票领域知识(K线、提醒、仓位)在组件层就被封装,页面只做装配;
- Vite alias + tsconfig paths让三包以源码形式参与开发,零发布、零延迟热更新。
如果你的项目也是「功能多、组件重、接口密」的中型 Web 应用,这套三包分离的 pnpm workspace 结构值得直接照搬。
📌 想深入了解后端与 TradingAgents 智能体架构,可继续阅读 src/ARCHITECTURE.md 与 CONTRIBUTING.md。
【免费下载链接】PanWatchPanWatch — AI stock monitoring for A-shares, HK & US markets, powered by TradingAgents. Portfolio insights, real-time alerts & automated reports.|盯盘侠:覆盖 A股/港股/美股的 AI 盯盘、持仓分析、实时提醒与自动报告。项目地址: https://gitcode.com/GitHub_Trending/pa/PanWatch
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考