编码、架构与项目 · KNOWLEDGE CHAPTER
K29

界面状态与大数据

界面上的节点只是数据状态的一扇窗口。输入、请求、选择、编辑历史与可见行各有不同身份和寿命;把它们都塞进当前 DOM,就会在乱序响应、排序、虚拟卸载或撤销分支时失去用户意图。

编码、架构与项目2 道章节练习5 道主练关联

核心模型

界面上的节点只是数据状态的一扇窗口。输入、请求、选择、编辑历史与可见行各有不同身份和寿命;把它们都塞进当前 DOM,就会在乱序响应、排序、虚拟卸载或撤销分支时失去用户意图。

原理与机制

输入不等于已提交查询

联想框至少区分输入值、组合输入状态、提交查询、候选、高亮和请求状态。中文 composition 期间通常暂停远程查询,结束后按策略触发;防抖只减少频率。每轮请求取得单调编号并取消旧工作,结果、错误和 loading 都需归属校验,A→B→A 不能只比较文字。空输入清空候选,失败保留可理解的重试入口;缓存键带语言及权限作用域,容量有界。这样输入反馈可以立即发生,而结果更新遵循独立的一致性规则。

可访问交互必须与实际状态一致

combobox 的展开状态、受控 listbox 和活动选项并非装饰属性。上下键移动、Enter 选择、Escape 收起及焦点保持都要定义;aria-activedescendant 指向的选项必须真的存在。候选尚未到达或虚拟行被卸载时,不能保留悬空引用,也不能把焦点悄悄交给 body。按角色定位的自动化检查再配合读屏、中文输入和放大验证,才能同时证明“正确选择”和“用户知道选了谁”。

把查询规模与渲染规模分别收敛

十万条数据先区分下载、筛选、排序、组件更新和布局瓶颈。服务端游标分页减少传输与计算,虚拟化减少 DOM,两者可以组合,却不能互相替代。定高列表将像素映射到左闭右开的索引窗口,再加少量 overscan;可变高度需测量缓存及累计偏移。上方行变高时保存顶部锚点行 ID 与行内偏移,重新求锚点位置并补偿,单存 scrollTop 无法维持同一阅读位置。

排序与选择依赖稳定的业务身份

分数为空应明确用 null,不以真假判断,0 仍是有效分数。多字段比较先处理缺失,再比较分数、指定 locale 的姓名;Collator 复用,比较器返回有符号数。现代稳定排序只保证当前输入中等值项的相对顺序,跨页仍需服务端统一排序和唯一 ID 兜底。编辑草稿、选择与校验结果以行 ID 存在数据层,不能随窗口卸载消失;跨页全选要说清是已加载行还是当前筛选全集。

撤销记录的是事务,而不是网络重播

历史可用 past、present、future 三组不可变快照表达,撤销和重做只移动快照;撤销后新编辑清空 future,因为形成了新分支。对象若原地修改,所有引用它的历史也会变,须使用不可变更新、结构共享或补丁。拖拽从起点到终点可合为一个事务,中间移动仅预览,取消回到起点;不应把100次 move 都塞进历史再猜着删除。远端保存的撤销是新业务操作,需要权限、版本和冲突处理,不能直接重发旧请求。

最小示例

javascript
javascript
// 输入契约:count/overscan 非负整数,其余尺寸合法
function windowRange(count, rowHeight, height, scrollTop,
                     overscan = 1) {
  const total = count * rowHeight;
  const top = Math.max(0, Math.min(scrollTop,
    Math.max(0, total - height)));
  const start = Math.max(0,
    Math.floor(top / rowHeight) - overscan);
  const end = Math.min(count,
    Math.ceil((top + height) / rowHeight) + overscan);
  return { start, end, offset: start * rowHeight, total };
}
console.log(windowRange(100, 20, 100, 40));

结果窗口为 [1,8),offset=20,总高2000;实际可见从索引2(第3行)开始,前后各多留一行。渲染行从 slice(start,end) 取,外层提供总占位高度。公式 O(1),渲染成本取决于窗口大小,不能直接套到折叠或可变行高。

核验:Node v24.19.0:标准窗口及零行边界断言通过;未运行浏览器滚动、动态行高或辅助技术测试。

边界与取舍

边界

  • 虚拟化后浏览器查找、打印和读屏不一定能访问未渲染内容,需要对应产品路径。
  • 原生稳定排序不保证不同语言环境和服务端排序一致;前端页内排序不能形成跨页全局有序。
  • 撤销本地快照不等于撤销远端副作用;使用共享可变对象会让历史一起被修改。

取舍

  • overscan 增大减少快速滚动空白,却增加渲染与内存;按设备和滚动行为测量。
  • 快照简单易调试,但大文档成本高;补丁和结构共享更省空间,恢复与版本兼容更复杂。

口述示范

我会先给输入、请求、行记录和历史事务分配稳定身份。查询限制数据量,虚拟化限制 DOM,草稿和焦点策略跨卸载保留;排序、撤销及可访问性都按用户可见契约验收。

章节练习

练习 1

搜索依次输入A、B、A,第三次失败而第一次最后成功,界面该是什么?

展开检查点
  • 第三轮错误仍有效,第一轮不能清掉错误或 loading。
  • 说明序号、组合输入与活动候选 ID 的归属。
练习 2

可变行表格在上方展开后跳动,滚走再回来草稿丢失,撤销又重发请求,如何分层修?

展开检查点
  • 保存锚点行ID和偏移,草稿/选择按行ID放数据层。
  • 历史按不可变事务组织,撤销不重播外部副作用,补键盘焦点测试。

参考来源与核验边界

2026-10-04 核验;引用长列表文章用于窗口化原理,不将其历史版本组件 API 当作当前 react-window API。超大像素高度还须考虑浏览器限制。

内容版本:2026-10-04.2 · 题目和来源 ID 保持原样,本站不将面经标签解释为企业官方出题或高频保证。