【文字三国志:第五篇】天命重构,游戏前端UI设计

第一章:设计总览

在开始具体的界面开发之前,我们首先需要确立一套贯穿始终的设计理念。这就像建造房屋前要先打好地基、确立风格一样。我们的目标是为玩家创造一个沉浸、流畅且友好 的游戏世界。

1.1 四大核心设计目标

我们将围绕以下四个支柱来构建所有界面:

  1. 中文本土化体验

    • 原理:游戏不仅是功能集合,更是一种文化体验。使用玩家最熟悉的语言、日期和时间格式,能显著降低认知负担,增强代入感。
    • 实现 :所有对用户展示的时间、日期,均强制使用 Asia/Shanghai 时区进行处理。
  2. 响应式布局 (Responsive Design)

    • 原理:玩家可能在PC、平板或手机上随时进入游戏。界面需要像水一样,自适应不同屏幕尺寸,保证在任何设备上都能顺畅操作,而不是"桌面版勉强在手机上用"。
    • 实现 :利用 Tailwind CSS 的响应式断点系统(sm, md, lg, xl),为不同屏幕宽度设计不同的布局和元素大小。
  3. 无障碍访问 (Accessibility)

    • 原理:优秀的软件应服务于所有人,包括有特殊需求的用户(如使用屏幕阅读器或仅通过键盘操作)。遵循无障碍标准,既是技术责任,也是设计关怀的体现。
    • 实现 :所有交互组件(按钮、弹窗、表单)都基于 shadcn/ui 构建。该组件库底层使用了 Radix UI ,默认提供了完善的键盘导航支持和 ARIA 标记,让辅助设备可以正确识别页面内容。
  4. 统一的视觉风格

    • 原理:一个风格混乱、颜色随意的界面会显得廉价且令人困惑。一个系统化的设计语言(Design System)能确保界面的一致性、专业性和美感。
    • 实现
      • 主题 :采用暗色主题为主,减少长时间游戏的眼部疲劳,并营造沉浸氛围。
      • 配置 :所有颜色(主色、辅色、强调色等)均在 tailwind.config.ts 中集中定义,通过语义化的类名(如 bg-primary)使用。
      • 跟随系统:支持根据操作系统偏好自动切换深浅色模式。

第二章:架构蓝图 - 组件层级与布局

清晰的组件结构是保证项目可维护性和性能的基础。我们采用树形结构来组织UI,自上而下地传递数据与事件。

2.1 组件层级结构

下图展示了从顶层应用到底层UI组件的完整嵌套关系:
#mermaid-svg-znIhqrG26q8uJbs7{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-znIhqrG26q8uJbs7 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-znIhqrG26q8uJbs7 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-znIhqrG26q8uJbs7 .error-icon{fill:#552222;}#mermaid-svg-znIhqrG26q8uJbs7 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-znIhqrG26q8uJbs7 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-znIhqrG26q8uJbs7 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-znIhqrG26q8uJbs7 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-znIhqrG26q8uJbs7 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-znIhqrG26q8uJbs7 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-znIhqrG26q8uJbs7 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-znIhqrG26q8uJbs7 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-znIhqrG26q8uJbs7 .marker.cross{stroke:#333333;}#mermaid-svg-znIhqrG26q8uJbs7 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-znIhqrG26q8uJbs7 p{margin:0;}#mermaid-svg-znIhqrG26q8uJbs7 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-znIhqrG26q8uJbs7 .cluster-label text{fill:#333;}#mermaid-svg-znIhqrG26q8uJbs7 .cluster-label span{color:#333;}#mermaid-svg-znIhqrG26q8uJbs7 .cluster-label span p{background-color:transparent;}#mermaid-svg-znIhqrG26q8uJbs7 .label text,#mermaid-svg-znIhqrG26q8uJbs7 span{fill:#333;color:#333;}#mermaid-svg-znIhqrG26q8uJbs7 .node rect,#mermaid-svg-znIhqrG26q8uJbs7 .node circle,#mermaid-svg-znIhqrG26q8uJbs7 .node ellipse,#mermaid-svg-znIhqrG26q8uJbs7 .node polygon,#mermaid-svg-znIhqrG26q8uJbs7 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-znIhqrG26q8uJbs7 .rough-node .label text,#mermaid-svg-znIhqrG26q8uJbs7 .node .label text,#mermaid-svg-znIhqrG26q8uJbs7 .image-shape .label,#mermaid-svg-znIhqrG26q8uJbs7 .icon-shape .label{text-anchor:middle;}#mermaid-svg-znIhqrG26q8uJbs7 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-znIhqrG26q8uJbs7 .rough-node .label,#mermaid-svg-znIhqrG26q8uJbs7 .node .label,#mermaid-svg-znIhqrG26q8uJbs7 .image-shape .label,#mermaid-svg-znIhqrG26q8uJbs7 .icon-shape .label{text-align:center;}#mermaid-svg-znIhqrG26q8uJbs7 .node.clickable{cursor:pointer;}#mermaid-svg-znIhqrG26q8uJbs7 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-znIhqrG26q8uJbs7 .arrowheadPath{fill:#333333;}#mermaid-svg-znIhqrG26q8uJbs7 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-znIhqrG26q8uJbs7 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-znIhqrG26q8uJbs7 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-znIhqrG26q8uJbs7 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-znIhqrG26q8uJbs7 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-znIhqrG26q8uJbs7 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-znIhqrG26q8uJbs7 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-znIhqrG26q8uJbs7 .cluster text{fill:#333;}#mermaid-svg-znIhqrG26q8uJbs7 .cluster span{color:#333;}#mermaid-svg-znIhqrG26q8uJbs7 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-znIhqrG26q8uJbs7 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-znIhqrG26q8uJbs7 rect.text{fill:none;stroke-width:0;}#mermaid-svg-znIhqrG26q8uJbs7 .icon-shape,#mermaid-svg-znIhqrG26q8uJbs7 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-znIhqrG26q8uJbs7 .icon-shape p,#mermaid-svg-znIhqrG26q8uJbs7 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-znIhqrG26q8uJbs7 .icon-shape .label rect,#mermaid-svg-znIhqrG26q8uJbs7 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-znIhqrG26q8uJbs7 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-znIhqrG26q8uJbs7 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-znIhqrG26q8uJbs7 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 底部栏
中央主面板 动态切换
顶部栏
游戏主视图 使用CSS Grid
全局布局
App
Layout
Header
Sidebar
Footer
GameScreen
TopBar
MainPanel
BottomBar
ResourceBar
TurnCounter
CombatPanel
DiplomacyPanel
...
QuickActions
InfoMessages

