4.1 Ajax 性能优化


4.1 Ajax 性能优化

本节摘要:Ajax 应用的性能问题集中在网络往返上——请求太多、太重复、太串行、回来得太晚才渲染。本节打开网络面板的瀑布视图,从请求数量、请求时机、响应体积、渲染时机四个方向给出可操作的优化清单,每项都讲清适用条件与副作用,最后给出排查慢请求的定位方法。

先测量,再优化

性能优化的第一原则是用数据定位瓶颈,而不是凭感觉加优化。打开开发者工具的网络面板,勾选禁用缓存,刷新页面,你会看到一张瀑布图——每个请求一条横杠,从发出到完成,颜色分段表示各阶段耗时。

网络瀑布图:耗时都花在哪了

请求瀑布图与四类典型病症

请求瀑布图与四类典型病症

看瀑布图的四个关键读法:横杠之间的空白是排队或串行等待(时间没花在传输上);黄色段长是服务器首字节慢(问题在服务端);蓝色段长是内容下载慢(体积问题);同一时刻堆叠的横杠超过六个是浏览器并发上限排队。不同的形状对应不同的药方,先判断属于哪类,再往下翻对应的段落。

一、减少请求数量:合并与去重

病症特征:页面加载几十上百条请求,其中大量是同类小请求(逐个取标签详情、逐条校验)或完全相同的重复请求。

合并:把"逐个取"改成"一批取"。十个标签详情请求合成一个带 id 列表的批量接口,往返次数从十降到一。接口设计期就要为批量留门(2.4 节),改造存量接口时注意批量接口的响应要保序或带 id 对应,否则前端对不上账。

去重:同一份数据被多个组件分别请求(当前用户信息被五处各取一次),应在请求层做在途合并与短缓存——相同 URL 的在途请求共享同一个 Promise,结果短时间内存活复用:

