当前位置: 首页 > news >正文

网络请求优化实战:让你的应用加载更快

网络请求优化实战:让你的应用加载更快

大家好,我是蔓蔓。在大厂时,我负责过项目的性能优化,网络请求往往是瓶颈。今天我来和大家分享网络请求优化的实战技巧。

请求合并

// 1. 批量请求 async function fetchUsers(ids: number[]) { const response = await fetch(`/api/users?ids=${ids.join(',')}`); return response.json(); } // 2. 时间窗口合并 class RequestBatcher { constructor() { this.queue = []; this.timer = null; this.batchWindow = 50; // 50ms内的请求合并 } add(request) { this.queue.push(request); if (!this.timer) { this.timer = setTimeout(() => { this.executeBatch(); }, this.batchWindow); } } executeBatch() { const requests = this.queue; this.queue = []; this.timer = null; // 发送批量请求 fetchUsers(requests.map(r => r.id)).then(results => { requests.forEach((req, index) => { req.resolve(results[index]); }); }).catch(errors => { requests.forEach(req => req.reject(errors)); }); } } const batcher = new RequestBatcher(); // 使用 function getUser(id: number) { return new Promise((resolve, reject) => { batcher.add({ id, resolve, reject }); }); } // 同一时间内的多次调用会被合并 getUser(1); getUser(2); getUser(3);

请求缓存