2.2 各层级职责与设计原理
  • Layout (全局布局)

    • 职责 :应用最外层。负责包裹全局状态提供者(Provider),处理主题切换逻辑,并捕获全局错误。
    • 原理:将状态和主题等顶级关注点与具体游戏页面解耦,确保整个应用的环境一致性。
  • GameScreen (游戏主视图)

    • 职责 :游戏核心页面的容器。使用 CSS Grid 定义顶部栏、主面板、底部栏的区域划分。
    • 原理 :CSS Grid 是二维布局系统,非常适合定义页面的宏观区域结构,并能通过 grid-template-areas 轻松实现不同屏幕尺寸下的布局重排,比浮动或Flexbox更适合做整体框架。
  • Panel 组件 (业务子系统)

    • 职责:每个独立的功能模块(如战斗、政策面板)都是一个自包含的组件。
    • 原理 :遵循 单向数据流 原则。父组件通过 props 将数据传给Panel;Panel内部的用户操作通过回调函数 onAction 通知父组件,父组件再更新状态。这种模式让数据流动方向清晰,便于调试和测试。

#mermaid-svg-KDSltZS1yyLucQg6{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-KDSltZS1yyLucQg6 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-KDSltZS1yyLucQg6 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-KDSltZS1yyLucQg6 .error-icon{fill:#552222;}#mermaid-svg-KDSltZS1yyLucQg6 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-KDSltZS1yyLucQg6 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-KDSltZS1yyLucQg6 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-KDSltZS1yyLucQg6 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-KDSltZS1yyLucQg6 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-KDSltZS1yyLucQg6 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-KDSltZS1yyLucQg6 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-KDSltZS1yyLucQg6 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-KDSltZS1yyLucQg6 .marker.cross{stroke:#333333;}#mermaid-svg-KDSltZS1yyLucQg6 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-KDSltZS1yyLucQg6 p{margin:0;}#mermaid-svg-KDSltZS1yyLucQg6 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-KDSltZS1yyLucQg6 .cluster-label text{fill:#333;}#mermaid-svg-KDSltZS1yyLucQg6 .cluster-label span{color:#333;}#mermaid-svg-KDSltZS1yyLucQg6 .cluster-label span p{background-color:transparent;}#mermaid-svg-KDSltZS1yyLucQg6 .label text,#mermaid-svg-KDSltZS1yyLucQg6 span{fill:#333;color:#333;}#mermaid-svg-KDSltZS1yyLucQg6 .node rect,#mermaid-svg-KDSltZS1yyLucQg6 .node circle,#mermaid-svg-KDSltZS1yyLucQg6 .node ellipse,#mermaid-svg-KDSltZS1yyLucQg6 .node polygon,#mermaid-svg-KDSltZS1yyLucQg6 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-KDSltZS1yyLucQg6 .rough-node .label text,#mermaid-svg-KDSltZS1yyLucQg6 .node .label text,#mermaid-svg-KDSltZS1yyLucQg6 .image-shape .label,#mermaid-svg-KDSltZS1yyLucQg6 .icon-shape .label{text-anchor:middle;}#mermaid-svg-KDSltZS1yyLucQg6 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-KDSltZS1yyLucQg6 .rough-node .label,#mermaid-svg-KDSltZS1yyLucQg6 .node .label,#mermaid-svg-KDSltZS1yyLucQg6 .image-shape .label,#mermaid-svg-KDSltZS1yyLucQg6 .icon-shape .label{text-align:center;}#mermaid-svg-KDSltZS1yyLucQg6 .node.clickable{cursor:pointer;}#mermaid-svg-KDSltZS1yyLucQg6 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-KDSltZS1yyLucQg6 .arrowheadPath{fill:#333333;}#mermaid-svg-KDSltZS1yyLucQg6 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-KDSltZS1yyLucQg6 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-KDSltZS1yyLucQg6 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-KDSltZS1yyLucQg6 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-KDSltZS1yyLucQg6 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-KDSltZS1yyLucQg6 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-KDSltZS1yyLucQg6 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-KDSltZS1yyLucQg6 .cluster text{fill:#333;}#mermaid-svg-KDSltZS1yyLucQg6 .cluster span{color:#333;}#mermaid-svg-KDSltZS1yyLucQg6 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-KDSltZS1yyLucQg6 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-KDSltZS1yyLucQg6 rect.text{fill:none;stroke-width:0;}#mermaid-svg-KDSltZS1yyLucQg6 .icon-shape,#mermaid-svg-KDSltZS1yyLucQg6 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-KDSltZS1yyLucQg6 .icon-shape p,#mermaid-svg-KDSltZS1yyLucQg6 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-KDSltZS1yyLucQg6 .icon-shape .label rect,#mermaid-svg-KDSltZS1yyLucQg6 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-KDSltZS1yyLucQg6 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-KDSltZS1yyLucQg6 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-KDSltZS1yyLucQg6 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 通过Props传递数据
用户点击/输入等事件触发回调
更新State
新的Props
父组件 例如GameScreen
子组件 Panel


