怎么用 这是格式与深度的参考,不是让你照抄的模板。数字全部是示例,直接抄进简历一问就穿——必须换成你自己的真实数据,而且要能说清是怎么测出来的。下面每个模块都有「数字是怎么测的」和「面试追问」两段,写不出那两段的模块就不要往简历上写

面试题库(电商 / 短视频 / 社区 / 本地生活 / 旅游 / 企业服务 多业务场景)

项目背景设定 即时配送的城市运营与运力看板,Vue 3 + TypeScript + Pinia + 地图 SDK + Canvas。使用者是城市运营与值班调度,他们要在午晚高峰盯住全城的运力与订单分布、调整城市配置、并在值班大屏上长时间常驻。派单调度与骑手位置流的服务端在 本地生活 · 后端 · 即时配送派单调度,商家侧后台在 商家管理后台
为什么选这三个模块 这一页在题库里对应「数据密集型监控界面」这一类,三个模块都是别的后台不会遇到的:地图上的海量点位(全城几万个骑手与订单同屏,用常规的标记点做法直接卡死,这是纯粹的渲染工程)、多维配置矩阵(城市 × 时段 × 品类三个维度交叉,配置项之间会冲突,而错配的影响是全城级的)、值班大屏的长时运行(页面要连续开几天不刷新,内存泄漏、断连、数据陈旧这些在普通页面里被「用户会刷新」掩盖掉的问题,在这里全部暴露)。第三块是这一页最特别的地方——它讨论的是「页面活很久」这个前提带来的一整类问题。