class RequestCache { constructor(ttl = 5 * 60 * 1000) { // 默认5分钟 this.cache = new Map(); this.ttl = ttl; } get(key: string) { const entry = this.cache.get(key); if (!entry) return null; if (Date.now() - entry.timestamp > this.ttl) { this.cache.delete(key); return null; } return entry.value; } set(key: string, value: any) { this.cache.set(key, { value, timestamp: Date.now() }); } generateKey(url: string, options = {}) { return `${url}-${JSON.stringify(options)}`; } } const cache = new RequestCache(); // 缓存装饰器 async function fetchWithCache(url: string, options = {}) { const key = cache.generateKey(url, options); const cached = cache.get(key); if (cached) { return cached; } const response = await fetch(url, options); const data = await response.json(); cache.set(key, data); return data; }

请求取消

// 使用AbortController class RequestManager { constructor() { this.abortControllers = new Map(); } fetch(url: string, options = {}) { const abortController = new AbortController(); const signal = abortController.signal; this.abortControllers.set(url, abortController); const promise = fetch(url, { ...options, signal }).finally(() => { this.abortControllers.delete(url); }); return promise; } cancel(url: string) { const controller = this.abortControllers.get(url); if (controller) { controller.abort(); this.abortControllers.delete(url); } } cancelAll() { this.abortControllers.forEach((controller) => { controller.abort(); }); this.abortControllers.clear(); } } // 使用示例 const manager = new RequestManager(); // 发起请求 manager.fetch('/api/data'); // 路由切换时取消 router.beforeEach((to, from, next) => { manager.cancelAll(); next(); });

预加载与懒加载

// 预加载关键资源 function preloadResources() { // 预加载字体 const fontLink = document.createElement('link'); fontLink.rel = 'preload'; fontLink.as = 'font'; fontLink.href = '/fonts/my-font.woff2'; document.head.appendChild(fontLink); // 预加载关键数据 fetch('/api/init-data'); } // 图片懒加载 const imageObserver = new IntersectionObserver((entries) => { entries.forEach(entry => { if (entry.isIntersecting) { const img = entry.target; img.src = img.dataset.src; imageObserver.unobserve(img); } }); }); document.querySelectorAll('img[data-src]').forEach(img => { imageObserver.observe(img); }); // 路由预加载 const router = { navigate(to) { // 预加载下一个页面的资源 if (to === 'dashboard') { import('./dashboard.js'); // 预加载模块 fetch('/api/dashboard-data'); // 预加载数据 } } };

请求重试与降级

async function fetchWithRetry(url: string, options = {}, retries = 3) { let lastError; for (let i = 0; i < retries; i++) { try { const response = await fetch(url, { ...options, signal: options.signal }); if (response.ok) { return response; } // 只有5xx错误重试 if (response.status < 500) { throw new Error(`HTTP ${response.status}`); } } catch (error) { lastError = error; // 指数退避重试 await new Promise(resolve => setTimeout(resolve, Math.pow(2, i) * 1000)); } } // 降级策略 if (options.fallback) { console.warn('Falling back to static data'); return new Response(JSON.stringify(options.fallback)); } throw lastError; } // 使用示例 fetchWithRetry('/api/data', { fallback: { items: [] } // 降级数据 });

压缩与优化

// 1. 使用压缩 // 服务端配置gzip/brotli // 2. 选择合适的图片格式 // 使用WebP、AVIF等现代格式 // 3. 图片压缩 // 压缩工具:TinyPNG, Squoosh // 4. 减少请求大小 function minimizeRequest(data) { // 只发送必要字段 const { id, name, ...rest } = data; return { id, name }; } // 5. 使用数据节流 class Throttler { constructor(interval = 2000) { this.lastTime = 0; this.interval = interval; } throttle(fn) { const now = Date.now(); if (now - this.lastTime >= this.interval) { fn(); this.lastTime = now; } } }

总结

网络请求优化是一个系统工程,需要从请求合并、缓存策略、取消机制、预加载、重试降级等多个方面综合考虑。根据实际场景选择合适的优化策略,能显著提升应用的性能和用户体验。

技术应当有温度,更快的响应能让用户感受到你的用心。

http://www.jsqmd.com/news/805231/

相关文章:

  • QQ截图独立版终极指南:免费高效的Windows截图与OCR识别工具完全解析
  • 国家中小学智慧教育平台电子课本下载终极指南:如何快速获取PDF教材资源
  • 别再折腾Anaconda了!用PyCharm 2024.1自带工具5分钟搞定TensorFlow 2.15 + Keras 3环境
  • aibot安卓
  • Node.js 异步日志记录如何配置 Winston transports 避免阻塞主线程写入
  • 音频编码实战指南:从无损PCM到高效AAC的选型与应用
  • 原生JavaScript日历组件calendar.js:如何用15KB代码重塑日期交互体验?
  • Sora 2与3D Gaussian结合实战指南(工业级部署避坑手册)
  • 3分钟搞定Word论文格式:APA第7版终极解决方案
  • 如何用91160-cli实现医疗挂号自动化:技术原理与实战指南
  • 四无范式颠覆传统:无标签 / 无基站 / 无穿戴 / 无信号,纯视觉驱动智造升级
  • 量子相位估计在NISQ时代的优化:PFA-TQFT算法解析
  • 实战指南:5分钟掌握ImageToSTL,轻松将照片变成立体模型
  • 保姆级教程:手把手在H3C路由器上配置IPsec over NAT(含IKE提议、转换集详解)
  • 驾车后怕
  • Midjourney咖啡印相为何总偏灰?揭秘RGB→Lab→咖啡染料光谱响应的3层色彩断层及校正算法
  • 20260512 之所思 - 人生如梦
  • Spring Boot项目里LocalDateTime格式化,别再只用@JsonFormat了!这几种全局配置方案更省心
  • 淘宝商品详情 API 技术深度解析:从协议到架构的全方位探讨
  • 告别玄学调试:用QGroundControl地面站给Pixhawk刷固件的保姆级图文指南
  • 深入理解STM32的FSMC:如何像操作SRAM一样轻松点亮你的TFTLCD屏幕
  • STM32CubeMX配置RTC时钟,手把手教你做个不掉电的电子钟(附串口打印代码)
  • 供应商资质真伪难辨?架构师老王教你用实在Agent构建非侵入式风险防控体系
  • [技术解析] K-means与WGCNA:从模块化聚类到基因共表达网络的整合分析策略
  • 2026年获客增长陪跑训练营深度评测:AI+IP双轮驱动模型
  • 终极指南:如何快速反编译Adobe JSXBIN文件并恢复JavaScript源代码
  • 大班教的是公式,吉米小班拆的是你的个人缺陷
  • 别再为驱动发愁了!Realtek RTL8156B-CG 2.5G USB网卡免驱体验与选购指南
  • 为Claude Code配置Taotoken作为备用API解决封号与额度焦虑
  • 别再只盯着应力云图了!用ANSYS Workbench的‘圣维南原理’和模型简化,把你的计算效率提升200%