【Vue3】day77-boss-screen-b-vue

作者:mario 发布时间: 2026-09-03 阅读量:5 评论数:0

Day 77 · BOSS 战 — 屏 B Vue 版整屏迁移与"原生 vs Vue"对比报告

本周收官。今天把 Day 74-76 的零件装配成完整的屏 B(实时监控页)Vue 版:五卡片布局 + 五图表模块 + 容错表现,与原生版行为逐项对齐。然后做本周最有价值的事:整理"原生 vs Vue 对照表"与性能对比——这张表是博客《原生 vs Vue 开发大屏的体会》(Week 14 写)的骨架,也是面试聊框架选型的全部弹药。


目录


一、屏 B Vue 版:装配全清单

模块 原生版出处 Vue 版实现 组件/函数
卡片容器 Day 66 createTechCard TechCard Day 74
温度曲线 Day 56/69 LineChart TempLineChart Day 76
仪表盘组 Day 53 Gauge GaugeRow(VChart 包装) Day 76 模式
KPI 卡 Day 56 KpiCard + Day 67 翻牌 KpiTile(useRollingNumber) Day 73
能耗排名 Day 54 RankChart RankChart(VChart 包装) Day 76 模式
健康雷达 Day 54 RadarChart RadarChart(VChart 包装) Day 76 模式
数据管道 Day 65 Store + activate 协议 useScreenStore(常驻订阅) Day 75
布局 Day 56 grid CSS 同一份 CSS 原样迁移

新写的只有"翻译层":把 Store 数据转成 option 的 computed——这正是组件化的兑换率证明。


二、ScreenB.vue 的完整结构

<!-- src/screens/ScreenB.vue -->
<script setup lang="ts">
/**
 * 屏 B:实时监控(Vue 版)
 * 对照原生版 createMonitorScreen(Day 69):
 *   - 布局 DOM 手艺 → 模板 + 全局 grid 类
 *   - activate/deactivate 协议 → 常驻订阅 + 分频(Day 75 简化决策)
 *   - resize 手动调用 → VChart 内部 ResizeObserver
 */
import TechCard from "../components/TechCard.vue";
import TempLineChart from "../components/TempLineChart.vue";
import GaugeRow from "../components/GaugeRow.vue";
import KpiTile from "../components/KpiTile.vue";
import RankChart from "../components/RankChart.vue";
import RadarChart from "../components/RadarChart.vue";
import { useScreenStore } from "../composables/useScreenStore";

// 数据管道:一行拿到全局响应式状态(订阅/退订/分频全部内聚在钩子里)
const { latest, tempWindows, connState } = useScreenStore();
</script>

<template>
  <section class="screen screen-b">
    <!-- 五卡片布局:Day 56 的 grid 原样迁移(全局类,非 scoped) -->
    <div class="monitor-grid">
      <div class="monitor__kpi">
        <TechCard title="关键指标">
          <div class="kpi-row">
            <!-- KPI 翻牌:useRollingNumber 内聚在 KpiTile 里 -->
            <KpiTile label="今日产量" :value="latest?.kpi.output ?? 0" />
            <KpiTile label="平均温度" :value="latest?.kpi.avgTemp ?? 0" :decimals="1" />
            <KpiTile label="告警数" :value="latest?.kpi.alarmCount ?? 0" />
          </div>
        </TechCard>
      </div>

      <div class="monitor__line">
        <TechCard title="实时温度监控">
          <template #extra>
            <!-- 具名插槽:连接状态徽标(断线可见) -->
            <span class="badge" :data-state="connState">
              {{ connState === "connected" ? "实时" : "离线" }}
            </span>
          </template>
          <!-- 空状态:断线且无数据时的占位(VChart 的 empty 位) -->
          <TempLineChart
            :windows="tempWindows"
            :device-ids="['f1', 'f2']"
            :names="['1号炉', '2号炉']"
            :threshold="92"
            :empty="connState === 'closed' && !latest"
          />
        </TechCard>
      </div>

      <div class="monitor__rank">
        <TechCard title="车间能耗排名"><RankChart :snapshot="latest" /></TechCard>
      </div>
      <div class="monitor__radar">
        <TechCard title="设备健康度评估"><RadarChart :snapshot="latest" /></TechCard>
      </div>
      <div class="monitor__gauge">
        <TechCard title="关键设备仪表"><GaugeRow :snapshot="latest" /></TechCard>
      </div>
    </div>
  </section>
</template>

读这份模板的正确姿势:结构即文档——五卡片、每张卡的内容、数据来源,在模板里一眼可见。原生版要读 90 行 createMonitorScreen 才能拼出的画面,这里 40 行模板自解释。


三、行为对齐验收(对照原生版)

两个版本同时开(原生版 5174、Vue 版 5173,吃同一个数据服务器),逐项对照:

□ 数据一致性:两版曲线最新点相同、KPI 数值相同(同源同帧)
□ 分频表现:曲线持续推进、KPI 一秒一跳、仪表平滑摆动
□ 容错:杀服务器 → 徽标变"离线" + 曲线冻结;重启 → 两版同帧恢复
□ 视觉:并排截图逐像素对比(token 纪律的最终验收)
□ 动画:KPI 翻牌滚动节奏与原生版一致(同 easeOutCubic 800ms)
□ 性能:两版各跑 10 分钟 Performance 录制(第四节填表)