模块一:地图上的海量点位与实时更新

  1. 地图上的海量点位与实时更新(Canvas 图层替代 DOM 标记 + 网格聚合按层级切换 + 视野内裁剪 + 增量更新与位置插值 + 帧内合并重绘)★★★
    简历这样写 城市看板的海量点位地图渲染(Canvas 自绘图层替代逐点 DOM 标记 + 网格聚合并按缩放层级切换展示粒度 + 仅渲染当前视野内点位 + 位置变更走增量更新与插值动画 + 高频更新在一帧内合并重绘 + 点击命中用空间索引而非遍历):全城骑手与订单点位达数万量级,早期用地图 SDK 的逐点标记在数千点位时即出现明显卡顿并伴随内存快速增长;改为Canvas 自绘图层统一绘制,并按缩放层级做网格聚合(远景显示聚合数字、近景显示单点),同时只渲染当前视野内的点位;位置更新走增量而非全量替换并对移动做插值动画避免跳变;高频推送在一帧内合并为一次重绘而非逐条触发;点击命中改用空间索引查询替代全量遍历。改造后可流畅渲染的点位量级由数千提升到数万,高峰期高频位置推送下的重绘次数由与消息条数同阶降为与帧率同阶
    展开完整拆解
    为什么要这么设计

    看板的核心界面是一张城市地图,上面要同时显示骑手位置、待派订单、异常订单、商家门店,全城加起来是数万个点位,而且骑手位置是持续变化的。

    第一版直接用地图 SDK 的标记点 API:每个点位一个标记对象,本质上是一个 DOM 元素。测试时用某个区的数据(几百个点)完全正常,切到全城视图(几千个点)就开始卡,滚动缩放明显掉帧,开一段时间内存持续上涨。运营的应对是「只看一个区」,等于全城视图这个核心功能不可用。

    根因很清楚:几千个 DOM 元素的创建、样式计算、重排重绘,浏览器扛不住;而位置更新时每个标记都要改样式,等于每次推送触发几千次 DOM 操作。

    所以第一个决定是放弃逐点 DOM,改用 Canvas 自绘图层:在地图上叠一层 Canvas,所有点位由我们自己画。这样几万个点位只是一次 Canvas 绘制,成本从「点位数量级的 DOM 操作」降到「一次绘制」。代价是交互(点击、悬浮)要自己实现。

    但只换渲染方式还不够,因为几万个点位画出来本身也是视觉噪音——全城视图下点位密密麻麻,运营什么也看不出来。所以第二个设计是网格聚合:按缩放层级把点位聚合到网格里,远景显示「这个网格有 120 个骑手」的数字,放大到一定层级才显示单个点。这既解决了视觉噪音,也顺带把要绘制的图元数量降了一个量级

    第三个是视野裁剪:只渲染当前视野内的点位。地图缩放到某个区时,其他区的点位压根不需要参与绘制。这一条实现简单但收益直接。

    第四个是更新方式。骑手位置持续推送,第一版是每次推送替换整份数据然后全量重绘。问题有两个:一是位置跳变(上报间隔是几秒,点位会从 A 直接跳到 B,看起来很突兀),二是重绘次数和推送条数同阶(高峰期每秒几十条推送就是每秒几十次重绘)。

    改法是增量更新加插值动画加帧内合并:只更新变化的点位;移动做插值让点位平滑滑过去短时间内的多次更新在一帧内合并成一次重绘,让重绘次数与帧率同阶而不是与消息条数同阶

    最后一个容易被忽略的点:点击命中。用 Canvas 自绘之后没有 DOM 事件了,要自己判断点击到了哪个点位。朴素做法是遍历所有点位算距离,几万个点位每次点击都遍历一遍会卡顿。所以用空间索引(网格或四叉树),点击时只查附近网格里的点。

    整体链路
    问题规模(先认清量级) ├─ 骑手位置 + 待派订单 + 异常订单 + 门店 ├─ 全城合计数万个点位 └─ 骑手位置持续变化,高峰期每秒几十条推送 第一版为什么不行 ├─ 用地图 SDK 的标记点:每点一个 DOM 元素 ├─ 几百点(单区)正常,几千点(全城)就卡 ├─ 滚动缩放明显掉帧,内存持续上涨 └─ 运营应对:只看一个区 → 全城视图这个核心功能等于不可用 一、Canvas 自绘图层替代逐点 DOM ├─ 地图上叠一层 Canvas,所有点位自己画 ├─ 成本从「点位数量级的 DOM 操作」降到「一次绘制」 └─ 代价:点击与悬浮交互要自己实现(见最后一条) 二、网格聚合,按缩放层级切换粒度 ├─ 远景:显示「这个网格有 120 个骑手」 ├─ 近景:放大到一定层级才显示单点 ├─ 解决视觉噪音:几万个点密密麻麻,运营看不出东西 └─ 顺带把要绘制的图元数量降一个量级 三、视野裁剪 ├─ 只渲染当前视野内的点位 ├─ 缩放到某区时其他区的点不参与绘制 └─ 实现简单,收益直接 四、更新方式(三件事一起做) │ ├─ 增量更新,不全量替换 │ 第一版每次推送替换整份数据再全量重绘 │ ├─ 移动做插值动画 │ 上报间隔几秒,点位会从 A 直接跳到 B │ 插值让它平滑滑过去 │ └─ 帧内合并重绘 短时间内的多次更新合并为一次重绘 → 重绘次数从「与消息条数同阶」变成「与帧率同阶」 → 高峰期每秒几十条推送不再是每秒几十次重绘 五、点击命中(Canvas 自绘之后没有 DOM 事件了) ├─ 朴素做法:遍历所有点位算距离 │ 几万个点每次点击都遍历 → 卡顿 ├─ 改用空间索引(网格或四叉树) └─ 点击时只查附近网格里的点 分层与图例 ├─ 不同类型点位分图层,可独立开关 │ 运营常只想看「异常订单 + 骑手」 ├─ 图层开关要能减少绘制量,不只是隐藏 └─ 热力图与点位图互斥展示,避免叠加后看不清
    分步拆解
    1. 先确认量级,再选渲染方案。几百个点用 SDK 标记点没问题,几万个点必须自绘——这个判断要在动手前做,否则会走一遍我们的弯路。
    2. 用 Canvas 自绘图层替代逐点 DOM 标记。成本从「点位数量级的 DOM 操作」降到「一次绘制」。
    3. 接受自绘的代价:点击、悬浮、动画都要自己实现。这是一次性投入,但要事先知道。
    4. 按缩放层级做网格聚合:远景显示聚合数字,近景显示单点。这不只是性能优化,更是可读性的必需——几万个点密密麻麻,运营什么也看不出来。
    5. 只渲染当前视野内的点位。实现简单收益直接,缩放到某区时其他区的点压根不该参与绘制。
    6. 位置更新走增量,不要全量替换数据再重绘。全量替换会让所有点位重新计算,而实际只有少数在动。
    7. 移动做插值动画。上报间隔是几秒,不插值点位会从 A 直接跳到 B,视觉上很突兀,运营会以为数据有问题。
    8. 把短时间内的多次更新在一帧内合并为一次重绘。让重绘次数与帧率同阶而不是与消息条数同阶——这是高频推送场景的核心手段。
    9. 点击命中用空间索引,不要遍历所有点位。网格或四叉树,点击时只查附近网格。
    10. 不同类型的点位分图层并可独立开关。运营常常只想看「异常订单加骑手」,无关图层关掉。
    11. 图层关闭要真的减少绘制量,不能只是视觉隐藏。否则关了图层性能没改善,用户会觉得开关没用。
    12. 热力图与点位图互斥展示。叠加之后两层都看不清,互斥比让用户自己调透明度更实用
    13. Canvas 尺寸要处理设备像素比。不处理会在高分屏上模糊,这是自绘必须补的一步。
    14. 地图缩放平移过程中降级绘制。拖动时可以只画聚合、停下后再画细节,保证交互跟手比保证每一帧都精确更重要
    15. 监控自身的帧率与内存,超阈值主动降级。比如自动提高聚合层级并提示,主动降级优于卡死
    关键决策与取舍

    Canvas 自绘 vs 地图 SDK 标记点,这是这个模块的分水岭决策。SDK 标记点的好处很实在:交互(点击、悬浮、信息窗)、层级管理、动画全部现成,开发快。但它的成本随点位数线性增长,几千个点就到上限。自绘的代价是把交互全部重新实现一遍(点击命中、悬浮高亮、信息窗定位),这是实打实的工作量。判断依据是点位量级:几百个用 SDK,几万个必须自绘,中间的看更新频率——更新越频繁越应该自绘,因为 DOM 方案的更新成本更高。

    网格聚合既是性能手段也是可读性手段,而后者更重要。如果只从性能考虑,视野裁剪加 Canvas 已经能撑住几万个点;但几万个点画出来运营压根看不出信息——密密麻麻一片,既看不出哪里骑手多也看不出哪里订单堆积。所以聚合的第一目的是让数据可读,性能收益是附带的。这个认识影响了聚合层级的设计:层级切换的阈值是按「什么粒度对运营有意义」定的,不是按「多少个点会卡」定的。

    插值动画是必需的,不是锦上添花。看起来这只是视觉效果,但它解决了一个真实的可信度问题:点位跳变会让运营怀疑数据准确性(「这个骑手怎么一下跳这么远,是不是定位漂了」)。而实际是上报间隔的正常现象。插值让「离散上报」看起来像「连续移动」,减少了不必要的质疑。取舍是插值期间显示的位置是推算的、不是真实上报的,所以点击查看详情时要显示最后一次真实上报的时间,不能让推算位置被当成实测位置。

    踩过的坑:用 SDK 标记点,全城视图卡到不可用,运营只看单区。测试时用单区数据(几百点)完全正常,切全城(几千点)就卡,滚动缩放明显掉帧,开一段时间内存持续上涨。修法是 Canvas 自绘加聚合加视野裁剪。教训是:渲染方案的选择必须按生产数据量级验证,而不是按测试数据量级——几百和几千不是量的差别,是要换方案的差别。而且用户会用「少用」绕开性能问题(只看一个区),不会来提工单,所以要看功能使用数据:如果没人用全城视图,说明它不可用。

    踩过的坑二:位置推送逐条触发重绘,高峰期每秒几十次全量重绘。页面在午高峰明显卡顿,而单看每次重绘的耗时是可接受的,问题在次数。修法是帧内合并:把短时间内的多次更新合并成一次重绘教训是:高频数据驱动的渲染,要把「数据更新频率」和「渲染频率」解耦——数据可以来得很密,渲染只需要跟上帧率,这条在图表、地图、列表实时更新上都成立。

    踩过的坑三:Canvas 自绘后点击命中用遍历,点击有明显延迟。几万个点位每次点击都遍历算距离,点击后要等一下才弹出信息窗,运营以为没点上会再点一次。修法是建空间索引,点击时只查附近网格教训是:换自绘方案时要把「交互成本」一起算进去,只算绘制成本会漏掉命中检测这类隐藏工作量。

    踩过的坑四:位置跳变引发对数据准确性的质疑。运营反馈「骑手位置乱跳,定位是不是有问题」,实际是上报间隔几秒的正常表现,我们花了时间去排查一个不存在的定位问题。修法是插值动画,同时在详情里显示最后一次真实上报时间教训是:离散数据的可视化如果不做平滑,用户会把「采样间隔」误读为「数据异常」——而澄清这类误解的沟通成本远高于做插值。

    没做的部分:没上 WebGL。Canvas 二维绘制在我们的量级下够用,WebGL 能再提升一个量级但复杂度和兼容成本明显上升,当时判断收益不足。也没做骑手轨迹的回放(在地图上重放某个骑手一段时间的移动),需求存在但需要拉取轨迹数据,工作量主要在服务端。

    数字是怎么测的

    可流畅渲染的点位量级:最该报的数字。报「在指定机型与浏览器下,从多少点位开始掉帧」,改造前后对比必须写明机型、浏览器、点位类型构成、是否开启聚合——脱离这些条件的点位数没有意义。

    重绘次数:重绘次数与推送条数的关系变化——改造前与消息条数同阶(每秒几十条就是几十次),改造后与帧率同阶。报这个关系比报一个具体次数更能说明设计,因为它不依赖当时的推送量。

    点击响应:确定性验证加耗时——报点击到信息窗弹出的耗时,改造前是遍历几万个点(有可感知延迟),改造后是查附近网格。

    内存:连续运行一段时间后的内存曲线,改造前是持续上涨(几千个标记对象反复创建销毁),改造后平稳。用曲线形态描述而不是峰值数字。

    全城视图的使用率:这是个很有说服力的间接指标——改造前运营只看单区(用「少用」绕开性能问题),改造后全城视图被真实使用用户行为的变化比性能数字更能证明问题被解决。

    插值的副作用:要主动说明插值期间显示的是推算位置,并说明详情里会显示最后一次真实上报时间主动交代这个取舍比等面试官问出来好

    不要报「支持无限点位」或「渲染性能提升 10 倍」。前者不成立(Canvas 也有上限),后者分母不清(对比的是什么、什么条件下)。正确表述是「在什么条件下从多少点位提升到多少、重绘次数与推送条数解耦、内存曲线由上涨变平稳、点击命中从遍历改为空间索引」。

    面试追问
    Q:地图上要显示几万个点,怎么做到不卡? A:四件事一起做,而第一件是换渲染方式。我们第一版用地图 SDK 的标记点 API,每个点位一个标记对象、本质是一个 DOM 元素;测试用单区数据(几百点)完全正常,切到全城(几千点)就卡,滚动缩放明显掉帧、内存持续上涨,运营的应对是「只看一个区」,等于全城视图这个核心功能不可用。一、改用 Canvas 自绘图层:地图上叠一层 Canvas,所有点位自己画,成本从「点位数量级的 DOM 操作」降到「一次绘制」二、按缩放层级做网格聚合:远景显示「这个网格 120 个骑手」,近景才显示单点。三、只渲染视野内的点位。四、更新走增量并在帧内合并重绘。教训有两条:渲染方案必须按生产数据量级验证,几百和几千不是量的差别、是要换方案的差别;还有用户会用「少用」绕开性能问题而不来提工单,所以要看功能使用数据——如果没人用全城视图,说明它不可用。
    Q:网格聚合是为了性能吗? A:性能是附带收益,第一目的是可读性——这个区分很重要,因为它决定了聚合层级怎么定。如果只从性能考虑,Canvas 自绘加视野裁剪已经能撑住几万个点,不需要聚合。但几万个点画出来运营压根看不出信息:密密麻麻一片,既看不出哪里骑手多、也看不出哪里订单堆积。所以聚合是让数据可读的手段,顺带把绘制的图元数量降了一个量级。这个认识直接影响了设计:层级切换的阈值是按「什么粒度对运营有意义」定的,不是按「多少个点会卡」定的——比如全城看商圈粒度、区级看街道粒度,这是业务判断而不是性能判断。配套还有两个可读性设计不同类型点位分图层可独立开关(运营常只想看「异常订单加骑手」),而且关闭图层要真的减少绘制量而不只是视觉隐藏,否则用户会觉得开关没用;热力图与点位图互斥展示,叠加之后两层都看不清,互斥比让用户自己调透明度更实用。
    Q:骑手位置每秒推几十条,怎么处理? A:增量更新 + 插值动画 + 帧内合并,三件事解决三个不同的问题。增量更新:第一版每次推送替换整份数据再全量重绘,而实际只有少数点位在动帧内合并解决的是我们踩过的一个坑:页面在午高峰明显卡顿,而单看每次重绘的耗时是可接受的,问题在次数——每秒几十条推送就是每秒几十次重绘。教训是高频数据驱动的渲染要把「数据更新频率」和「渲染频率」解耦:数据可以来得很密,渲染只需要跟上帧率,这条在图表、地图、列表实时更新上都成立插值动画解决的是一个我没预料到的问题:运营反馈「骑手位置乱跳,定位是不是有问题」,实际是上报间隔几秒的正常表现,我们还花时间排查了一个不存在的定位问题教训是离散数据的可视化如果不做平滑,用户会把「采样间隔」误读为「数据异常」,而澄清这类误解的沟通成本远高于做插值。但插值有个取舍要主动交代:插值期间显示的是推算位置,所以点击详情时要显示最后一次真实上报时间,不能让推算位置被当成实测位置。
    Q:用 Canvas 自己画,点击事件怎么办? A:要自己做命中检测,而且必须用空间索引不能遍历——这是自绘方案容易漏算的隐藏成本。我们踩过:命中检测用遍历所有点位算距离,几万个点每次点击都遍历一遍,点击后要等一下才弹信息窗,运营以为没点上会再点一次。修法是建空间索引(网格或四叉树),点击时只查附近网格里的点教训是:换自绘方案时要把「交互成本」一起算进去——决策时我们只算了绘制成本,漏掉了点击命中、悬浮高亮、信息窗定位这些原本 SDK 免费提供的东西所以这个决策的完整判断依据是点位量级(几百个用 SDK、几万个必须自绘、中间的看更新频率——更新越频繁越该自绘,因为 DOM 方案的更新成本更高)加上愿不愿意付交互重实现的代价另外自绘还有两个必须补的细节Canvas 尺寸要处理设备像素比(不处理在高分屏上会模糊);地图拖动缩放过程中降级绘制(拖动时只画聚合、停下再画细节,保证交互跟手比保证每一帧都精确更重要)。

