面试官:前端有哪些常见的请求方式?如何实现ChatGPT打字机聊天效果?

前言

哈喽,我是Fine

我们在实际项目中少不了与后端通信获取数据,不同的业务请求的方式也不尽相同。大部分时候前端通过Ajax向后端发送数据或获取数据即可满足业务需求,但有时的一些特殊场景可能就Ajax就满足不了了。比如常见的IM需要我们实时的获取信息,就需要通过websocket来实现了。

再比如最近比较火的ChatGPT,它的打字机聊天效果很炫酷,它是通过SSE去实现的。SSE 是一种 HTML5 技术,允许服务器向客户端推送数据,而不需要客户端主动请求。通过 SSE,我们可以在服务器端有新消息时,实时将消息推送到前端,从而实现动态的聊天效果。

今天会为大家分享一篇前端常见的请求方式的文章,以下是正文:

1. DOM标签

在HTML中,有很多标签可以用来发起HTTP请求。以下是最常见的一些:

  1. <a>:此标签用于创建一个链接,当用户点击链接时将发起请求到指定的URL。
<a href="http://example.com">Visit Example</a>  
  1. <img>:此标签用于加载图片,浏览器会发送请求去获取对应的src的图片资源。
<img src="image.jpg" alt="My test image">  
  1. <link>:此标签通常位于HTML文件的<head>部分,用于引入CSS文件。
<link rel="stylesheet" type="text/css" href="styles.css">  
  1. <script>:此标签用于引入JavaScript文件。
<script src="script.js"></script>  
  1. <iframe>:此标签用于嵌入另一个HTML页面。
<iframe src="http://example.com"></iframe>  
  1. <video> and <audio>:这两个标签用于嵌入视频和音频内容。
<video src="myVideo.mp4" controls></video>  
<audio src="myAudio.mp3" controls></audio>  
  1. <object><embed><source>等也能发起请求。

请注意,以上的所有请求都是浏览器根据HTML内容自动发起的,不需要做额外的配置。

如果使用js进行获取呢?

JavaScript 并不能直接获取到HTML标签请求的结果,但可以通过监听事件和使用一些DOM 属性来间接得到一些信息。以下是一些常见的方法:

  1. 以img为例,为图像添加 loaderror 事件:
var img = new Image();
img.src = 'http://example.com/image.jpg';
img.onload = function({
    console.log('Image has loaded');
};
img.onerror = function({
    console.log('An error occurred while loading image');
};

我们并没有直接获取到图片的内容,但是我们知道图片何时加载成功,或者是否出错,可以获取返回的内容。

2. AJAX

2.1 概念

AJAXAsynchronous JavaScript and XML的缩写,即异步的JavaScript和XML。它是一种在不重新加载整个网页的情况下,与服务器交换数据并更新部分网页的技术。

AJAX的核心是XMLHttpRequest对象,它提供了一个API,使得浏览器可以通过JavaScript向服务器发送请求并接收响应。XMLHttpRequest是一个浏览器对象,用于在不重新加载页面的情况下向服务器发送HTTP请求并获取响应。它是AJAX技术的核心组成部分之一,用于实现异步数据交互。

2.2 案例

  1. 请求过程
1. 创建ajax对象 
2. 调用ajax对象的open 函数  准备发送请求 
3. 调用ajax对象的send 函数  发送请求  但是请求还没有到达服务器 
4. 调用ajax对象的监听函数 onreadystatechange   监听ajax的创建过程 
      readystate  ajax 的状态码 
        0  准备创建ajax
              new XMLHttpRequest();
        1  发送了请求  服务器尚未接到数据 [客户端和服务器   建立了链接]
              open()
              send()
        以下这些状态码 都是服务器对客户端数据进行操作的状态     
        2  发送了请求  服务器接受到了请求 

        3  服务器接收到了请求 并且在处理请求 

        4  服务器处理请求成功  响应给客户端 

      status  相应状态码  表示 readystate === 4 
         服务器已经向客户端响应了数据 
           状态码 会判断 服务器相应数据的状态 
             200 ok   响应成功 
             404      响应的页面没有找到 
             500      服务器产生异常 失败

  1. 案例
<button id="btn">获取数据</button>
<h1 id="h1">
 希望获取新加载的数据
</
h1>
<div id="content">
 原有的数据........
</div>
<script>

 /
/ 注册事件
 const btnNode = document.querySelector("#btn");
 const contentNode = document.getElementById('content')

 /
/ 注册onclick事件 
 btnNode.onclick = function () {
  /
/ 1. 创建ajax 对象 
  /
/ 高版本浏览器创建的对象 
  let xhr;
  if (window.XMLHttpRequest) {
   xhr = new XMLHttpRequest();
  } else {
   /
/ IE5  IE6 低版本浏览器 
   xhr = new ActiveXObject("Microsoft.XMLHTTP")
  }

  /
/ 2. 准备发送请求 
  /
*
  参数
   1.  请求方式  get/post/put/delete
   2.  请求位置  url
   3.  是否是异步请求 
       true  表示异步
       false 表示同步
  */
  xhr.open("POST", "http:/
/localhost:3000/api/xhr", true)
  // 设置请求头
  xhr.setRequestHeader("
token", "888888888")

  // 3. 发送请求 
  xhr.send()

  // 4.  监听ajax的整个过程
  // onreadystatechange 监听ajax 发送的事件 
  xhr.onload = function () {
   console.log('打印***onload')
   const data = xhr.responseText;
   //获取h1 节点 
   const h1Node = document.getElementById("
h1")
   h1Node.innerText = data;
   console.log(data);
  }
 }
</script>

其中第四步可以使用xhr.onload进行替换

xhr.onload = function(){
    const data = xhr.responseText;
    //获取h1 节点 
    const h1Node = document.getElementById("h1")
    h1Node.innerText = data;
    console.log(data);
}

2.3 xhr.onloadxhr.onreadystatechange两者的区别

xhr.onloadxhr.onreadystatechange执行相同的操作:在一个异步请求的各种生命周期阶段接收通知。但是,它们的使用方式有一些差异。

「xhr.onreadystatechange:」

onreadystatechange事件会在readyState属性值改变时触发。这意味着你需要检查readyState值并确定请求是否完成。如果readyState等于4,那么请求已经完成并且响应已经就绪。

xhr.onreadystatechange = function({
    if (xhr.readyState === 4 && xhr.status === 200) {
        console.log(xhr.responseText);
    }
};

「xhr.onload:」

相比之下,xhr.onload事件只在请求成功完成时触发。不需要检查readyState或状态代码,因为如果触发了onload事件,那么就可以确保请求已经成功完成。

xhr.onload = function({
    console.log(xhr.responseText);
};

xhr.onload提供了一个更简洁、更直观的方式来处理成功的HTTP请求。然而,如果你需要更细粒度的控制(例如,你想在请求的不同阶段执行不同的操作),那么xhr.onreadystatechange可能会是一个更好的选择。

3. fetch

3.1 概念

Fetch是 XMLHttpRequest 的升级版,用于在 JavaScript 脚本里面发出 HTTP 请求。

3.2 案例

<button id="btn">获取数据</button>
<h1 id="h1">
 希望获取新加载的数据
</h1>
<div id="content">
 原有的数据........
</div>
<script>

 // 注册事件
 const btnNode = document.querySelector("#btn");

 // 注册onclick事件 
 btnNode.onclick = function ({
  fetch('http://localhost:3000/api/fetch', {
   headers: {
    token'66666666666'
   }
  }).then(res => {
   return res.text()
  }).then(data => {
   const h1Node = document.getElementById("h1")
   h1Node.innerText = data;
  })

 }

</script>

image.png

3.3 基于fetch封装的库umi-request


总结ajax和fetch区别

功能点XHRFetch
基本请求、获取响应能力
监控请求进度x
监控响应进度
Service-Worker中是否可用x
控制cookie的携带x
控制重定向x
请求取消
自定义referrerx
x
API风格EventPromise
活跃度x

4. websocket

4.1 概念

WebSocket 是一种在单个 TCP 连接上进行全双工通信的协议。WebSocket 使得客户端和服务器之间的数据交换变得更加简单,允许服务端主动向客户端推送数据。在 WebSocket API 中,浏览器和服务器只需要完成一次握手,两者之间就可以直接创建持久性的连接,并进行双向数据传输。

现在许多网站的实时功能(例如聊天,实时游戏等)都是使用 WebSocket 实现的。它解决了 HTTP 协议只能由客户端向服务端发起请求的问题,使得服务端也能主动向客户端发送数据。

4.2 案例

<button id="btn">获取数据</button>
<h1 id="h1">
 希望获取新加载的数据
</
h1>
<div id="content">
 原有的数据........
</div>

<script>

 // 注册事件
 const btnNode = document.querySelector("#btn");
 const h1Node = document.getElementById("h1")

 // 注册onclick事件 
 btnNode.onclick = function ({

  const socket = new WebSocket('ws://localhost:3000');

  socket.onopen = function ({
   console.log('连接已建立');
  };

  socket.onmessage = function (event{
   const data = event.data;
   console.log('接收到服务器发送的消息:', data);
   h1Node.innerText = data;
  };

  socket.onclose = function ({
   console.log('连接已关闭');
  };
  // 心跳
  setInterval(() => {
   socket.send('ping');
  }, 3000);

}
</script>


image.png

5. Beacon

5.1 概念

Beacon API 用于发送异步和非阻塞请求到服务器。这类请求不需要响应。与 XMLHttpRequest 或 Fetch API请求不同,浏览器会保证在页面卸载前,将信标请求初始化并运行完成。

Beacon API 的优点是,它可以在浏览器关闭或离开当前页面时异步发送数据到服务器,而不会阻塞页面的操作或等待服务器的响应。这对于进行统计分析、性能监控和日志记录等后台数据收集的场景非常有用。

5.2 案例

<button id="btn">获取数据</button>
<h1 id="h1">
 希望获取新加载的数据
</h1>
<div id="content">
 原有的数据........
</div>
<script>

 // 注册事件
 const btnNode = document.querySelector("#btn");
 const h1Node = document.getElementById("h1")

 // 注册onclick事件 
 btnNode.onclick = function ({
  const data = {
   event'按钮点击',
   timestampnew Date().toISOString(),
   userAgent: navigator.userAgent
  };
  // 调用发送数据的函数, 返回true
   const res =  navigator.sendBeacon('http://localhost:3000/api/beacon'JSON.stringify(data))
  h1Node.innerText = res
 }

</script>

image.png

6. SSE(Eventsource)

6.1 概念

Server-Sent Events(SSE)是一种基于HTTP的服务器向客户端发送更新的技术。与WebSockets类似,SSE也允许服务器向客户端推送实时更新。然而,不同于WebSockets提供的双向通信,SSE是单向的:只允许服务器向客户端发送数据。

SSE 是基于HTTP的,因此比WebSocket更容易使用,并且与现有的HTTP基础设施兼容性更好。使用SSE,你可以使用传统的HTTP请求到服务器上开启一个持久连接,服务器可以通过这个连接连续地发送消息。

6.2 案例

<button id="btn">获取数据</button>
<h1 id="h1">
 希望获取新加载的数据
</h1>
<div id="content">
 原有的数据........
</div>
<script>

 // 注册事件
 const btnNode = document.querySelector("#btn");
 const h1Node = document.getElementById("h1")

 // 注册onclick事件 
 btnNode.onclick = function ({
  // 创建一个EventSource对象,连接到服务器端的SSE端点
  const eventSource = new EventSource('http://localhost:3000/api/sse');

  // 监听服务器端发送的消息
  eventSource.onmessage = (event) => {
   console.log(event.data);
   const data = event.data
   h1Node.innerText = data;
   // 在这里处理服务器端发送的消息
  };

  // 监听连接关闭事件
  eventSource.onclose = () => {
   console.log('SSE connection closed');
  };

  // 监听错误事件
  eventSource.onerror = (error) => {
   console.error('SSE error:', error);
  };
 }
</script>
image.png

7. 以上案例服务端代码

使用koa进行编写:

const Koa = require('koa')
const Router = require('koa-router')
const websockify = require('koa-websocket')
const cors = require('koa2-cors')
const { koaBody } = require('koa-body');

const app = new Koa();

app.use(koaBody());


// 使用koa2-cors中间件解决跨域
app.use(cors())

const router = new Router()

//  使用 koa-websocket 将应用程序升级为 WebSocket 应用程序
const appWebSocket = websockify(app)

// webSocket
appWebSocket.ws.use((ctx, next) => {
 // 存储新连接的客户端
 clients.add(ctx.websocket)
 // 处理连接关闭事件
 ctx.websocket.on('close'() => {
  clients.delete(ctx.websocket)
 })
 ctx.websocket.on('message'(data) => {
  console.log('打印***data', data)
  ctx.websocket.send('pong' + Math.random())
 })
 ctx.websocket.on('error'(err) => {
  console.log('打印***err', err)
  clients.delete(ctx.websocket)
 })

 return next(ctx)
})

// xhr
router.post('/api/xhr'(ctx) => {
 ctx.body = 'I am xhr!'
})
// fetch
router.get('/api/fetch'(ctx) => {
 ctx.body = 'I am fetch!'
})
// beacon
router.post('/api/beacon'(ctx) => {
 const data = ctx.request.body
 console.log('打印***data', data)
 ctx.body = 'I am beacon!'
})
// sse
router.get('/api/sse'(ctx) => {

 ctx.respond = false
 ctx.res.writeHead(200, {
  "Content-Type""text/event-stream",
  "Cache-Control""no-cache",
  Connection: "keep-alive",
 });

 // 发送初始事件数据
 ctx.res.write(`data: I am sse!\n\n`);

 // 定期发送事件数据
 setInterval(() => {
  ctx.res.write(`data: This is a SSE message at ${new Date().toISOString()}\n\n`);
 }, 1000);
})

// 将路由注册到应用程序
appWebSocket.use(router.routes()).use(router.allowedMethods())

// 启动服务器
appWebSocket.listen(3000() => {
 console.log('Server started on port 3000')
})

8. 总结

主要介绍了几种前端常见的请求方式:

  1. xhr
  2. fetch
  3. websocket
  4. eventsource
  5. sendBeacon
  6. 常见标签请求

原文地址:https://juejin.cn/post/7274856771410264075

原文作者: yinuo

本文来自掘金文章分享

最后

觉得本文有用的小伙伴,可以帮忙点个“在看”,让更多的朋友看到咱们的文章。