第三章:关键交互实现解析

本章节深入几个典型场景,解释其背后选择的组件和技术原理。

场景 核心组件 原理解析
回合结束弹窗 AlertDialog + useToast Hook 1. 后端通过SSE(Server-Sent Events)推送 turnResult 事件。 2. 前端 Hook (useGameNotifications) 监听该事件。 3. 触发 AlertDialog 显示,同时使用 framer-motion 库实现淡入动画,吸引玩家注意但不突兀。
角色卡片 Card + Avatar + useMemo 1. 角色的属性(如攻击力、防御力)可能由基础值和多种装备、Buff加成计算得出。 2. useMemo Hook会"记住"计算结果,只有当依赖项(如装备、Buff列表)改变时才重新计算。 3. 原理:避免了在每一帧的渲染中都重复执行复杂的计算逻辑,显著提升性能。
实时通知 use-game-notifications Hook + EventSource 1. 使用浏览器的 EventSource API 建立一个到 /api/game/events 的持久连接,订阅服务器事件。 2. 当收到 turnResult 等通知时,Hook会更新全局状态管理库(如Zustand或Redux)。 3. 任何订阅了该状态片段的组件都会自动重新渲染,展示最新信息。这是一种发布-订阅模式的实践。
联盟管理 Dialog + Form + react-hook-form + zod 1. react-hook-form 高效管理表单状态和验证,减少不必要的重渲染。 2. zod 定义一个与后端期望数据结构匹配的"模式"(Schema),前端用其进行数据校验。 3. 原理 :前端校验能快速给予用户反馈(如"联盟名称不能为空"),避免无效请求发送到服务器,提升用户体验和系统效率。校验通过后,数据才被发送到 POST /api/game/alliances/join
成就展示 Accordion 1. Accordion(手风琴)组件用于将大量信息(成就列表)分组折叠。 2. 根据每个成就的解锁状态,动态赋予不同的样式类(如已解锁为 text-green-500,未解锁为 text-gray-500)。 3. 原理:折叠面板能有效节省屏幕空间,让玩家可以按需展开查看细节。通过视觉差异(灰色)和提示文字,清晰地展示进度和未完成目标。

第四章:样式与交互规范

为了确保UI的一致性和开发效率,我们需要建立一套详细的规范。