模块二:城市配置矩阵与生效预览

  1. 城市配置矩阵与生效预览(多维矩阵编辑 + 继承与覆盖可视 + 冲突与空洞检测 + 生效预览与影响面 + 灰度与定时生效)★★★
    简历这样写 城市运营配置的多维矩阵编辑与生效预览(城市 × 时段 × 品类三维矩阵化编辑 + 默认值继承与单元格覆盖的可视区分 + 覆盖冲突与规则空洞检测 + 生效预览按输入条件反查命中的配置 + 影响面量化确认 + 灰度比例与定时生效):配送费、起送价、时效承诺等配置需按城市、时段、品类三个维度交叉设置,早期以表单逐条配置既无法看出整体覆盖情况,也无法发现规则空洞(某组合无任何配置命中导致线上取到兜底值);改为矩阵化编辑可视区分「继承默认值」与「本单元格覆盖」,发布前做冲突与空洞检测(重叠规则、未被任何规则覆盖的组合);提供生效预览——运营输入具体的城市时段品类即反查将命中哪一条配置及其来源,替代原先只能上线后观察的做法;变更支持影响面量化确认、灰度比例与定时生效。上线后规则空洞由发布前检测暴露,配置错误由生效预览在发布前被发现
    展开完整拆解
    为什么要这么设计

    本地生活业务有一个根本特点:强地域性。配送费、起送价、时效承诺、补贴力度这些配置,每个城市不一样,同一城市不同时段不一样,同一时段不同品类还不一样(外卖和跑腿的时效承诺完全不同)。所以配置天然是三个维度的交叉

    第一版是逐条表单:新建一条配置,选城市、选时段、选品类、填值。功能上没问题,但运营用起来完全抓不住整体

    第一个问题是看不出覆盖情况。几十个城市 × 几个时段 × 几个品类,配置条数是几百条,运营在一个列表里翻,压根不知道「哪些组合已经配了、哪些没配」

    第二个问题更严重:规则空洞。某个组合(比如「某新开城市 + 夜间 + 跑腿」)没有任何配置命中,线上就取了系统兜底值——而兜底值往往是保守的默认配置,跟这个城市的实际运营策略不符。这类问题的发现路径通常是「运营发现某个城市的某个时段数据不对」,排查半天才找到是没配

    第三个问题是不知道最终生效的是哪一条。配置有默认值、有城市级、有城市加时段级,层层覆盖之后,运营问「上海晚高峰的外卖起送价到底是多少」,得靠人去推

    所以三个设计。

    第一是矩阵化编辑:把「城市 × 时段」摊成一张表格(品类切换),每个单元格就是一个配置值。这样覆盖情况一目了然——哪些格子有值、哪些是继承默认值、哪些是空的,看一眼就知道。而且要可视区分「继承」和「覆盖」(继承的用灰色显示默认值,覆盖的用正常颜色),这一点和连锁菜单的继承标记是同一个思路——用户看不见的继承关系等于不存在

    第二是发布前的冲突与空洞检测空洞是「某组合无任何规则命中」,冲突是「多条规则重叠且优先级不明确」。这两类都能在发布前算出来。空洞检测是这个模块收益最大的一项——它把一类「上线后才发现」的问题变成了发布前的一个列表。

    第三是生效预览:运营输入具体条件(上海、晚高峰、外卖),系统反查「将命中哪一条配置、这条配置来自哪一级」。这解决了「层层覆盖之后不知道最终生效什么」的问题。它的作用和营销规则的试算器完全一样:让配置结果可见。

    另外因为城市配置的影响面是全城级的(改错一个配送费,全城所有订单都受影响),所以变更要有影响面确认、灰度比例、定时生效——定时生效尤其必要,因为很多配置调整要在特定时间点生效(比如活动开始前)。

    整体链路
    配置的三个维度(本地生活的强地域性决定的) ├─ 城市:每个城市的成本、运力、竞争态势都不同 ├─ 时段:午晚高峰与平峰的策略完全不同 └─ 品类:外卖与跑腿的时效承诺不是一回事 第一版逐条表单的三个问题 ├─ 看不出覆盖情况 │ 几百条配置在一个列表里翻 │ 不知道哪些组合配了、哪些没配 ├─ 规则空洞(最严重) │ 某组合无任何配置命中 → 线上取系统兜底值 │ 兜底值与该城市实际策略不符 │ 发现路径:运营发现数据不对,排查半天才知道是没配 └─ 不知道最终生效哪一条 默认值 / 城市级 / 城市+时段级,层层覆盖 「上海晚高峰外卖起送价到底多少」要靠人推 一、矩阵化编辑(覆盖情况一目了然) ├─ 城市 × 时段 摊成表格,品类用切换 ├─ 每个单元格就是一个配置值 ├─ 可视区分:继承默认值(灰色)/ 本格覆盖(正常色) └─ 和连锁菜单的继承标记同一个思路 用户看不见的继承关系等于不存在 二、发布前检测(把上线后才发现变成发布前的列表) │ ├─ 空洞检测:某组合无任何规则命中 │ → 这一项是本模块收益最大的 │ → 它消掉了一整类「上线后才发现」的问题 │ ├─ 冲突检测:多条规则重叠且优先级不明确 │ └─ 检测结果给具体的组合清单,不只说「有问题」 三、生效预览(作用等同营销规则的试算器) ├─ 运营输入:上海 + 晚高峰 + 外卖 ├─ 系统反查:将命中哪一条配置、来自哪一级 ├─ 解决「层层覆盖后不知道最终生效什么」 └─ 核心是让配置结果可见,而不是让运营去推 变更控制(影响面是全城级的) ├─ 影响面量化确认 │ 将影响多少城市 / 多少品类 / 预计多少订单 ├─ 灰度比例:先放小比例流量 ├─ 定时生效:很多调整要在特定时间点生效 │ 活动开始前、政策调整日 └─ 变更留痕:谁在什么时候改了哪个单元格 矩阵编辑器的实现要点 ├─ 单元格用「城市 ID + 时段 ID」做稳定键 ├─ 支持整行整列批量填充(某城市所有时段、某时段所有城市) ├─ 支持从表格软件粘贴(和 SKU 矩阵同一个需求) └─ 只提交有变化的单元格,不全量提交 多人同时编辑配置是常态
    分步拆解
    1. 先认清配置是三个维度的交叉,不是一维列表。城市、时段、品类,这是本地生活强地域性决定的,不是我们把问题复杂化了。
    2. 把配置摊成矩阵:城市 × 时段 成表格,品类用切换。覆盖情况一目了然——第一版几百条配置在列表里翻,运营抓不住整体。
    3. 单元格要可视区分「继承默认值」和「本格覆盖」。继承的用灰色显示默认值、覆盖的用正常色。用户看不见的继承关系等于不存在——这和连锁菜单的继承标记是同一条。
    4. 单元格用「城市 ID + 时段 ID」做稳定键。不要用行列下标,城市列表会增删(新开城市、暂停运营)
    5. 支持整行整列批量填充。「某城市所有时段」「某时段所有城市」是最常见的两种批量操作。
    6. 支持从表格软件粘贴。运营做城市策略时本来就在表格里算,和 SKU 矩阵是完全同一个需求
    7. 只提交有变化的单元格,不做全量提交。多人同时编辑配置是常态,全量提交会互相覆盖。
    8. 做发布前的空洞检测:找出没有任何规则命中的组合。这是本模块收益最大的一项——它消掉了一整类「上线后才发现」的问题。
    9. 做冲突检测:找出重叠且优先级不明确的规则。重叠本身不一定是错,但优先级不明确就会导致行为不可预测
    10. 检测结果要给具体的组合清单,不能只说「有问题」。运营要能顺着清单一个个补。
    11. 做生效预览:输入具体条件,反查将命中哪一条配置及其来源。作用和营销规则试算器完全一样——让配置结果可见。
    12. 预览要说明「来自哪一级」。只说最终值不够,运营需要知道是默认值、城市级还是城市加时段级,才知道该改哪一层。
    13. 变更要有影响面量化确认。城市配置改错一个配送费全城所有订单都受影响,确认弹窗要报「将影响多少城市、多少品类、预计多少订单」。
    14. 支持灰度比例与定时生效。定时生效尤其必要——很多配置调整要在特定时间点生效(活动开始前、政策调整日)。
    15. 变更留痕到单元格级别。谁在什么时候改了哪个城市哪个时段的哪个值,出问题时要能精确定位
    关键决策与取舍

    矩阵化编辑 vs 逐条表单,代价是「维度多于二维时矩阵就摊不开了」。我们有三个维度,摊了两个(城市 × 时段)、第三个(品类)用切换。如果再增加一个维度(比如渠道),矩阵就不够用了。这是矩阵方案的天然上限。我们接受这个上限,理由是「三维是业务的稳定形态」——城市、时段、品类是本地生活配置的固有维度,短期不会增加。如果将来真要加维度,正确的做法不是继续叠切换器,而是回到规则列表加上强大的生效预览:矩阵的价值在于「一眼看出覆盖情况」,维度多了这个价值本身就消失了。

    空洞检测比冲突检测重要得多,这一点值得单独说。冲突(多条规则重叠)通常有明确的优先级规则兜着,行为虽然可能不符合预期但至少是确定的;而空洞是完全没有配置,系统悄悄用了兜底值——它是静默的、而且兜底值往往和该城市的实际策略差很远。「静默地用了一个错的默认值」比「两条规则冲突」危险,因为前者没有任何信号。这个判断和审批流程里「条件分支必须有默认出口」是同一个思路:要在结构上保证「任何输入都有明确的处理路径」。

    生效预览是「反查」而不是「模拟」。营销规则的试算器是模拟一次计算,这里的预览是反查规则命中链路——输入条件,输出「命中了哪一条、这条来自哪一级、上面还有哪些规则因为优先级低没命中」。后半句很重要:只告诉运营「最终值是 X」,他仍然不知道该改哪一层;告诉他「命中的是城市级规则,你改默认值没用」,他才能正确操作这和试算器「要给明细不能只给总价」是同一条原则。

    踩过的坑:新开城市漏配置,线上静默用了兜底值。某个新开城市的「夜间 + 跑腿」组合没有配置,系统用了保守的默认起送价,比该城市实际策略高很多,导致夜间跑腿单量异常低。运营是看数据发现异常,排查了半天才找到是没配。修法是发布前空洞检测,并且新城市开城时强制走一遍完整性检查教训是:有兜底值的配置系统里,「漏配」是静默故障——它不报错、不告警,只是行为不对,所以必须有独立的完整性检查去发现,不能指望它自己暴露。

    踩过的坑二:单元格用行列下标做键,城市列表变化后配置错位。某个城市暂停运营被从列表里移除,后面所有城市的配置整体串位了一行,是运营发现「这个城市的配送费怎么变了」才发现的。修法是用「城市 ID + 时段 ID」做稳定键教训和 SKU 矩阵、楼层排序完全一样:键不能来自数组位置——这个错误我在三个不同场景里都遇到过,已经成了我写任何列表渲染时的固定检查项。

    踩过的坑三:全量提交导致多人编辑互相覆盖。两个运营同时在矩阵里改不同城市的配置,后提交的把先提交的盖掉了,先提交那位以为自己没保存成功。修法是只提交有变化的单元格教训是:只要一份数据有多个并发编辑者就绝不能整体提交——这和直播中控台的协同覆盖是完全同一个问题,而配置类界面的并发编辑比想象中常见。

    踩过的坑四:配置立即生效,一次改错在几分钟内影响了全城订单。运营调整配送费时多输了一位,立即生效,全城订单的配送费瞬间异常,几分钟后才被发现并回滚。修法是影响面量化确认加灰度比例加定时生效教训是:影响面是全局的配置变更,不应该有「立即全量生效」这个默认行为——默认应该是灰度或定时,立即全量要作为一个显式选择。

    没做的部分:没做配置变更的效果归因(改了配送费之后单量的变化有多少是这次改动带来的)。运营很想要,但需要因果推断,不是前端能解决的。也没做配置的版本对比视图(两个版本的矩阵差异高亮),只有变更记录列表,这是个明确的体验缺口。

    数字是怎么测的

    规则空洞:空洞检测发现的组合数量,尤其是首次上线检测时发现的历史空洞数这个数字最有说服力——「首次运行检测发现 N 个历史存在的空洞组合」,说明这类问题一直存在只是没人知道。同时报新城市开城时检测拦下的漏配数

    配置错误的发现阶段:发现路径的变化而不只是数量——改造前是「运营看数据异常,排查半天定位到是没配」,改造后是「发布前检测列出清单」。路径的变化比数字更能说明问题。

    生效预览的使用:预览功能的使用次数与「预览后修改了配置」的比例。后者说明预览真的在纠正认知——如果运营预览之后经常发现和自己预期不同,正好证明「层层覆盖后靠人推是不可靠的」

    配置错位:确定性验证——增删城市、调整城市列表顺序,检查各单元格配置未串位。这是踩坑后固化的用例。

    并发编辑:确定性验证——两个会话分别修改矩阵的不同单元格并提交,检查双方修改都保留。改造前必然互相覆盖。

    影响面确认的作用:确认弹窗出现后被取消的次数,尤其是因为看到「影响多少城市」而取消的。这和商品批量操作的度量方式一致。

    不要报「配置准确率 100%」。配置的内容由运营决定,他配的值对不对不是系统能保证的正确表述是「空洞检测发现了多少历史空洞、配置错误的发现从上线后变为发布前、生效预览的使用与纠正率、错位与并发覆盖由确定性用例保证」——把系统的责任和运营的责任分清。

    面试追问
    Q:配置要按城市、时段、品类三个维度设置,界面怎么做? A:矩阵化编辑:把「城市 × 时段」摊成表格,品类用切换。第一版是逐条表单(新建配置、选城市选时段选品类、填值),功能没问题但运营完全抓不住整体——几百条配置在一个列表里翻,压根不知道哪些组合已经配了、哪些没配。矩阵化之后覆盖情况一目了然,而且单元格要可视区分「继承默认值」和「本格覆盖」(继承用灰色显示默认值、覆盖用正常色)——用户看不见的继承关系等于不存在,这和连锁菜单的继承标记是同一条。代价要说清:维度多于二维时矩阵就摊不开了。我们摊了两维、第三维用切换,再加一个维度(比如渠道)矩阵就不够用我们接受这个上限,理由是三维是业务的稳定形态如果将来真要加维度,正确做法不是继续叠切换器,而是回到规则列表加强大的生效预览——矩阵的价值在于「一眼看出覆盖情况」,维度多了这个价值本身就消失了。
    Q:有些组合忘了配怎么办? A:发布前做空洞检测,这是这个模块收益最大的一项。我们踩过很实在的坑:某个新开城市的「夜间 + 跑腿」组合没有配置,系统用了保守的默认起送价、比该城市实际策略高很多,导致夜间跑腿单量异常低运营是看数据发现异常、排查了半天才找到是没配教训是:有兜底值的配置系统里,「漏配」是静默故障——它不报错、不告警,只是行为不对,所以必须有独立的完整性检查去发现,不能指望它自己暴露。修法是发布前扫出「没有任何规则命中的组合」并给具体清单(不能只说「有问题」,运营要能顺着清单一个个补),另外新城市开城时强制走一遍完整性检查我想强调空洞检测比冲突检测重要得多:冲突(多条规则重叠)通常有优先级规则兜着,行为虽可能不符预期但至少是确定的而空洞是完全没配、系统悄悄用了兜底值,它是静默的「静默地用了一个错的默认值」比「两条规则冲突」危险,因为前者没有任何信号——这和审批流程里「条件分支必须有默认出口」是同一个思路。
    Q:配置层层覆盖,运营怎么知道最终生效的是哪一条? A:做「生效预览」——运营输入具体条件,系统反查将命中哪一条配置及其来源。比如输入「上海 + 晚高峰 + 外卖」,返回「命中城市加时段级规则,起送价 X」。关键是必须说明「来自哪一级」,而不只给最终值:只告诉运营结果是 X,他仍然不知道该改哪一层;告诉他「命中的是城市级规则,你改默认值没用」,他才能正确操作这和营销规则试算器「要给优惠明细不能只给总价」是同一条原则:诊断类工具要指出原因,不能只给结论。不过这里和试算器有个区别值得说:试算器是模拟一次计算,这里是反查规则命中链路——理想的输出还包括「上面还有哪些规则因为优先级低没命中」,这样运营能理解整个优先级结构。配套还有变更控制:城市配置的影响面是全城级的(改错一个配送费全城订单都受影响),所以要有影响面量化确认、灰度比例、定时生效——定时生效尤其必要,很多调整要在活动开始前那个时间点生效。
    Q:这个配置改错了影响很大,怎么防? A:核心判断是:影响面全局的配置变更,不应该有「立即全量生效」这个默认行为。我们踩过——运营调整配送费时多输了一位,立即生效,全城订单的配送费瞬间异常,几分钟后才被发现并回滚。修法三层:影响面量化确认(报「将影响多少城市、多少品类、预计多少订单」)、灰度比例(先放小流量观察)、定时生效而且默认应该是灰度或定时,「立即全量」要作为一个显式选择——把危险行为从默认路径上移走。另外两个踩过的坑也和这个模块的可靠性有关。一是单元格用行列下标做键:某个城市暂停运营被移出列表,后面所有城市的配置整体串位了一行——教训和 SKU 矩阵、楼层排序完全一样:键不能来自数组位置,这个错误我在三个不同场景都遇到过,已经成了固定检查项。二是全量提交导致多人编辑互相覆盖:两个运营同时改不同城市,后提交的把先提交的盖掉了;修法是只提交有变化的单元格——只要一份数据有多个并发编辑者就绝不能整体提交

