从0到1:揭秘前端网络请求的进化之路

作为一名前端开发者,你是否好奇过为什么我们从XHR过渡到了Fetch?本文将通过实际代码和分析,带你深入理解这两种网络请求方式的本质区别。

前言

在我最近的一个项目中,需要对接多个第三方API,这让我不得不重新审视前端网络请求的实现方式。通过分析两段真实代码,我发现了许多值得分享的技术细节和演进思路。

一、XHR:前端网络请求的开拓者

先看一段使用XMLHttpRequest的代码:

js 复制代码
const getJSON = async (url) =>  {
  return new Promise((resolve, reject) => {
    const xhr = new XMLHttpRequest(); // 实例化XHR对象
    
    console.log(xhr.readyState) // 0 - 未初始化
    xhr.open('GET', 'https://api.github.com/users/shunwuyu/repos')
    console.log(xhr.readyState) // 1 - 已调用open()
    xhr.send() // 发送请求 
    console.log(xhr.readyState) // 1 - 仍然是1,因为send()是异步操作
    
    xhr.onreadystatechange = function() {
      if (xhr.readyState == 4) { // 4 - 请求完成
        resolve(JSON.parse(xhr.responseText))
      }
    }
  })
}

1. 前后端分离的先行者

XHR具有深远的历史意义 - 它是前后端分离的关键技术。在XHR出现之前,网页内容主要依赖服务器渲染,页面交互往往需要整页刷新。而XHR允许JavaScript主动发起请求,获取数据后局部更新页面,这正是现代SPA应用的基础。

2. 数据通道的建立过程

观察这段关键代码:

js 复制代码
xhr.open('GET', 'https://api.github.com/users/shunwuyu/repos')
// http 请求  GET  打开一个数据传输的通道
// 底层 好理解 浏览器网络请求的通道被打开

这里的实现精确地展示了open()方法的作用 - 它并不是发送请求,而是打开一个数据传输通道 。这个理解非常到位,因为open()方法确实只是配置请求,真正发送是在调用send()方法时。

通过三处console.log(xhr.readyState),我们可以清晰地看到XHR状态的变化:

  • 初始化后:0
  • open()后:1
  • send()后:仍然是1(因为send是异步操作)

3. 从XML到JSON的进化

代码实现揭示了一个重要的技术演进:数据格式从XML向JSON的转变

XMLHttpRequest名字中的"XML"正是因为它最初设计时预期处理的是XML格式数据。但XML格式冗长、解析复杂,而JSON格式轻量、易于解析,且与JavaScript对象结构天然契合,所以逐渐成为了主流。

代码中使用JSON.parse(xhr.responseText),正是这种演进的体现。

4. Promise的封装

值得注意的是:

js 复制代码
 es6之前的 对象 连promise 都没有,拿来的then 
 事件监听 回调函数 

这点出了XHR的一个历史局限:它诞生于ES6之前,没有原生Promise支持。原始的XHR使用回调函数处理异步结果,容易导致回调地狱。

代码通过手动封装Promise解决了这个问题:

js 复制代码
return new Promise((resolve, reject) => {
  // executor  
  // pending 状态
  // ...XHR相关代码
  xhr.onreadystatechange = function() {
    if (xhr.readyState == 4) {
      resolve(JSON.parse(xhr.responseText))
    }
  }
})

这种封装模式在ES6普及前非常常见,也是各种Ajax库(如jQuery.ajax)的核心实现思路。

二、Fetch:现代网络请求的优雅之选

再来看使用Fetch API的代码:

js 复制代码
document.addEventListener('DOMContentLoaded',async()=>{
    console.log('fetch(https://api.github.com/users/pose203/repos)');
    // resolve fullfilled 完成了
    const result = await fetch('https://api.github.com/users/pose203/repos')
    const data = await result.json()
    document.getElementById('repos').innerHTML = data.map(item=>`<li>${item.name}</li>`).join('');
})

1. 页面加载时机的选择

代码实现反映了一个重要的前端优化思路:选择合适的DOM操作时机 。代码使用DOMContentLoaded事件而非window.onload,是因为前者在DOM结构解析完成后就触发,不需要等待所有资源(如图片)加载完成,可以更早地执行JavaScript逻辑,提升用户体验。

2. Promise链与async/await的对比

代码中展示了两种异步处理方式:

