现代前端开发必备:一步步解析 AJAX+手写AJAX请求 💻✨

何为 ajax?

ajax 全名为 Asynchronous JavaScript and XML = 异步 JS + XML

ajax 是一种在无需重新加载整个网页的情况下,能够更新部分网页的技术(通过在后台发送异步 HTTP 请求并接收响应)。相比于传统网页如果需要更新页面,就必须重新刷新页面,ajax技术使网页变得更加动态和响应迅速,极大地提升了用户的交互体验。

异步请求

运行在不堵塞主线程的同时发送请求,页面的加载和用户的操作不在同一个线程上,互不打扰。这样用户可以继续浏览或与页面互动,而不会被全页刷新打断。

注:如果不理解异步的话可以翻看我之前的文章 掌握异步编程精髓:从回调地狱到promise再到async/await 的优雅进化 - 掘金

ajax 的工作流程

  • 用户触发事件(例如点击按钮)。
  • 发送请求 :JavaScript 创建一个XMLHttpRequest对象(或使用现代的fetch API),并向服务器发送请求。
  • 服务器处理请求:服务器接收并处理请求,然后返回响应(JSON、XML、HTML等)。
  • ajax响应处理:JavaScript 接收响应并根据需要更新页面的部分内容。

ajax 操作实例

不妨假设现在需要从GitHub API异步获取组织成员信息,并将其显示在网页上,并且要求使用异步获取数据。

传统基于 XMLHttpRequest 对象

1. 创建 XMLHttpRequest 对象:

javascript 复制代码
const xhr = new XMLHttpRequest(); // 实例化一个 xhr 请求对象

2. 初始化请求:

javascript 复制代码
xhr.open('GET', 'URL', true); // true 同意异步请求

此处open方法用于初始化请求,其接收三个参数,格式为: xhr.open( 配置请求方式,请求地址,是否异步 )

3. 设置回调函数✨:

javascript 复制代码
xhr.onreadystatechange = function() {
    if (xhr.readyState !== 4) return;
            
    if (xhr.status === 200 || xhr.status === 304) {
        const data = JSON.parse(xhr.responseText)
        resolve(data)
    } else {
        reject(new Error(xhr.responseText))
    }
}
分析
  • onreadystatechange事件处理程序会在XMLHttpRequest对象的readyState发生变化时被调用。
  • readyState属性表示请求的状态:
yaml 复制代码
0 : 请求未初始化。
1 : 请求已建立,但尚未发送。
2 : 请求已发送,但尚未收到响应。
3 : 请求正在处理中,部分数据已经接收。
4 : 请求已完成,且响应已就绪。
  • status属性表示HTTP状态码其等于200304时,表示请求成功。

  • 使用JSON.parse将响应文本转换为JavaScript对象。

resolve函数

用于将Promise的状态从Pending转变为Fulfilled,并传递结果数据给后续的处理程序。

reject函数

用于将Promise的状态从Pending转变为Rejected,并传递错误信息给后续的处理程序。

4.发送请求:

javascript 复制代码
xhr.send();

代码演示:

html 复制代码
<!DOCTYPE html>
<html lang="en">
<head>
    <title>Document</title>
</head>
<body>
    <ul id="memberList">
    </ul>
    <script>
        const oUL = document.getElementById('memberList');
        const xhr = new XMLHttpRequest();
        xhr.open('GET', 'URL', true);
        xhr.onreadystatechange = function() {
            if (xhr.readyState !== 4) return;
            
            if (xhr.status === 200 || xhr.status === 304) {
                const data = JSON.parse(xhr.responseText)
                resolve(data)
            } else {
                reject(new Error(xhr.responseText))
            }
        }
        xhr.send()
    </script>
</body>
</html>

现代fetch API 基于 Promise 对象

1. 发起fetch请求:

JavaScript 复制代码
fetch('URL')
    .then(res => res.json()) // 将返还的二进制代码转换为json对象 
    .then(data => {
        // 处理数据
    })
    .catch(error => console.error('Error:', error)); // 捕获错误
分析
  • 使用fetch API发起一个GET请求到指定的URL。
  • fetch返回一个 Promise,该 Promise 在请求成功时解析为响应对象。

2. 数据处理:

javascript 复制代码
oUL.innerHTML = data.map(member => `
    <li>
        <span>${member.login}</span>
    </li>
`).join(''); // 将json对象赋值给data变量
分析
  • data.map(member => { ... }):遍历data数组中的每个成员对象,并生成相应的HTML字符串。
  • .join(''):将数组中的所有 HTML 字符串用''连接成一个完整的字符串。
  • 最后将生成的 HTML 字符串赋值给oUL.innerHTML,从而动态更新页面内容。

代码演示

html 复制代码
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Ajax</title>
</head>
<body>
    <ul id="memberList">
    </ul>
    <script>
        const oUL = document.getElementById('memberList');
        fetch('URL')
            .then(res => res.json())
            .then(data => {
                oUL.innerHTML = data.map(member => `
                <li>
                    <span>${member.login}</span>
                </li>
                `).join('')
            })
    </script>
</body>
</html>

手写Ajax

在手写ajax之前,需要知道ajax请求的执行流程,即:URL -> http请求(200+4) -> 异步耗时任务 -> 执行流程(DOM) -> promise,知道流程后就可以一步步拆解代码了

步骤详解:

javascript 复制代码
// 6.创建getJSON函数获取目标URL
const getJSON = function(url) {
    // 5.Promise封装
    return new Promise((resolve, reject) => {
        // 1.创建 XMLHttpRequest 对象
        const xhr = XMLHttpRequest
            ? new XMLHttpRequest() 
            : new ActiveXObject("Microsoft.XMLHTTP"); 
        // 2. 规定 请求的类型、URL、是否异步
        xhr.open("GET", url, true); 
        // 3. 设置状态监听函数
        xhr.onreadystatechange = function() {
            if (xhr.readyState !== 4) return;
            
            if (xhr.status === 200 || xhr.status === 304) {
                resolve(xhr.responseText)
            } else {
                reject(new Error(xhr.responseText))
            }
        }
        // 4.发送请求到服务器
        xhr.send();
    })
}

1. 创建 XMLHttpRequest 对象

javascript 复制代码
const xhr = XMLHttpRequest
    ? new XMLHttpRequest() 
    : new ActiveXObject("Microsoft.XMLHTTP");

由于在一些旧版本的 IE 浏览器(如 IE6 和 IE7)中,不支持 XMLHttpRequest,而是使用 ActiveXObject 来创建类似的功能对象。

所以这里通过三元运算符来选择创建的实例:

  • 支持 XMLHttpRequest:创建并返回一个新的 XMLHttpRequest 实例。
  • 不支持 XMLHttpRequest:创建并返回一个新的 ActiveXObject 实例,类型为 "Microsoft.XMLHTTP"。(微软提供的一个用于发送 HTTP 请求的对象)
亮点:
  1. 浏览器兼容性

    • 确保代码在大多数现代浏览器以及一些旧版本的 IE 浏览器中都能正常运行。
    • 利用三元运算符的简洁性,使得代码更加紧凑和易读。
  2. 条件判断

    • 使用 XMLHttpRequest 是否存在作为判断条件,是一种常见的浏览器嗅探(browser sniffing)技术。
    • 这种方式避免了直接检测用户代理字符串(User-Agent),从而减少了误判的可能性。

2 and 4. 发送请求到服务器

javascript 复制代码
xhr.open("GET",'url',true);
xhr.send();

3. 设置状态监听函数

javascript 复制代码
xhr.onreadystatechange = function() {
    if (xhr.readyState !== 4) return;
    
    if (xhr.status === 200 || xhr.status === 304) {
        resolve(xhr.responseText)
    } else {
        reject(new Error(xhr.responseText))
    }
}

每当 readyState 改变时,就会触发 onreadystatechange 事件,并且根据http响应码(xhr.status)来确定请求是否成功。

而当readyState为4,并且xhr.status为200或304时就代表服务器响应就绪,接下来就可以进行任意操作了。并且通过xhr.responseText就可以访问数据了。

xhr.responseText - 服务器以文本字符的形式返回


5. Promise封装

javascript 复制代码
new Promise((resolve, reject) => {
    const xhr = XMLHttpRequest
        ? new XMLHttpRequest() 
        : new ActiveXObject("Microsoft.XMLHTTP");
    xhr.open("GET", url, true); 
    xhr.onreadystatechange = function() {
        if (xhr.readyState !== 4) return;
        
        if (xhr.status === 200 || xhr.status === 304) {
            resolve(xhr.responseText)
        } else {
            reject(new Error(xhr.responseText))
        }
    }
    xhr.send();
})

这里插入一个小知识点方便各位理解Promise类

Promise() 构造函数

语法: new Promise(executor) --> new Promise((resolve,reject) => {})

executor是在构造函数中的function,并且其接收两个函数resolvefuncrejectfunc,这俩个函数控制着Promise的状态。

  • resolve : 当异步操作成功完成时调用,将Promise的状态从"pending"变为"fulfilled",并传递结果,后执行.then()
  • reject : 当异步操作失败时调用,将Promise的状态从"pending"变为"rejected",并传递错误信息,后执行.catch()

注:状态只会从 pending 到 fulfilled 或 rejected 状态,并且状态一旦确定就不再改变了。


测试:

javascript 复制代码
getJSON('URL')
    .then(data => {
        console.log(data)
    })
相关推荐
匹马夕阳11 分钟前
Vite项目中vite.config.js中为什么只能使用process.env,无法使用import.meta.env?
开发语言·前端·javascript
只有一斤了呐15 分钟前
超硬核!教你手搓一套船新架构的前端脚手架~
前端·javascript·开源
拉不动的猪19 分钟前
刷刷题38(前端实现分包及组件懒加载的核心方案&&图片懒加载)
前端·javascript·面试
任磊abc27 分钟前
在react当中利用IntersectionObserve实现下拉加载数据
前端·react·observer·下拉加载·intersection
NaZiMeKiY28 分钟前
HTML5前端第三章节
前端·html·html5
Loadings36 分钟前
Cursor内置的系统提示词学习
前端·javascript·cursor
拉不动的猪41 分钟前
前端数据库indexDB
前端·javascript·面试
自学前端_又又41 分钟前
前端苦熬一月,被 Cursor 5 天超越,未来技术浪潮如何破局?
前端·人工智能·cursor
冴羽1 小时前
SvelteKit 最新中文文档教程(4)—— 表单 actions
前端·javascript·svelte
搬砖-无恙1 小时前
vue uniapp里照片多张照片展示
前端·vue.js·uni-app