从XMLHttpRequest到Fetch:前端异步请求的演进之路

作为一名前端开发者,我们每天都在与各种API打交道。从最初的XMLHttpRequest到现在的Fetch API,前端异步请求技术经历了怎样的演变?今天就让我们通过实际代码来探索这段技术演进的历程。

前后端分离时代的到来

还记得早期的Web开发吗?那时候前后端是紧密耦合的,页面刷新是家常便饭。而现在,我们已经进入了前后端分离的时代:

前后端分离 js 主动请求接口 (异步任务),拿到数据

这种架构让前端可以"自己做主",通过JavaScript主动拉取资源,实现了真正的Web 2.0动态页面体验。

XMLHttpRequest:异步请求的开山鼻祖

理解XMLHttpRequest的工作原理

XMLHttpRequest可以说是前端异步请求的开山鼻祖。虽然它"早期接口请求的对象",但理解它的工作原理对我们掌握现代异步编程至关重要。

让我们来看看XMLHttpRequest的经典用法:

javascript 复制代码
const getJSON = async(url) =>  {
    return new Promise((resolve,reject) => {
        // executor
        // pending 状态
        const xhr = new XMLHttpRequest(); // 实例化
        // http 请求 GET 打开一个数据传输的通道
        // 底层,好理解 浏览器网络请求的通道被打开
        console.log(xhr.readyState);
        xhr.open('GET','https://api.github.com/users/usersname/repos')
        console.log(xhr.readyState);
        xhr.send() // 发送请求
        
        xhr.onreadystatechange = function() {
            if(xhr.readyState === 4) {
                // 响应内容到到达了
                resolve(JSON.parse(xhr.responseText))
            }
        }
    })
}

XMLHttpRequest的readyState详解

这里有个关键概念需要理解:readyState。它表示XMLHttpRequest对象的状态:

  • 0: 未初始化(还没有调用open()方法)
  • 1: 启动(已经调用open()方法,但尚未调用send()方法)
  • 2: 发送(已经调用send()方法,但尚未接收到响应)
  • 3: 接收(已经接收到部分响应数据)
  • 4: 完成(已经接收到全部响应数据)

readyState === 4时,说明响应内容已经到达,我们就可以处理数据了。

从XML到JSON的数据格式演变

有趣的是,XMLHttpRequest中的"XML"反映了早期的数据交换格式:

xml 复制代码
<song>
    <author>周深</author>
    <title>大鱼</title>
</song>

而现在我们更多使用的是JSON格式:

json 复制代码
{
    "author": "林俊杰",
    "title": "江南"
}

JSON格式更轻量、更易于JavaScript处理,这也体现了前端技术的不断进步。

Fetch API:现代异步请求的新宠

告别回调地狱,拥抱Promise

XMLHttpRequest有个明显的问题:它是"es6之前的对象,连promise都没有"。这意味着我们只能通过事件监听和回调函数来处理异步操作,容易陷入回调地狱。

Fetch API的出现彻底改变了这种情况:

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

Promise的三种状态

使用Fetch API时,我们需要理解Promise的三种状态:

  1. pending状态:等待中,异步操作还未完成
  2. fulfilled状态:已完成,通过resolve()触发
  3. rejected状态:已失败,通过reject()触发

async/await:让异步代码像同步代码一样

ES8引入的async/await语法糖让异步代码变得更加优雅:

javascript 复制代码
// 传统的then链式调用
fetch('https://api.github.com/users/usersname/repos')
.then(res => res.json())
.then(json => {
    // 处理数据
})

// async/await写法
const result = await fetch('https://api.github.com/users/usersname/repos')
const data = await result.json()
// 像同步代码一样处理数据

"then的链式调用有的繁琐",而async/await让代码"像同步代码一样",大大提高了代码的可读性和可维护性。

性能优化:DOM加载时机的选择

DOMContentLoaded vs window.onload

在实际开发中,我们经常需要在DOM加载完成后执行JavaScript代码。这里有两个重要的事件:

javascript 复制代码
document.addEventListener('DOMContentLoaded', async () => {
    // DOM结构加载完成就执行,不等待图片、样式表等资源
})

window.onload = function() {
    // 所有资源都加载完成后执行,"有点晚"
}

正如注释中提到的,window.onload"有点晚",因为它要等待所有资源(包括图片、样式表)都加载完成。而DOMContentLoaded只需要DOM结构加载完成就可以执行,性能更好。

API接口 vs 网站地址:理解URL的本质

在前端开发中,我们需要区分两种不同的URL:

ruby 复制代码
// API接口地址 - 返回JSON格式数据
// https://api.github.com/users/usersname/repos

// 网站地址 - 用户访问的页面
// www.bilibili.com

API接口地址是"资源"的概念,前端通过JavaScript主动拉取这些资源,而网站地址是用户直接访问的页面。这种分离让前端可以更灵活地处理数据和用户交互。

实战应用:GitHub仓库列表展示

让我们看看如何将获取到的数据渲染到页面上:

javascript 复制代码
// 获取数据并渲染
const data = await getJSON('https://api.github.com/users/usersname/repos')
document.getElementById('repos').innerHTML = data.map(item => `<li>${item.name}</li>`).join('')

这里使用了函数式编程的思想:

  1. 使用map()方法遍历数组
  2. 将每个仓库对象转换为<li>标签
  3. 使用join('')将数组元素连接成字符串
  4. 通过innerHTML更新DOM

技术演进的思考

从XMLHttpRequest到Fetch API,从回调函数到Promise,从then链式调用到async/await,前端异步请求技术的演进体现了几个重要趋势:

  1. 语法简化:代码变得更加直观和易读
  2. 错误处理:更好的错误处理机制
  3. 性能优化:更高效的资源管理
  4. 开发体验:更好的开发者体验

写在最后

无论是XMLHttpRequest还是Fetch API,它们都是前端异步编程的重要工具。理解它们的工作原理和使用场景,不仅能帮助我们写出更好的代码,也能让我们在面对复杂的异步场景时游刃有余。

技术在不断演进,但核心思想是不变的:让前端能够主动获取数据,提供更好的用户体验。这正是Web 2.0时代动态页面的魅力所在。

相关推荐
好果不榨汁7 分钟前
qiankun 路由选择不同模式如何书写不同的配置
前端·vue.js
小蜜蜂dry7 分钟前
Fetch 笔记
前端·javascript
拾光拾趣录8 分钟前
列表分页中的快速翻页竞态问题
前端·javascript
小old弟9 分钟前
vue3,你看setup设计详解,也是个人才
前端
Lefan13 分钟前
一文了解什么是Dart
前端·flutter·dart
Patrick_Wilson18 分钟前
青苔漫染待客迟
前端·设计模式·架构
vvilkim20 分钟前
Nuxt.js 全面测试指南:从单元测试到E2E测试
开发语言·javascript·ecmascript
写不出来就跑路39 分钟前
基于 Vue 3 的智能聊天界面实现:从 UI 到流式响应全解析
前端·vue.js·ui
OpenTiny社区41 分钟前
盘点字体性能优化方案
前端·javascript
FogLetter1 小时前
深入浅出React Hooks:useEffect那些事儿
前端·javascript