JavaScript前后端交互-AJAX/fetch

server/2025/2/5 15:02:08/

摘自千峰教育kerwin的js教程

AJAX

1、AJAX 的优势

  1. 不需要插件的支持,原生 js 就可以使用
  2. 用户体验好(不需要刷新页面就可以更新数据)
  3. 减轻服务端和带宽的负担
  4. 缺点: 搜索引擎的支持度不够,因为数据都不在页面上,搜索引擎搜索不到

2、AJAX 的使用

  • 在 js 中有内置的构造函数来创建 ajax 对象
  • 创建 ajax 对象以后,我们就使用 ajax 对象的方法去发送请求和接受响应

3、AJAX发送请求

  1. 创建AJAX对象
  2. 配置连接信息(最好不要配置成同步)
  3. 发送
const xhr = new XMLHttpRequest();// xhr 对象中的 open 方法是来配置请求信息的
// 第一个参数是本次请求的请求方式 get / post / put / ...
// 第二个参数是本次请求的 url 
// 第三个参数是本次请求是否异步,默认 true 表示异步,false 表示同步
// xhr.open('请求方式', '请求地址', 是否异步)
xhr.open('get', './data.php')// 使用 xhr 对象中的 send 方法来发送请求
xhr.send()

4、AJAX接收响应

  • 发送完成后,我们就需要等待服务端的消息
  • 如果服务端正常的话,响应也能回到客户端
  • 但是我们拿不到响应
  • 如果想拿到响应,我们有两个前提条件
    1. 本次 HTTP 请求是成功的,也就是我们之前说的 http 状态码为 200 ~ 299
    2. ajax 对象也有自己的状态码,用来表示本次 ajax 请求中各个阶段
4.1、AJAX状态码
  • ajax 状态码 - xhr.readyState
  • 是用来表示一个 ajax 请求的全部过程中的某一个状态
    • readyState === 0: 表示未初始化完成,也就是 open 方法还没有执行
    • readyState === 1: 表示配置信息已经完成,也就是执行完 open 之后
    • readyState === 2: 表示 send 方法已经执行完成
    • readyState === 3: 表示正在解析响应内容
    • readyState === 4: 表示响应内容已经解析完毕,可以在客户端使用了
  • 这个时候我们就会发现,当一个 ajax 请求的全部过程中,只有当 readyState === 4 的时候,我们才可以正常使用服务端给我们的数据
  • 所以,配合 http 状态码为 200 ~ 299
    • 一个 ajax 对象中有一个成员叫做 xhr.status
    • 这个成员就是记录本次请求的 http 状态码的
  • 两个条件都满足的时候,才是本次请求正常完成
4.2、readyStateChange事件
  • ajax 对象中有一个事件,叫做 readyStateChange 事件

  • 这个事件是专门用来监听 ajax 对象的 readyState 值改变的的行为

  • 也就是说只要 readyState 的值发生变化了,那么就会触发该事件(所以响应过程会触发多次)

  • 所以我们就在这个事件中来监听 ajaxreadyState 是不是到 4 了

    const xhr = new XMLHttpRequest()
    xhr.open('get', './data.php')xhr.send()xhr.onreadyStateChange = function () {// 每次 readyState 改变的时候都会触发该事件// 我们就在这里判断 readyState 的值是不是到 4// 并且 http 的状态码是不是 200 ~ 299if (xhr.readyState === 4 && /^2\d{2}$/.test(xhr.status)) {// 这里表示验证通过// 我们就可以获取服务端给我们响应的内容了}
    }
    
4.3、onload事件
  • 相比于上述事件,这个只有在xhr.readyState === 4时才会触发
    xhr.onload = function () {if (xhr.status == 200) {} else of (xhr.status == 404) {}
    }
    