const inflight = new Map(); const cache = new Map(); const TTL = 5000; function fetchShared(url) { if (cache.has(url)) { const { value, expireAt } = cache.get(url); if (Date.now() < expireAt) return Promise.resolve(value); } if (inflight.has(url)) return inflight.get(url); // 在途共享 const p = fetch(url).then(r => r.json()).then(data => { cache.set(url, { value: data, expireAt: Date.now() + TTL }); inflight.delete(url); return data; }); inflight.set(url, p); return p; }

这段二十行的代码就是 React Query、SWR 这类库的核心机制之一(5.1 节):在途去重防并发重复,短 TTL 防短时重复。副作用要心里有数:TTL 内拿到的是旧数据,对强时效数据(余额)不适用,要按字段选 TTL 或干脆不缓存。

防抖节流处理输入驱动的高频请求(3.4 节已详述),此处归入"数量"方向的清单:搜索联想 300 毫秒防抖、滚动加载节流,是数量控制的第一站。

二、优化请求时机:并行化与预加载

病症特征:瀑布图里大量横向空白——请求在排队等前序请求,而不是同时进行。

并行无依赖:第 3.6 节的老朋友。审查每个页面的请求依赖图,把"其实不依赖"的请求从串行链里摘出来放进 Promise.all。经验上,首屏请求应尽量全部并行,只有真正存在数据依赖(订单 ID 依赖订单列表)的才串行。

数据依赖的消除:有些"依赖"是接口设计制造出来的——先取列表再逐条取详情,改成接口直接回填详情字段,串行链就消失了一层。性能优化做到深处的常规操作是反过来推动接口设计

预加载:在用户明确提出需求前先取数据。时机选择按确定性递增:链接的鼠标悬停时预取目标页数据(点击概率较高,浪费率低);空闲时预取下一页列表(无限滚动的 rootMargin 提前量,3.4 节);关键的 DNS 预解析与连接预热(对跨域资源提前完成 DNS 与 TLS,省掉冷连接的几百毫秒)。预加载的代价是流量浪费与命中率——用户没点的页面白白取了数据,按"点击概率乘节省时长"做预算,别无脑全预取。

请求优先级:首屏关键数据(主内容)先发,非关键(统计上报、推荐)延后——等浏览器空闲或主内容渲染完再发,别让上报请求挤占并发额度。

三、减小响应体积:字段裁剪与压缩

病症特征:下载段(蓝色)长,响应体几百 KB 到几 MB。

字段裁剪:列表接口只回列表要显示的字段,详情字段留给详情接口。常见反面案例是列表接口直接序列化整个 ORM 对象——关联表字段、内部标记全数上线,体积翻几倍,还泄漏内部结构。接口设计期约定"列表瘦身、详情齐全"。

压缩:确认服务端开了 gzip 或 brotli 压缩(响应头 Content-Encoding 可查)。JSON 是高度可压缩的文本,压缩后通常缩到三成以下——这是零代码改动的收益,务必先确认。再往下是分页与按需加载(首屏只取第一屏数据,3.4 节的无限滚动就是体积优化)。

缓存:2.2 节的 HTTP 缓存两层结构在此变现——强缓存让静态资源零请求命中,协商缓存用 ETag 换 304 省传输;接口数据按时效要求配短缓存。缓存是唯一让请求"快过去不存在"的手段,优先级排在一切代码优化之前。

四、渲染时机:别让数据白等着

病症特征:网络面板显示数据早就到了,页面还是空白或骨架屏。

分块渲染:全部数据到齐再画整页,改成"主内容先画、辅助内容到了再补"。配合 3.6 节的 allSettled,把非关键请求从首屏关键路径上摘下来。

避免布局抖动:骨架屏高度与真实内容接近(3.3 节),否则数据到达时的跳动会被用户感知为"慢"——感知性能与实测性能同等重要。

感知优化:乐观更新(3.4 节)、按钮即时反馈、进度提示,这些不减少任何网络耗时,但显著改善等待体验。性能优化的最终指标是用户感受,不只是毫秒数。

五、一个优化决策表

病症 瀑布图特征 首选方案 副作用与前提
请求太多 几十条小请求堆叠 批量接口、在途去重、防抖 批量接口要保序;TTL 有时效风险
串行等待 横杠间大片空白 并行化、接口回填消除依赖 依赖真实存在时不能硬并行
响应过大 下载段长 字段裁剪、压缩、分页 裁剪需要接口配合改造
重复传输 同 URL 反复出现 HTTP 缓存、请求层缓存 按数据时效选缓存策略
服务器慢 等待段长 反馈后端(索引、缓存层) 前端只能缓解不能根治
感知慢 数据早到界面迟改 分块渲染、乐观更新 注意状态一致性

⚠️ 两个常见的优化误区。误 区一:过早优化。首屏快了 100 毫秒、低频设置页快 300 毫秒,用户无感——把精力花在关键路径(首屏、高频交互)上,长尾页面维持现状。误 区二:只测一次。网络环境、数据量、服务负载都影响结果,优化前后各测三次取中位数,弱网模式(面板可模拟)单独测一轮,结论才可靠。

常见疑问解答

优化应该从哪一项开始

按"零成本高收益"排序:确认压缩开启(零代码)→ 修缓存头(配置级)→ 首屏并行化(小改)→ 在途去重(请求层)→ 批量接口(要后端配合)。先摘低处的果子,再爬梯子。

请求数是不是越少越好

不是。为了合并把无关数据塞进一个巨型接口,会造成"改一个字段全量重传"与缓存粒度恶化。数量的合理下限由数据变更模式决定:一起变的数据适合合并,变更频率悬殊的数据分开。

弱网下最有效的一招是什么

体积。弱网的带宽是硬瓶颈,压缩加裁剪加首屏减量的收益,超过一切时机与渲染优化。用面板的慢速模式(3G 档)实测最能说明问题。

本节要点回顾

  • 先读瀑布图再动手:空白是串行、黄长是服务端慢、蓝长是体积大、堆叠是并发上限——形状对应药方。
  • 数量方向:批量合并、在途去重加短缓存、防抖节流;注意批量的保序与缓存的时效风险。
  • 时机方向:首屏全并行、接口回填消除伪依赖、按点击概率做预加载、非关键请求让路。
  • 体积方向:压缩确认是零成本第一步,字段裁剪与分页需要接口配合;缓存优先于一切代码优化。
  • 渲染方向:分块渲染、骨架屏防跳动、乐观更新改善感知——最终指标是用户感受。
  • 两个纪律:只优化关键路径,优化前后弱网各测多轮。

一次真实的优化复盘

把本章方法套在一个真实形态的案例上复盘。某内容平台的详情页,用户抱怨"慢",运营催促优化。打开面板录制首屏:三十二条请求、总耗时四点二秒,瀑布图形状一目了然——主文档之后先串了三个请求(用户信息等图表接口等列表),中段有八条重复的标签接口(同一 URL 反复出现),尾部三个推荐接口排队等待(并发上限),响应体里列表接口带回了全部详情字段(单响应八百多 KB)。

按"低处果子先摘"排序执行。第一刀砍重复:八条标签请求来自三个组件各自取数,加在途去重与三十秒短缓存,请求降到一条,省下约四百毫秒。第二刀解串行:审查发现三个"串行"里只有一个真依赖(图表接口依赖用户信息里的偏好设置),另两个摘出来并行,总耗时从"三者之和"变为"最慢者",又省六百毫秒。第三刀切体积:列表接口裁掉详情字段(响应从八百 KB 降到一百二十 KB,配合确认开启的压缩,实际传输四十 KB),下载段明显缩短。第四刀推接口:把"偏好设置"并进用户信息接口(一次往返带回),串行链再少一环——这刀要后端配合,排进了下一个迭代。四刀之后,首屏总耗时一秒六,弱网模拟下从九秒降到三秒半,用户投诉消失。

复盘时最值得记的不是数字,是方法顺序带来的效率:先读图定性(四类病症各占几成)、再按投入产出排序动刀、每刀之后重测一次。反例做法是凭直觉先上"感觉高级"的手段(比如加缓存层、上服务端渲染),既可能砸错地方,也没有基线可比对。性能优化是一门测量学科,纪律比技巧值钱。

最后补一句关于"什么时候停止优化"的判断:当剩余病症的修复成本开始陡增(要动架构、要协调多团队),而用户侧指标(首屏时间、关键交互延迟)已进入目标区间时,就该收手并把精力转向监控——给关键页面加上性能指标的持续上报,让劣化在用户投诉前自己报警。优化不是把每个数字压到极限,是在成本与体验的交点上停笔,并守住这个点。守住的动作要具体:给首屏时间与关键接口耗时设阈值告警,指标越过线就在群里@负责人——没有守门的优化,三个月后被悄悄加回来的请求就会把数字拖回原点,而那时已经没人记得当初动过哪几刀。


作者与出处
原作者: 灏天文库
来源:灏天文库
整理: 灏天文库整理
由灏天文库平台收录,内容或由平台用户上传,仅供学习交流
发布者: 作者: 灏天文库 转发
评论区 (0)
U