4.1 样式约定
  • 颜色系统 :在 tailwind.config.ts 中定义语义化颜色变量。

    javascript 复制代码
    // 示例
    colors: {
      primary: { DEFAULT: '#3B82F6', dark: '#1D4ED8' }, // 主要操作按钮
      secondary: '#10B981', // 次要信息、成功状态
      accent: '#8B5CF6',    // 强调高亮
      background: '#0F172A'  // 主背景色
    }

    原则 :永远使用 bg-primarytext-secondary 这类语义化类名,而不是硬编码颜色如 bg-blue-500。这样未来修改主题色只需改动一处配置文件。

  • 间距系统 :严格遵循 Tailwind 的预设间距比例(p-1, p-2, p-4...),避免使用 style={``{ margin: '13px' }} 等内联或硬编码值,保证全局间距的统一节奏。

  • 动画规范

    • :统一使用 framer-motion
    • 时长:200-300毫秒(ms),这是人类感知流畅动画的最舒适区间,过短会感觉生硬,过长则会令人焦躁。
    • 缓动曲线 :统一使用 ease-out,即动画开始时较快,结束时减速。这符合物理世界的物体运动直觉(启动快,停止慢)。
  • 字体 :中文使用 Noto Sans SC(思源黑体),英文字体为系统默认无衬线体。确保中西文混排时的视觉和谐。

4.2 国际化与本地化预备
  • 现状:当前专注中文市场。
  • 前瞻设计 :已预留完整的国际化框架。
    • 使用 next.config.ts 配置多语言路由。
    • 所有文本抽取到独立的JSON文件(zh-CN.json, en.json)。
    • 组件通过 useTranslation Hook 获取文本,完全与硬编码文本解耦。
    • 日期和数字通过 Intl API 动态格式化,并统一指定时区。
    • 原理:这种设计模式使得未来添加新语言时,无需修改任何组件逻辑,只需增加对应的翻译文件即可。这是一种"为变化而设计"的工程实践。

第五章:性能优化策略