模块三:值班大屏的长时运行稳定性

  1. 值班大屏的长时运行稳定性(内存泄漏治理 + 长连接自愈与全量对齐 + 数据陈旧显式提示 + 跨天与时钟处理 + 渲染降级与自恢复)★★★
    简历这样写 值班大屏的长时间常驻运行治理(定时器与监听器的注册即清理封装 + 数据窗口上限与历史丢弃 + 长连接指数退避重连与重连后全量对齐 + 数据陈旧时长显式提示与陈旧阈值告警 + 跨天边界与时钟漂移处理 + 帧率内存自监控与主动降级恢复):值班大屏需连续数日不刷新,普通页面中被「用户会刷新」掩盖的问题在此全部暴露:早期页面运行数小时后内存持续增长并出现明显掉帧,长连接断开后界面仍显示旧数据而无任何提示;因此把定时器与事件监听封装为注册即清理并对时序数据设窗口上限主动丢弃历史,长连接采用指数退避重连并在重连后做一次全量对齐(而非仅依赖增量推送),界面显式展示各数据块的最后更新时间并在超过陈旧阈值时置为警示态;同时处理跨天边界(「今日数据」在零点需自动切换统计周期)与时钟漂移(一律以服务端时间为准);页面自监控帧率与内存并在超阈值时主动降级。改造后连续运行数日的内存曲线由持续上涨变为平稳,「大屏数字不动但没人知道」的情况由陈旧提示消除
    展开完整拆解
    为什么要这么设计

    值班大屏有一个和所有普通页面都不同的前提:它要连续开几天不刷新。挂在调度室的墙上,值班的人换班但页面不换。

    这个前提本身就是一整类问题的来源。普通页面里很多问题被「用户会刷新」掩盖掉了——内存泄漏无所谓(用户几分钟就换页面了)、长连接断了无所谓(下次刷新就重连了)、数据陈旧无所谓(刷新一下就好了)。大屏把这些掩盖全部撕掉。

    我们踩到的问题按暴露顺序是这样的。

    第一是内存持续增长。页面运行几小时后开始明显掉帧,一天之后浏览器基本没法用了。查下来是三类泄漏叠加:定时器没清理(组件卸载了但 setInterval 还在跑)、事件监听没解绑(window 上的 resize、visibilitychange)、时序数据无上限累积(趋势图的数据点只往数组里 push,一天下来几十万个点)。

    前两类的解法是封装成「注册即清理」的组合函数——不是靠人记得在 onUnmounted 里清理,而是让清理在封装内部自动发生,业务代码不可能忘。第三类的解法是给时序数据设窗口上限,超出的主动丢弃

    第二是长连接断了没人知道。某次网络抖动后长连接断开,页面上的数字就停在了断开那一刻,看起来完全正常——值班的人以为是真的没有新订单。这个问题特别危险,因为大屏的作用就是让人「看一眼就知道现在什么情况」,而它在撒谎

    解法有三层:长连接指数退避重连(不能断了就不管);重连后做一次全量对齐(不能只依赖后续增量,断开期间的变化要补回来);最关键的是界面上显式展示每个数据块的最后更新时间,超过陈旧阈值就把它置为警示态——让「数据是旧的」这件事本身变成可见信息。

    第三是跨天问题。大屏上有很多「今日单量」「今日准时率」这类指标。页面开了三天没刷新,「今日」这个概念在零点应该切换,但第一版是页面加载时算好日期就不动了——结果第二天的大屏还在显示前一天的日期范围,数字停着不涨。

    解法是显式处理跨天边界:到零点主动切换统计周期并重新拉数据。而且时间一律以服务端为准——大屏机器的时钟可能漂移,用本地时间判断跨天会在错误的时刻切换。

    第四是渲染降级后不会自己恢复。我们做了「帧率过低时自动降级」(减少动画、提高地图聚合层级),但降级之后没有恢复逻辑,一次瞬时卡顿之后大屏就永久停在降级状态,值班的人看到的一直是粗粒度的画面。所以降级必须可逆,指标恢复后要自动升回去

    整体链路
    特殊前提(这一条决定了整个模块) ├─ 大屏要连续开几天不刷新,挂在调度室墙上 └─ 普通页面里很多问题被「用户会刷新」掩盖掉了 内存泄漏无所谓(几分钟就换页面) 长连接断了无所谓(下次刷新就重连) 数据陈旧无所谓(刷新一下就好) → 大屏把这些掩盖全部撕掉 一、内存泄漏(三类叠加) │ ├─ 定时器没清理:组件卸载了 setInterval 还在跑 ├─ 事件监听没解绑:window 上的 resize、visibilitychange ├─ 时序数据无上限累积 │ 趋势图数据点只往数组里 push │ 一天下来几十万个点 │ ├─ 前两类的解法:封装成「注册即清理」的组合函数 │ 不靠人记得在 onUnmounted 里清理 │ 让清理在封装内部自动发生 │ → 业务代码不可能忘 │ └─ 第三类的解法:时序数据设窗口上限,超出主动丢弃 二、长连接断了没人知道(最危险的一个) │ ├─ 现象:网络抖动后长连接断开 │ 页面数字停在断开那一刻,看起来完全正常 │ 值班的人以为是真的没有新订单 │ → 大屏的作用是「看一眼就知道现在什么情况」 │ → 而它在撒谎 │ ├─ 第一层:指数退避重连,断了不能不管 │ ├─ 第二层:重连后做一次全量对齐 │ 不能只依赖后续增量 │ 断开期间的变化要补回来 │ └─ 第三层(最关键):让「数据是旧的」变成可见信息 每个数据块显示最后更新时间 超过陈旧阈值 → 置为警示态 → 宁可显示「数据已停止更新 3 分钟」 → 也不要显示一个看起来正常的旧数字 三、跨天与时钟 │ ├─ 「今日单量」「今日准时率」这类指标 │ 页面开了三天没刷新 │ 第一版加载时算好日期就不动了 │ → 第二天大屏还显示前一天的日期范围,数字停着 │ ├─ 到零点主动切换统计周期并重新拉数据 │ └─ 时间一律以服务端为准 大屏机器时钟可能漂移 用本地时间判断跨天会在错误的时刻切换 四、渲染降级必须可逆 ├─ 帧率过低时自动降级:减少动画、提高地图聚合层级 ├─ 第一版降级后没有恢复逻辑 │ 一次瞬时卡顿之后永久停在降级状态 │ 值班看到的一直是粗粒度画面 └─ 指标恢复后要自动升回去 其他长时运行要处理的 ├─ 页面自监控帧率与内存,超阈值降级并上报 ├─ 前端版本更新的提示(大屏不会主动刷新) │ 有新版本时提示值班在合适时机刷新 ├─ 屏幕常亮与防休眠 └─ 异常时的自恢复:连续错误达阈值后自动重载页面 这是最后的兜底,但要有次数限制防止循环重载
    分步拆解
    1. 先意识到「页面活很久」本身就是一个前提,会带来一整类问题。普通页面里很多问题被「用户会刷新」掩盖掉了,大屏把这些掩盖全部撕掉。
    2. 把定时器与事件监听封装成「注册即清理」的组合函数。不要靠人记得在 onUnmounted 里清理——让清理在封装内部自动发生,业务代码不可能忘。
    3. 给所有时序数据设窗口上限,超出的主动丢弃。趋势图数据点只 push 不丢,一天下来几十万个点
    4. 长连接要指数退避重连。断了不能不管——这在普通页面里可以靠用户刷新兜住,大屏没有这个兜底。
    5. 重连后必须做一次全量对齐。不能只依赖后续增量推送,断开期间的变化要补回来。
    6. 最关键的一条:让「数据是旧的」变成可见信息。每个数据块显示最后更新时间,超过陈旧阈值就置为警示态
    7. 宁可显示「数据已停止更新 3 分钟」,也不要显示一个看起来正常的旧数字。大屏的作用是让人看一眼就知道现状,显示旧数据等于在撒谎。
    8. 陈旧阈值要按数据块分别设。实时订单量几十秒没更新就是异常,而日报类指标几分钟不更新是正常的。
    9. 显式处理跨天边界:到零点主动切换统计周期并重新拉数据。第一版加载时算好日期就不动,第二天大屏还显示前一天的范围
    10. 时间一律以服务端为准,不用大屏机器的本地时间。大屏机器时钟可能漂移,用本地时间判断跨天会在错误的时刻切换。
    11. 渲染降级必须可逆。第一版降级后没有恢复逻辑,一次瞬时卡顿之后永久停在降级状态
    12. 页面自监控帧率与内存,超阈值降级并上报。上报很重要——大屏出问题时没人会去看控制台
    13. 前端版本更新要有提示。大屏不会主动刷新,发了新版本它还在跑旧代码,要提示值班在合适时机刷新。
    14. 处理屏幕常亮与防休眠。大屏休眠了等于没有大屏。
    15. 做最后的兜底:连续错误达阈值后自动重载页面,但要限制重载次数。不限次数会在持续故障时循环重载,反而更糟。
    关键决策与取舍

    「让数据陈旧变成可见信息」是这个模块最重要的一条,它的思路是「宁可显示难看的真相,也不要显示好看的假象」。不做陈旧提示的界面看起来更干净——数字就在那里,没有多余的时间戳和警示色。但它在数据停止更新时会持续给出一个错误的印象,而大屏的唯一价值就是「看一眼知道现状」。取舍是界面上多了一些时间戳和状态色,换来的是界面不会撒谎。这个判断和「乐观更新失败必须回滚告知」「降级期间要明确提示连接不稳定」是完全同一条线:在监控与安全类界面上,假的正常比明确的异常危险得多。

    清理逻辑靠封装而不是靠规范。另一种做法是定一个规范「所有 setInterval 必须在 onUnmounted 里清理」并靠代码评审保证。但规范会被忘,尤其是在一个团队里多人开发多个大屏组件时。封装成组合函数(注册时自动登记清理)之后,业务代码写不出泄漏的形态取舍是团队要接受「不直接用原生 API」这个约束,缓解手段是封装要足够薄、语义要和原生一致,不要变成一个需要学习的新东西。判断依据是:能靠机制保证的事情不要靠规范保证。

    自动重载作为最后兜底,但必须限次。连续错误后自动重载能让大屏从很多未知故障里自恢复,这在无人值守的场景下很有价值。但如果故障是持续的(比如接口一直 500),不限次数的重载会变成循环刷新,既解决不了问题又让人无法在页面上看到任何错误信息。所以限制重载次数,超过之后停下来并显示明确的错误信息——让故障可见比让页面一直在尝试自救更有用。

    踩过的坑:三类内存泄漏叠加,页面运行一天后浏览器基本没法用。定时器没清理、window 事件监听没解绑、趋势图数据点无上限累积。这三个单独看都不严重,叠加在「连续运行几天」这个前提下就是致命的。修法是注册即清理的封装加数据窗口上限教训是:泄漏问题的严重性取决于页面的生命周期——同样的代码在一个几分钟就关掉的页面里毫无问题,在常驻页面里是致命的。所以「这个页面会活多久」应该是设计时就明确的输入。

    踩过的坑二:长连接断开后大屏显示旧数据,值班以为没有新订单。网络抖动导致连接断开,页面数字停在断开那一刻、看起来完全正常,值班的人一直没意识到,直到有商家打电话问订单为什么没派。这是这个模块里后果最严重的一个坑。修法是退避重连加重连后全量对齐加陈旧提示教训是:监控界面的「无变化」有两种含义——「真的没变」和「没收到更新」,界面必须能区分这两者,否则它在最需要它的时候提供的是错误信息。

    踩过的坑三:跨天不切换,第二天大屏还显示前一天的数据。页面加载时算好「今日」的日期范围就不动了,值班换班后看到的「今日单量」是昨天的,而且数字停着不涨。修法是到零点主动切换统计周期并重新拉数据,且时间以服务端为准教训是:任何「相对当前时间」的概念在长生命周期页面里都必须动态重算——「今日」「最近一小时」「本周」这些在页面加载时算一次是不够的。

    踩过的坑四:降级之后不会恢复,大屏永久停在粗粒度状态。一次瞬时卡顿触发降级(减少动画、提高聚合层级),之后帧率恢复了但降级状态没撤,值班看到的一直是粗粒度画面并且以为大屏就是这样。修法是降级可逆,指标恢复后自动升回教训是:任何自动降级都必须配自动恢复,只降不升的降级机制会让系统单向劣化到最低配置。

    没做的部分:没做大屏的多屏协同(几块屏拼成一个大画面,需要跨屏同步视口与时间轴)。需求提过,但涉及多端同步,复杂度不低。也没做无人值守时的异常自动通知(大屏检测到自身异常时主动通知值班手机),只做了上报到监控系统。

    数字是怎么测的

    长时运行内存:最该报的数字,而且要报连续运行的时长与内存曲线形态——「连续运行数日内存曲线平稳」,改造前是「运行数小时后明显掉帧、一天后基本不可用」。用曲线形态与可运行时长描述,比给一个内存峰值有说服力。

    数据陈旧的暴露:这个用「大屏数字不动但没人知道」这类事件的次数来度量,改造前后对比。最有说服力的是那次具体案例:长连接断开后值班一直没意识到,直到商家打电话问订单为什么没派。

    重连与对齐:确定性验证——人为断网后恢复,检查连接自动恢复、断开期间的数据被补齐、陈旧提示在断开期间正确显示并在恢复后消失。这三项要分别验。

    跨天切换:确定性验证——把页面挂过零点,检查「今日」指标自动切换到新的一天并重新拉数这个用例只能靠改系统时间或等真实零点来验,要说明怎么验的。

    降级与恢复:确定性验证——人为制造卡顿触发降级,恢复后检查自动升回原有渲染精度。这是踩坑后固化的用例。

    自动重载的限次:确定性验证——模拟持续故障,检查重载次数达上限后停止并显示明确错误信息,而不是循环刷新。

    不要报「大屏可用性 99.99%」或「零故障运行」。大屏的可用性依赖网络、接口、机器本身,而且它的故障模式很多是「显示了但不对」而不是「打不开」,用可用性数字表达不了。正确表述是「连续运行时长与内存曲线、陈旧数据由显式提示暴露、重连与全量对齐由确定性用例保证、降级可自动恢复、自动重载限次后停止并暴露错误」——这些都是可核查的具体机制。

    面试追问
    Q:一个页面要连续开几天不刷新,要注意什么? A:先说一句认知:普通页面里很多问题被「用户会刷新」掩盖掉了,常驻页面把这些掩盖全部撕掉。内存泄漏无所谓(用户几分钟就换页面)、长连接断了无所谓(下次刷新就重连)、数据陈旧无所谓(刷新一下就好)——这些前提在大屏上全部不成立。我们踩到的内存问题是三类泄漏叠加定时器没清理(组件卸载了 setInterval 还在跑)、window 事件监听没解绑时序数据无上限累积(趋势图数据点只 push,一天几十万个点)。这三个单独看都不严重,叠加在「连续运行几天」这个前提下就是致命的——页面运行数小时后明显掉帧、一天后浏览器基本没法用。修法是把定时器与监听封装成「注册即清理」的组合函数(不靠人记得清理,让业务代码写不出泄漏的形态),加上时序数据设窗口上限主动丢弃教训是:泄漏问题的严重性取决于页面的生命周期——同样的代码在几分钟就关掉的页面里毫无问题,所以「这个页面会活多久」应该是设计时就明确的输入。
    Q:长连接断了,大屏会怎样? A:第一版会显示一个看起来完全正常的旧数字,这是这个模块后果最严重的坑。网络抖动导致连接断开,页面数字停在断开那一刻,值班的人以为是真的没有新订单,一直没意识到,直到有商家打电话问订单为什么没派大屏的唯一价值是「看一眼就知道现在什么情况」,而它在撒谎。解法三层:指数退避重连(断了不能不管,普通页面能靠用户刷新兜住、大屏没有这个兜底);重连后做一次全量对齐(不能只依赖后续增量,断开期间的变化要补回来);最关键的第三层是让「数据是旧的」变成可见信息——每个数据块显示最后更新时间,超过陈旧阈值就置为警示态,而且阈值要按数据块分别设(实时订单量几十秒不更新就是异常,日报类指标几分钟不更新是正常的)。教训是:监控界面的「无变化」有两种含义——「真的没变」和「没收到更新」,界面必须能区分这两者,否则它在最需要它的时候给的是错误信息。宁可显示「数据已停止更新 3 分钟」,也不要显示一个看起来正常的旧数字。
    Q:大屏上有「今日单量」,页面开了三天会怎样? A:第一版会一直显示第一天的数据,而且数字停着不涨。因为「今日」的日期范围是页面加载时算好就不动了值班换班后看到的「今日单量」其实是前天的,还以为是今天真的没单。修法是到零点主动切换统计周期并重新拉数据而且时间一律以服务端为准,不用大屏机器的本地时间——大屏机器的时钟可能漂移,用本地时间判断跨天会在错误的时刻切换教训可以推广:任何「相对当前时间」的概念在长生命周期页面里都必须动态重算——「今日」「最近一小时」「本周」这些在页面加载时算一次是不够的。这一条和另一个坑是同一类:降级之后不会恢复。我们做了「帧率过低时自动降级」(减少动画、提高地图聚合层级),但一次瞬时卡顿触发降级后没有恢复逻辑,帧率恢复了降级状态没撤,值班看到的一直是粗粒度画面并且以为大屏就是这样教训是任何自动降级都必须配自动恢复,只降不升的机制会让系统单向劣化到最低配置。
    Q:大屏没人管,出问题了怎么自恢复? A:做自动重载兜底,但必须限次——这个限次比自动重载本身更重要。连续错误达到阈值后自动重载页面,能让大屏从很多未知故障里自恢复,无人值守场景下很有价值。但如果故障是持续的(比如接口一直 500),不限次数的重载会变成循环刷新,既解决不了问题,又让人无法在页面上看到任何错误信息。所以超过重载次数上限后要停下来并显示明确的错误信息——让故障可见比让页面一直在尝试自救更有用。配套还有三件长时运行才需要考虑的事。一是页面自监控帧率与内存并上报——上报很关键,因为大屏出问题时没人会去看控制台二是前端版本更新提示大屏不会主动刷新,发了新版本它还在跑旧代码,所以要检测到新版本时提示值班在合适时机刷新(不能自动刷,可能正在处理紧急情况)。三是屏幕常亮与防休眠——大屏休眠了等于没有大屏,这个细节很容易被漏掉,而它是这个场景的基本要求。

没有匹配的内容,换个关键词试试。

项目拆解 · 城市运营与运力看板(Vue)· 共 3 个模块 · 数字均为示例,需替换成自己项目的真实数据