4.4、responseText响应体
  • ajax 对象中的 responseText 成员

  • 就是用来记录服务端给我们的响应体内容的

  • 所以我们就用这个成员来获取响应体内容就可以

    const xhr = new XMLHttpRequest()
    xhr.open('get', './data.php')xhr.send()xhr.onreadyStateChange = function () {if (xhr.readyState === 4 && /^2\d{2}$/.test(xhr.status)) {// 我们在这里直接打印 xhr.responseText 来查看服务端给我们返回的内容console.log(xhr.responseText)}
    }
    

5、发送post和get请求时如何携带参数

5.1、get请求携带参数
  • get 请求的参数就直接在 url 后面进行拼接就可以
    const xhr = new XMLHttpRequest()
    // 直接在地址后面加一个 ?,然后以 key=value 的形式传递
    // 两个数据之间以 & 分割
    xhr.open('get', './data.php?a=100&b=200')xhr.send()
    
    • 这样服务端就能接受到两个参数
    • 一个是 a,值是 100
    • 一个是 b,值是 200
5.2、 post请求携带参数
  • post 请求的参数是携带在请求体中的,所以不需要再 url 后面拼接
    const xhr = new XMLHttpRequest()
    xhr.open('get', './data.php')// 如果是用 ajax 对象发送 post 请求,必须要先设置一下请求头中的 content-type
    // 告诉一下服务端我给你的是一个什么样子的数据格式
    xhr.setRequestHeader('content-type', 'application/x-www-form-urlencoded')// 请求体直接再 send 的时候写在 () 里面就行
    // 不需要问号,直接就是 'key=value&key=value' 的形式
    xhr.send('a=100&b=200')
    
    • application/x-www-form-urlencoded 表示的数据格式就是 key=value&key=value

6、封装AJAX

  • 重点:要注意header和data的数据格式处理成对应的
function queryStringify(obj) {let str = ''for (let k in obj) str += `${k}=${obj[k]}&`return str.slice(0, -1)
}// 封装 ajax
function ajax(options) {let defaultoptions = {url: "",method: "GET",async: true,data: {},headers: {},success: function () { },error: function () { }}// 默认值合并,保证所有参数都有效let { url, method, async, data, headers, success, error } = {...defaultoptions,...options}// 当传的是对象,且设置header也是json时,将传的对象{key:value}转化为jsonif (typeof data === 'object' && headers["content-type"]?.indexOf("json") > -1) {data = JSON.stringify(data)}else {// 以下情况需要处理// 1. 传的是对象,但是header设置的xxx-form-urlencoded// 2. 传的直接就是表单(不设置的话默认是form编码) name=123&age=10data = queryStringify(data)}// 如果是 get 请求, 并且有参数, 那么直接组装一下 url 信息if (/^get$/i.test(method) && data) url += '?' + data// 4. 发送请求const xhr = new XMLHttpRequest()xhr.open(method, url, async)xhr.onload = function () {if (!/^2\d{2}$/.test(xhr.status)) {error(`错误状态码:${xhr.status}`)return }// 执行解析try {let result = JSON.parse(xhr.responseText)success(result)} catch (err) {error('解析失败 ! 因为后端返回的结果不是 json 格式字符串')}}// 设置请求头内的信息for (let k in headers) xhr.setRequestHeader(k, headers[k])if (/^get$/i.test(method)) {xhr.send()} else {xhr.send(data)}
}
ajax({url:"http://localhost:3000/users",method:"GET",async:true,// 这里header默认是form编码,但是传的对象,所以走else逻辑data:{username:"kerwin",password:"123"}, headers:{},success:function(res){console.log(res)},error:function(err){console.log(err)}
})

7、回调地狱

  • 当一个回调函数嵌套一个回调函数的时候

  • 就会出现一个嵌套结构

  • 当嵌套的多了就会出现回调地狱的情况

  • 比如我们发送三个 ajax 请求

    • 第一个正常发送

    • 第二个请求需要第一个请求的结果中的某一个值作为参数

    • 第三个请求需要第二个请求的结果中的某一个值作为参数

      ajax({url: '我是第一个请求',success (res) {// 现在发送第二个请求ajax({url: '我是第二个请求'data: { a: res.a, b: res.b },success (res2) {// 进行第三个请求ajax({url: '我是第三个请求',data: { a: res2.a, b: res2.b },success (res3) { console.log(res3) }})}})}
      })
      
      • 当代码成为这个结构以后,已经没有维护的可能了
      • 所以我们要把代码写的更加的艺术一些

8、promise

  • promise 是一个 ES6 的语法

  • 承诺的意思,是一个专门用来解决异步 回调地狱 的问题

  • 语法:

    new Promise(function (resolve, reject) {
    // resolve 表示成功的回调
    // reject 表示失败的回调
    }).then(function (res) {// 成功的函数
    }).catch(function (err) {// 失败的函数
    })
    
    • promise 就是一个语法

    • 我们的每一个异步事件,在执行的时候

    • 都会有三个状态,执行中 / 成功 / 失败

  • 因为它包含了成功的回调函数

  • 所以我们就可以使用 promise 来解决多个 ajax 发送的问题

    new Promise(function (resolve, reject) {ajax({url: '第一个请求',success (res) {resolve(res)}})
    }).then(function (res) {// 准备发送第二个请求return new Promise(function (resolve, reject) {ajax({url: '第二个请求',data: { a: res.a, b: res.b },success (res) {resolve(res)}})})
    }).then(function (res) {ajax({url: '第三个请求',data: { a: res.a, b: res.b },success (res) {console.log(res)}})
    })
    

9、 async/await

  • async/await 是一个 es7 的语法

  • 这个语法是 回调地狱的终极解决方案

  • 语法:

    async function fn() {const res = await promise对象
    }
    
  • 可以把异步代码写的看起来像同步代码

  • 只要是一个 promiser 对象,那么我们就可以使用 async/await 来书写

async function fn() {const res = await new Promise(function (resolve, reject) {ajax({url: '第一个地址',success (res) {resolve(res)}})})// res 就可以得到请求的结果const res2 = await new Promise(function (resolve, reject) {ajax({url: '第二个地址',data: { a: res.a, b: res.b },success (res) {resolve(res)}})})const res3 = await new Promise(function (resolve, reject) {ajax({url: '第三个地址',data: { a: res2.a, b: res2.b },success (res) {resolve(res)}})})// res3 就是我们要的结果console.log(res3)
}

这种方式如果要捕获错误,可以使用try-catch方式

fetch

XMLHttpRequest 是一个设计粗糙的 API,配置和调用方式非常混乱, 而且基于事件的异步模型写起来不友好。

兼容性不好 polyfill: https://github.com/camsong/fetch-ie8,所以还是推荐封装ajax

fetch("http://localhost:3000/users").then(res=>res.json()).then(res=>{console.log(res)})fetch("http://localhost:3000/users",{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({username:"kerwin",password:"123"})}).then(res=>res.json()).then(res=>{console.log(res)})fetch("http://localhost:3000/users/5",{method:"PUT",headers:{"content-type":"application/json"},body:JSON.stringify({username:"kerwin",password:"456"})}).then(res=>res.json()).then(res=>{console.log(res)})fetch("http://localhost:3000/users/5",{method:"DELETE"}).then(res=>res.json()).then(res=>{console.log(res)})
//错误处理
fetch("http://localhost:3000/users1").then(res=>{if(res.ok){return res.json()}else{// 手动拒绝return Promise.reject({status:res.status,statusText:res.statusText})}}).then(res=>{console.log(res)}).catch(err=>{console.log(err)})

结语

我写过的基于React的项目中,一般会使用将axios(另个一将xhr封装的npm包)二次封装的方式,结合框架进行使用。


http://www.ppmy.cn/server/165165.html

相关文章

PyTorch生态系统中的连续深度学习:使用Torchdyn实现连续时间神经网络

神经常微分方程(Neural ODEs)是深度学习领域的创新性模型架构,它将神经网络的离散变换扩展为连续时间动力系统。与传统神经网络将层表示为离散变换不同,Neural ODEs将变换过程视为深度(或时间)的连续函数。…

LabVIEW的智能电源远程监控系统开发

在工业自动化与测试领域,电源设备的精准控制与远程管理是保障系统稳定运行的核心需求。传统电源管理依赖本地手动操作,存在响应滞后、参数调节效率低、无法实时监控等问题。通过集成工业物联网(IIoT)技术,实现电源设备…

2 MapReduce

2 MapReduce 1. MapReduce 介绍1.1 MapReduce 设计构思 2. MapReduce 编程规范3. Mapper以及Reducer抽象类介绍1.Mapper抽象类的基本介绍2.Reducer抽象类基本介绍 4. WordCount示例编写5. MapReduce程序运行模式6. MapReduce的运行机制详解6.1 MapTask 工作机制6.2 ReduceTask …

使用真实 Elasticsearch 进行高级集成测试

作者:来自 Elastic Piotr Przybyl 掌握高级 Elasticsearch 集成测试:更快、更智能、更优化。 在上一篇关于集成测试的文章中,我们介绍了如何通过改变数据初始化策略来缩短依赖于真实 Elasticsearch 的集成测试的执行时间。在本期中&#xff0…

【含文档+PPT+源码】基于小程序的智能停车管理系统设计与开发

项目介绍 本课程演示的是一款基于小程序的智能停车管理系统设计与开发,主要针对计算机相关专业的正在做毕设的学生与需要项目实战练习的 Java 学习者。 1.包含:项目源码、项目文档、数据库脚本、软件工具等所有资料 2.带你从零开始部署运行本套系统 3…

SQL 总结

SQL 总结 引言 SQL(Structured Query Language)是一种用于管理关系数据库的计算机语言。自从1970年代被发明以来,SQL已经成为了数据库管理的基础。本文将对SQL的基本概念、常用命令、高级特性以及SQL在数据库管理中的应用进行总结。 SQL基本概念 数据库 数据库是存储数…

Hive on Spark优化

文章目录 第1章集群环境概述1.1 集群配置概述1.2 集群规划概述 第2章 Yarn配置2.1 Yarn配置说明2.2 Yarn配置实操 第3章 Spark配置3.1 Executor配置说明3.1.1 Executor CPU核数配置3.1.2 Executor内存配置3.1.3 Executor个数配置 3.2 Driver配置说明3.3 Spark配置实操 第4章 Hi…

基于 Java 开发的 MongoDB 企业级应用全解析

基于Java的MongoDB企业级应用开发实战 目录 背景与历史MongoDB的核心功能与特性企业级业务场景分析MongoDB的优缺点剖析开发环境搭建 5.1 JDK安装与配置5.2 MongoDB安装与集群配置5.3 开发工具选型 Java与MongoDB集成实战 6.1 项目依赖与驱动选择6.2 连接池与客户端配置6.3…