高性能是良好用户体验的基石。我们采用"因地制宜"的策略,在不同层面进行优化。
#mermaid-svg-H5lcvAfGCllAAj3d{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-H5lcvAfGCllAAj3d .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-H5lcvAfGCllAAj3d .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-H5lcvAfGCllAAj3d .error-icon{fill:#552222;}#mermaid-svg-H5lcvAfGCllAAj3d .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-H5lcvAfGCllAAj3d .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-H5lcvAfGCllAAj3d .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-H5lcvAfGCllAAj3d .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-H5lcvAfGCllAAj3d .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-H5lcvAfGCllAAj3d .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-H5lcvAfGCllAAj3d .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-H5lcvAfGCllAAj3d .marker{fill:#333333;stroke:#333333;}#mermaid-svg-H5lcvAfGCllAAj3d .marker.cross{stroke:#333333;}#mermaid-svg-H5lcvAfGCllAAj3d svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-H5lcvAfGCllAAj3d p{margin:0;}#mermaid-svg-H5lcvAfGCllAAj3d .edge{stroke-width:3;}#mermaid-svg-H5lcvAfGCllAAj3d .section--1 rect,#mermaid-svg-H5lcvAfGCllAAj3d .section--1 path,#mermaid-svg-H5lcvAfGCllAAj3d .section--1 circle,#mermaid-svg-H5lcvAfGCllAAj3d .section--1 polygon,#mermaid-svg-H5lcvAfGCllAAj3d .section--1 path{fill:hsl(240, 100%, 76.2745098039%);}#mermaid-svg-H5lcvAfGCllAAj3d .section--1 text{fill:#ffffff;}#mermaid-svg-H5lcvAfGCllAAj3d .node-icon--1{font-size:40px;color:#ffffff;}#mermaid-svg-H5lcvAfGCllAAj3d .section-edge--1{stroke:hsl(240, 100%, 76.2745098039%);}#mermaid-svg-H5lcvAfGCllAAj3d .edge-depth--1{stroke-width:17;}#mermaid-svg-H5lcvAfGCllAAj3d .section--1 line{stroke:hsl(60, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-H5lcvAfGCllAAj3d .disabled,#mermaid-svg-H5lcvAfGCllAAj3d .disabled circle,#mermaid-svg-H5lcvAfGCllAAj3d .disabled text{fill:lightgray;}#mermaid-svg-H5lcvAfGCllAAj3d .disabled text{fill:#efefef;}#mermaid-svg-H5lcvAfGCllAAj3d .section-0 rect,#mermaid-svg-H5lcvAfGCllAAj3d .section-0 path,#mermaid-svg-H5lcvAfGCllAAj3d .section-0 circle,#mermaid-svg-H5lcvAfGCllAAj3d .section-0 polygon,#mermaid-svg-H5lcvAfGCllAAj3d .section-0 path{fill:hsl(60, 100%, 73.5294117647%);}#mermaid-svg-H5lcvAfGCllAAj3d .section-0 text{fill:black;}#mermaid-svg-H5lcvAfGCllAAj3d .node-icon-0{font-size:40px;color:black;}#mermaid-svg-H5lcvAfGCllAAj3d .section-edge-0{stroke:hsl(60, 100%, 73.5294117647%);}#mermaid-svg-H5lcvAfGCllAAj3d .edge-depth-0{stroke-width:14;}#mermaid-svg-H5lcvAfGCllAAj3d .section-0 line{stroke:hsl(240, 100%, 83.5294117647%);stroke-width:3;}#mermaid-svg-H5lcvAfGCllAAj3d .disabled,#mermaid-svg-H5lcvAfGCllAAj3d .disabled circle,#mermaid-svg-H5lcvAfGCllAAj3d .disabled text{fill:lightgray;}#mermaid-svg-H5lcvAfGCllAAj3d .disabled text{fill:#efefef;}#mermaid-svg-H5lcvAfGCllAAj3d .section-1 rect,#mermaid-svg-H5lcvAfGCllAAj3d .section-1 path,#mermaid-svg-H5lcvAfGCllAAj3d .section-1 circle,#mermaid-svg-H5lcvAfGCllAAj3d .section-1 polygon,#mermaid-svg-H5lcvAfGCllAAj3d .section-1 path{fill:hsl(80, 100%, 76.2745098039%);}#mermaid-svg-H5lcvAfGCllAAj3d .section-1 text{fill:black;}#mermaid-svg-H5lcvAfGCllAAj3d .node-icon-1{font-size:40px;color:black;}#mermaid-svg-H5lcvAfGCllAAj3d .section-edge-1{stroke:hsl(80, 100%, 76.2745098039%);}#mermaid-svg-H5lcvAfGCllAAj3d .edge-depth-1{stroke-width:11;}#mermaid-svg-H5lcvAfGCllAAj3d .section-1 line{stroke:hsl(260, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-H5lcvAfGCllAAj3d .disabled,#mermaid-svg-H5lcvAfGCllAAj3d .disabled circle,#mermaid-svg-H5lcvAfGCllAAj3d .disabled text{fill:lightgray;}#mermaid-svg-H5lcvAfGCllAAj3d .disabled text{fill:#efefef;}#mermaid-svg-H5lcvAfGCllAAj3d .section-2 rect,#mermaid-svg-H5lcvAfGCllAAj3d .section-2 path,#mermaid-svg-H5lcvAfGCllAAj3d .section-2 circle,#mermaid-svg-H5lcvAfGCllAAj3d .section-2 polygon,#mermaid-svg-H5lcvAfGCllAAj3d .section-2 path{fill:hsl(270, 100%, 76.2745098039%);}#mermaid-svg-H5lcvAfGCllAAj3d .section-2 text{fill:#ffffff;}#mermaid-svg-H5lcvAfGCllAAj3d .node-icon-2{font-size:40px;color:#ffffff;}#mermaid-svg-H5lcvAfGCllAAj3d .section-edge-2{stroke:hsl(270, 100%, 76.2745098039%);}#mermaid-svg-H5lcvAfGCllAAj3d .edge-depth-2{stroke-width:8;}#mermaid-svg-H5lcvAfGCllAAj3d .section-2 line{stroke:hsl(90, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-H5lcvAfGCllAAj3d .disabled,#mermaid-svg-H5lcvAfGCllAAj3d .disabled circle,#mermaid-svg-H5lcvAfGCllAAj3d .disabled text{fill:lightgray;}#mermaid-svg-H5lcvAfGCllAAj3d .disabled text{fill:#efefef;}#mermaid-svg-H5lcvAfGCllAAj3d .section-3 rect,#mermaid-svg-H5lcvAfGCllAAj3d .section-3 path,#mermaid-svg-H5lcvAfGCllAAj3d .section-3 circle,#mermaid-svg-H5lcvAfGCllAAj3d .section-3 polygon,#mermaid-svg-H5lcvAfGCllAAj3d .section-3 path{fill:hsl(300, 100%, 76.2745098039%);}#mermaid-svg-H5lcvAfGCllAAj3d .section-3 text{fill:black;}#mermaid-svg-H5lcvAfGCllAAj3d .node-icon-3{font-size:40px;color:black;}#mermaid-svg-H5lcvAfGCllAAj3d .section-edge-3{stroke:hsl(300, 100%, 76.2745098039%);}#mermaid-svg-H5lcvAfGCllAAj3d .edge-depth-3{stroke-width:5;}#mermaid-svg-H5lcvAfGCllAAj3d .section-3 line{stroke:hsl(120, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-H5lcvAfGCllAAj3d .disabled,#mermaid-svg-H5lcvAfGCllAAj3d .disabled circle,#mermaid-svg-H5lcvAfGCllAAj3d .disabled text{fill:lightgray;}#mermaid-svg-H5lcvAfGCllAAj3d .disabled text{fill:#efefef;}#mermaid-svg-H5lcvAfGCllAAj3d .section-4 rect,#mermaid-svg-H5lcvAfGCllAAj3d .section-4 path,#mermaid-svg-H5lcvAfGCllAAj3d .section-4 circle,#mermaid-svg-H5lcvAfGCllAAj3d .section-4 polygon,#mermaid-svg-H5lcvAfGCllAAj3d .section-4 path{fill:hsl(330, 100%, 76.2745098039%);}#mermaid-svg-H5lcvAfGCllAAj3d .section-4 text{fill:black;}#mermaid-svg-H5lcvAfGCllAAj3d .node-icon-4{font-size:40px;color:black;}#mermaid-svg-H5lcvAfGCllAAj3d .section-edge-4{stroke:hsl(330, 100%, 76.2745098039%);}#mermaid-svg-H5lcvAfGCllAAj3d .edge-depth-4{stroke-width:2;}#mermaid-svg-H5lcvAfGCllAAj3d .section-4 line{stroke:hsl(150, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-H5lcvAfGCllAAj3d .disabled,#mermaid-svg-H5lcvAfGCllAAj3d .disabled circle,#mermaid-svg-H5lcvAfGCllAAj3d .disabled text{fill:lightgray;}#mermaid-svg-H5lcvAfGCllAAj3d .disabled text{fill:#efefef;}#mermaid-svg-H5lcvAfGCllAAj3d .section-5 rect,#mermaid-svg-H5lcvAfGCllAAj3d .section-5 path,#mermaid-svg-H5lcvAfGCllAAj3d .section-5 circle,#mermaid-svg-H5lcvAfGCllAAj3d .section-5 polygon,#mermaid-svg-H5lcvAfGCllAAj3d .section-5 path{fill:hsl(0, 100%, 76.2745098039%);}#mermaid-svg-H5lcvAfGCllAAj3d .section-5 text{fill:black;}#mermaid-svg-H5lcvAfGCllAAj3d .node-icon-5{font-size:40px;color:black;}#mermaid-svg-H5lcvAfGCllAAj3d .section-edge-5{stroke:hsl(0, 100%, 76.2745098039%);}#mermaid-svg-H5lcvAfGCllAAj3d .edge-depth-5{stroke-width:-1;}#mermaid-svg-H5lcvAfGCllAAj3d .section-5 line{stroke:hsl(180, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-H5lcvAfGCllAAj3d .disabled,#mermaid-svg-H5lcvAfGCllAAj3d .disabled circle,#mermaid-svg-H5lcvAfGCllAAj3d .disabled text{fill:lightgray;}#mermaid-svg-H5lcvAfGCllAAj3d .disabled text{fill:#efefef;}#mermaid-svg-H5lcvAfGCllAAj3d .section-6 rect,#mermaid-svg-H5lcvAfGCllAAj3d .section-6 path,#mermaid-svg-H5lcvAfGCllAAj3d .section-6 circle,#mermaid-svg-H5lcvAfGCllAAj3d .section-6 polygon,#mermaid-svg-H5lcvAfGCllAAj3d .section-6 path{fill:hsl(30, 100%, 76.2745098039%);}#mermaid-svg-H5lcvAfGCllAAj3d .section-6 text{fill:black;}#mermaid-svg-H5lcvAfGCllAAj3d .node-icon-6{font-size:40px;color:black;}#mermaid-svg-H5lcvAfGCllAAj3d .section-edge-6{stroke:hsl(30, 100%, 76.2745098039%);}#mermaid-svg-H5lcvAfGCllAAj3d .edge-depth-6{stroke-width:-4;}#mermaid-svg-H5lcvAfGCllAAj3d .section-6 line{stroke:hsl(210, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-H5lcvAfGCllAAj3d .disabled,#mermaid-svg-H5lcvAfGCllAAj3d .disabled circle,#mermaid-svg-H5lcvAfGCllAAj3d .disabled text{fill:lightgray;}#mermaid-svg-H5lcvAfGCllAAj3d .disabled text{fill:#efefef;}#mermaid-svg-H5lcvAfGCllAAj3d .section-7 rect,#mermaid-svg-H5lcvAfGCllAAj3d .section-7 path,#mermaid-svg-H5lcvAfGCllAAj3d .section-7 circle,#mermaid-svg-H5lcvAfGCllAAj3d .section-7 polygon,#mermaid-svg-H5lcvAfGCllAAj3d .section-7 path{fill:hsl(90, 100%, 76.2745098039%);}#mermaid-svg-H5lcvAfGCllAAj3d .section-7 text{fill:black;}#mermaid-svg-H5lcvAfGCllAAj3d .node-icon-7{font-size:40px;color:black;}#mermaid-svg-H5lcvAfGCllAAj3d .section-edge-7{stroke:hsl(90, 100%, 76.2745098039%);}#mermaid-svg-H5lcvAfGCllAAj3d .edge-depth-7{stroke-width:-7;}#mermaid-svg-H5lcvAfGCllAAj3d .section-7 line{stroke:hsl(270, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-H5lcvAfGCllAAj3d .disabled,#mermaid-svg-H5lcvAfGCllAAj3d .disabled circle,#mermaid-svg-H5lcvAfGCllAAj3d .disabled text{fill:lightgray;}#mermaid-svg-H5lcvAfGCllAAj3d .disabled text{fill:#efefef;}#mermaid-svg-H5lcvAfGCllAAj3d .section-8 rect,#mermaid-svg-H5lcvAfGCllAAj3d .section-8 path,#mermaid-svg-H5lcvAfGCllAAj3d .section-8 circle,#mermaid-svg-H5lcvAfGCllAAj3d .section-8 polygon,#mermaid-svg-H5lcvAfGCllAAj3d .section-8 path{fill:hsl(150, 100%, 76.2745098039%);}#mermaid-svg-H5lcvAfGCllAAj3d .section-8 text{fill:black;}#mermaid-svg-H5lcvAfGCllAAj3d .node-icon-8{font-size:40px;color:black;}#mermaid-svg-H5lcvAfGCllAAj3d .section-edge-8{stroke:hsl(150, 100%, 76.2745098039%);}#mermaid-svg-H5lcvAfGCllAAj3d .edge-depth-8{stroke-width:-10;}#mermaid-svg-H5lcvAfGCllAAj3d .section-8 line{stroke:hsl(330, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-H5lcvAfGCllAAj3d .disabled,#mermaid-svg-H5lcvAfGCllAAj3d .disabled circle,#mermaid-svg-H5lcvAfGCllAAj3d .disabled text{fill:lightgray;}#mermaid-svg-H5lcvAfGCllAAj3d .disabled text{fill:#efefef;}#mermaid-svg-H5lcvAfGCllAAj3d .section-9 rect,#mermaid-svg-H5lcvAfGCllAAj3d .section-9 path,#mermaid-svg-H5lcvAfGCllAAj3d .section-9 circle,#mermaid-svg-H5lcvAfGCllAAj3d .section-9 polygon,#mermaid-svg-H5lcvAfGCllAAj3d .section-9 path{fill:hsl(180, 100%, 76.2745098039%);}#mermaid-svg-H5lcvAfGCllAAj3d .section-9 text{fill:black;}#mermaid-svg-H5lcvAfGCllAAj3d .node-icon-9{font-size:40px;color:black;}#mermaid-svg-H5lcvAfGCllAAj3d .section-edge-9{stroke:hsl(180, 100%, 76.2745098039%);}#mermaid-svg-H5lcvAfGCllAAj3d .edge-depth-9{stroke-width:-13;}#mermaid-svg-H5lcvAfGCllAAj3d .section-9 line{stroke:hsl(0, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-H5lcvAfGCllAAj3d .disabled,#mermaid-svg-H5lcvAfGCllAAj3d .disabled circle,#mermaid-svg-H5lcvAfGCllAAj3d .disabled text{fill:lightgray;}#mermaid-svg-H5lcvAfGCllAAj3d .disabled text{fill:#efefef;}#mermaid-svg-H5lcvAfGCllAAj3d .section-10 rect,#mermaid-svg-H5lcvAfGCllAAj3d .section-10 path,#mermaid-svg-H5lcvAfGCllAAj3d .section-10 circle,#mermaid-svg-H5lcvAfGCllAAj3d .section-10 polygon,#mermaid-svg-H5lcvAfGCllAAj3d .section-10 path{fill:hsl(210, 100%, 76.2745098039%);}#mermaid-svg-H5lcvAfGCllAAj3d .section-10 text{fill:black;}#mermaid-svg-H5lcvAfGCllAAj3d .node-icon-10{font-size:40px;color:black;}#mermaid-svg-H5lcvAfGCllAAj3d .section-edge-10{stroke:hsl(210, 100%, 76.2745098039%);}#mermaid-svg-H5lcvAfGCllAAj3d .edge-depth-10{stroke-width:-16;}#mermaid-svg-H5lcvAfGCllAAj3d .section-10 line{stroke:hsl(30, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-H5lcvAfGCllAAj3d .disabled,#mermaid-svg-H5lcvAfGCllAAj3d .disabled circle,#mermaid-svg-H5lcvAfGCllAAj3d .disabled text{fill:lightgray;}#mermaid-svg-H5lcvAfGCllAAj3d .disabled text{fill:#efefef;}#mermaid-svg-H5lcvAfGCllAAj3d .section-root rect,#mermaid-svg-H5lcvAfGCllAAj3d .section-root path,#mermaid-svg-H5lcvAfGCllAAj3d .section-root circle,#mermaid-svg-H5lcvAfGCllAAj3d .section-root polygon{fill:hsl(240, 100%, 46.2745098039%);}#mermaid-svg-H5lcvAfGCllAAj3d .section-root text{fill:#ffffff;}#mermaid-svg-H5lcvAfGCllAAj3d .section-root span{color:#ffffff;}#mermaid-svg-H5lcvAfGCllAAj3d .section-2 span{color:#ffffff;}#mermaid-svg-H5lcvAfGCllAAj3d .icon-container{height:100%;display:flex;justify-content:center;align-items:center;}#mermaid-svg-H5lcvAfGCllAAj3d .edge{fill:none;}#mermaid-svg-H5lcvAfGCllAAj3d .mindmap-node-label{dy:1em;alignment-baseline:middle;text-anchor:middle;dominant-baseline:middle;text-align:center;}#mermaid-svg-H5lcvAfGCllAAj3d :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 性能优化策略
加载时优化
懒加载
:非首屏Panel使用 dynamic import
图片优化
:Next.js Image组件 自动WebP与CDN
运行时优化
减少计算
:useMemo 缓存计算结果
减少渲染
:React.memo 缓存组件
useCallback 缓存函数
数据优化
缓存策略
:useSWR + LRU缓存 TTL5分钟