js 复制代码
 then 的链式调用有点繁琐
 fetch('https://api.github.com/users/pose203/repos')
 .then(res=>res.json())
 .then(data=>{
 document.getElementById('repos').innerHTML = data.map(item=>`<li>${item.name}</li>`).join('');
})

而实际使用的是async/await语法:

js 复制代码
const result = await fetch('https://api.github.com/users/pose203/repos')
const data = await result.json()

这种对比直观展示了async/await的优势:代码更加线性,逻辑更加清晰。特别是当处理多个异步操作时,async/await可以让代码结构接近同步代码,大大提升可读性。

3. Fetch的Promise本质

技术上的关键在于:

js 复制代码
new Promise Promise{} fetch本质是返回promise实例

这揭示了Fetch API的核心设计:它天生返回Promise实例。这使得Fetch可以自然地融入现代JavaScript的异步处理模式,无需像XHR那样手动封装。

4. Web开发模式的转变

Fetch API的设计理念深刻反映了现代Web开发的几个关键特点:

  1. 前端与API的分离:区分了用户访问的网站地址和后端API地址
  2. 数据格式标准化:JSON成为通用数据交换格式
  3. 后端技术多样化:不同语言都可以开发API
  4. 前端主导的开发模式:"前端自己做主",主动获取数据并控制UI渲染
  5. 动态页面体验:实现Web 2.0时代的丰富交互

三、实战对比:两种方式各有千秋

通过对比这两段代码,我们可以看出:

1. 代码复杂度

  • XHR:需要手动管理请求状态、事件监听,并封装Promise
  • Fetch:API更简洁,与async/await结合使用时尤为优雅

2. 错误处理

XHR代码中没有明确的错误处理逻辑,而Fetch虽然简洁,但也缺少错误处理。在实际项目中,我们通常需要添加:

js 复制代码
try {
  const result = await fetch(url);
  if (!result.ok) throw new Error(`HTTP error! Status: ${result.status}`);
  const data = await result.json();
  // 处理数据
} catch (error) {
  // 错误处理
}

3. 请求控制能力

XHR提供了更多的底层控制能力,如中止请求、监控上传进度等,而Fetch在这些方面的支持相对有限(虽然AbortController可以部分弥补)。

四、我的实践心得

在我的实际项目中,我发现:

  1. 小型项目:直接使用Fetch + async/await是最简洁高效的选择
  2. 大型应用:通常会使用Axios等库,它们基于XHR构建,提供了更丰富的功能和更一致的API
  3. 兼容性要求高:可能需要使用XHR或提供polyfill

最重要的是,无论使用哪种方式,都需要理解其底层原理,这样才能在遇到问题时快速定位和解决。

总结

通过分析这两段代码及其实现,我们可以清晰地看到前端网络请求技术的演进历程:从复杂的XHR到简洁的Fetch,从回调函数到Promise再到async/await,从XML到JSON。这些变化不仅仅是API的优化,更反映了Web开发模式的根本转变 - 前端从服务器的附庸变成了主导者。

理解这些演进背后的原理和思想,对于我们编写更高质量的前端代码至关重要。毕竟,网络请求是连接前后端的桥梁,是现代Web应用的核心环节。

你现在使用的是哪种网络请求方式?欢迎在评论区分享你的经验和见解!


相关推荐
姑苏洛言43 分钟前
编写产品需求文档:黄历日历小程序
前端·javascript·后端
知识分享小能手1 小时前
Vue3 学习教程,从入门到精通,使用 VSCode 开发 Vue3 的详细指南(3)
前端·javascript·vue.js·学习·前端框架·vue·vue3
姑苏洛言1 小时前
搭建一款结合传统黄历功能的日历小程序
前端·javascript·后端
hackchen2 小时前
Go与JS无缝协作:Goja引擎实战之错误处理最佳实践
开发语言·javascript·golang
你的人类朋友3 小时前
🤔什么时候用BFF架构?
前端·javascript·后端
知识分享小能手3 小时前
Bootstrap 5学习教程,从入门到精通,Bootstrap 5 表单验证语法知识点及案例代码(34)
前端·javascript·学习·typescript·bootstrap·html·css3
一只小灿灿3 小时前
前端计算机视觉:使用 OpenCV.js 在浏览器中实现图像处理
前端·opencv·计算机视觉
前端小趴菜053 小时前
react状态管理库 - zustand
前端·react.js·前端框架
zhuiQiuMX4 小时前
字节面试手撕中等题但还没做出来
面试