⚠️ 发现不一致时的排查顺序:先疑自己(翻译层的 computed 写错),再疑桥接(Day 75 镜像拷贝),最后才疑框架。今天修的每个不一致,都是对照表的一行素材


四、性能对比:原生 vs Vue

指标 原生版(Day 69 实测) Vue 版(今日实测) 结论
屏 B 稳态 fps(10 分钟) ___ ___
轮播整圈 fps ___ ___
JS Heap(30 分钟) ___ ___
屏 B 相关代码行数 ___ ___
首次可交互时间 ___ ___

预期与解读

  • fps 应持平(≥55):Vue 的响应式开销被"shallowRef + 引用替换"纪律压平——如果 Vue 版掉帧,先查有没有违反纪律(deep watch / reactive 大对象)
  • Heap 略高是正常的:Vue 运行时本身有底噪(约 100-200KB 级),关键是趋势稳定而非绝对值
  • 代码行数是最大赢面:翻译层模式让"每加一个图表"的边际成本从"一套 DOM 手艺"降到"一个 computed"

五、原生 vs Vue 对照表(本周笔记精华)

把 Day 71 起维护的 NOTES.md 汇总成表(≥12 行,Week 14 博客的骨架):

场景 原生版写法 Vue 版写法 兑换率/代价
缩放壳 addEventListener + 改 style ref + :style 绑定 3 行换 1 行
状态灯 回调改 classList :data-state 映射 免除手动同步
三屏互斥 ScreenRouter 类 v-show + current ref 路由逻辑暂留类(W12)
KPI 翻牌 工厂 + 手动 update/cancel useRollingNumber + watch 清理由框架兜底
卡片 createTechCard + append TechCard 标签 + slot 声明式自解释
告警列表 手动 DOM 增删 + 截断 v-for + computed 截断 diff 交给框架
曲线更新 update(windows) 手动调 computed option → VChart watch 自动联动
图表 resize 手动 resizeAll ResizeObserver 内聚 永不遗漏
图表销毁 手动 dispose 纪律 onUnmounted 结构保证 纪律变结构
数据订阅 activate/deactivate 成对 setup/unmount 成对 同上
排序/过滤 手动维护中间变量 computed 链惰性缓存 免除手动维护
调试 断点跟 DOM 操作 Vue Devtools 状态树 心智负担转移

表的第三列是灵魂:Vue 不是"更少的代码",是责任转移——手动同步的责任交给框架,你换来的是"状态即真相"的简单心智;代价是新增一层抽象的调试成本(Devtools 必须熟练)。


六、常见坑点

坑 1:两版数据不同帧

原生版与 Vue 版各自连了 WS(连接数翻倍)。检查:数据服务器控制台连接数应为 2(两个前端各一条);若 Vue 版内部出现第 3 条,说明有人绕过 useScreenStore 私连——Day 65 单连接纪律的回归检查。

坑 2:Vue 版 KPI 不更新但曲线正常

分频镜像正常、latest 镜像断了(或反之)。定位:useScreenStore 内四个镜像的赋值是否都写全——桥接层最容易漏 onSnapshot 里某个字段。

坑 3:并排对比时端口冲突

两个 Vite 工程默认都抢 5173。启动 Vue 版时指定端口(npm run dev -- --port 5173,原生版换 5174),或改 vite.config.ts 的 server.port 固化。

坑 4:对照表写成"Vue 真香"集

只记录变简单的地方,忽略变复杂的(如:多了构建层、调试要学 Devtools、deep watch 的隐性坑)——对比报告失去公信力。诚实的代价栏才是技术判断力的证明


七、自测挑战

T1 · 屏 B 完整迁移(90 分钟)

完成第一至二节全部模块(仪表/排名/雷达按 Day 76 模式包装 VChart),第三节对齐清单全过。本周核心作业

T2 · 性能对比填表(30 分钟)

第四节表格填满实测数据,与预期解读对照;如 Vue 版掉帧,用 Performance 火焰图定位违规(九成是 deep watch / reactive 大对象)。

T3 · 对照表成文(40 分钟)

第五节表格扩到 ≥ 12 行(本周 NOTES.md 汇总),每行必须能回答"为什么"——这是 Week 14 博客《原生 vs Vue 开发大屏的体会》的提纲验收。


八、总结

环节 要点
装配 零件全备,新写的只有 option 翻译层
对齐验收 双版并跑逐项对照——不一致即素材
性能 fps 应持平;Heap 底噪略高、趋势稳定即可
对照表 兑换率 + 代价两栏——责任转移是本质
收官 屏 B Vue 版完成,Week 12 攻 Router + Pinia

本周毕业标准:屏 B Vue 版与原生版行为对齐、性能达标、build 零报错、对照表 ≥ 12 行。达成即具备"用 Vue 干活"的能力——下周把手写的 ScreenRouter 和 ScreenStore 换成正规军(Vue Router + Pinia),并迁移屏 A 与屏 C。

评论