5.1 各优化点详解
  1. 懒加载 (Loading Optimization)

    • 目标:减小首屏加载的JavaScript包体积。
    • 原理 :对于不立即需要的组件(例如需要用户点击"政策"按钮后才出现的PolicyPanel),我们使用Next.js的 dynamic 函数来动态导入。这意味着该组件的代码会被分割成单独的块(chunk),只有在实际需要渲染时才会被浏览器下载和执行。
    • 实现const PolicyPanel = dynamic(() => import('./PolicyPanel'), { ssr: false });
  2. 数据缓存 (Data Caching)

    • 目标:避免频繁请求不常变化的游戏配置数据(如政策树、赛季活动列表)。
    • 原理 :使用 useSWR 这个数据获取库。它首先会返回缓存中的数据(如果有),然后发起请求去获取最新数据,并用新数据重新渲染。我们还配置了一个内部的LRU(最近最少使用)缓存,并为数据设置5分钟的有效期(TTL)。在这5分钟内,相同的请求将直接从缓存读取,不会产生网络开销。
  3. 渲染优化 (Rendering Optimization)

    • React.memo :对于像 ResourceBar(资源条)这样频繁因其内部数值变化而更新的组件,用 React.memo 包裹。这会让React"记住"组件上一次渲染时的 props。如果 props 没有改变,React将跳过本次渲染,直接复用上次的结果。
    • useCallback :当我们将一个函数作为 props 传递给子组件时,useCallback 可以确保该函数在组件的依赖项未改变时,其引用地址保持不变。结合 React.memo,可以避免子组件因收到一个"全新但内容相同"的函数而被迫重新渲染。
  4. 图片资源优化

    • 目标:优化图片加载速度和体积。
    • 原理 :使用 Next.js 的 Image 组件 。它会自动实现懒加载、响应式图片尺寸,并能根据浏览器支持情况自动将图片转换为更高效的 WebP 格式 ,同时配合 CDN(内容分发网络) 加速全球访问。

通过以上各章节的系统化阐述,我们不仅定义了UI"长什么样",更解释了它"为什么这样设计"。这为后续的开发、测试和维护提供了坚实、清晰的行动指南。

相关推荐
这是谁的博客?1 小时前
AI 领域精选新闻(2026-05-31)
人工智能·ai·技术新闻
海鸥-w1 小时前
前端学习python第二天手敲笔记整理
前端·python·学习
爱吃提升1 小时前
Figma 组件库搭建清单(含命名规范+常用组件模板)
前端·javascript·figma
veminhe1 小时前
trae下载安装与使用
人工智能
广州华水科技1 小时前
单北斗GNSS形变监测一体机在地质灾害监测中的应用与优势
前端
香蕉鼠片1 小时前
深度学习花卉识别笔记
人工智能·笔记·深度学习
HIT_Weston1 小时前
102、【Agent】【OpenCode】task 工具提示词(examples)
人工智能·agent·opencode
cyyt1 小时前
深度学习周报(5.25~5.31)
人工智能·深度学习
古韵1 小时前
从 Axios 到 alova:一个页面从 80 行到 5 行的故事
前端·后端