JavaScript网络通信方式

124 阅读3分钟

前言

JavaScript进行网络请求有四种方式,分别为AjaxFetchSSEWebSocket,下面将分别介绍这几种方式的基本使用。

Ajax

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>
<body>
  <button id="btn">中断</button>
  <div id="div"></div>
  <script>
    const btn = document.getElementById('btn');
    const div = document.getElementById('div');

    // Ajax
    const xhr = new XMLHttpRequest();
    // 设置超时时间
    xhr.timeout = 5000;
    // open 请求方法、请求地址、是否异步
    xhr.open("GET", "http://localhost:3000/txt", true);
    // 设置超时回调
    xhr.ontimeout = () => {
      console.log('请求超时');
    }
    // 设置中断回调
    xhr.onabort = () => {
      console.log('请求中断');
    }
    // 设置进度条
    xhr.onprogress = (event) => {
      div.innerHTML = (event.loaded / event.total * 100).toFixed(2) + '%';
    }
    // 设置失败回调
    xhr.onerror = () => {
      console.log('请求失败');
    }
    // readyState 0 1 2 3 4 分别为未初始化、已经调用open方法、已经调用send方法、正在接收数据、数据接收完毕
    // xhr.onreadystatechange = () => {
    //   if (xhr.readyState === 4 && xhr.status === 200) {
    //     console.log(xhr.responseText);
    //   }
    // }
    // onload 指 readyState === 4
    xhr.onload = () => {
      if (xhr.status === 200) {
        // 如果返回的数据为JSON对象,需要自己parse
        console.log(xhr.responseText);
      }
    }
    // 发送请求数据
    xhr.send(null);
    // 中断请求
    btn.addEventListener('click', () => {
      xhr.abort();
    })
  </script>
</body>
</html>
import express from "express";
import fs from "node:fs";
import cors from "cors";

const app = express();

// 处理跨域
app.use(cors());

app.get("/txt", (req, res) => {
  const txt = fs.readFileSync("./index.txt", "utf-8");
  res.send(txt);
});


app.listen(3000, () => {
  console.log("正在监听3000端口...");
});

Fetch

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>
<body>
  <button id="btn">中断请求</button>
  <div id="div"></div>
  <script>
    const btn = document.getElementById('btn');
    const div = document.getElementById('div');
    // 跨平台:浏览器支持 fetch 函数,NodeJS 18版本之后,fetch方法已经内置,不需要再安装node-fetch
    // 默认为 GET 请求
    // 与Ajax的区别 默认只支持GET和POST请求,不支持PUT和DELETE请求
    // 默认不携带cookie
    // 不支持超时控制
    // 不支持中断请求
    // 进度条实现复杂
    const abort = new AbortController();
    fetch("http://localhost:3000/txt", {
      signal: abort.signal,
      method: "DELETE", // 非GET、POST请求,需要后端支持
      headers: {
        "Content-Type": "application/json"
      },
      body: JSON.stringify({
        name: "张三"
      }),
      // credentials: "include" // 携带cookie
    })
      // 返回的数据格式 text文本、json、blob二进制、formData表单数据、arrayBuffer二进制
      .then(response => response.text())
      .then(data => {
        console.log(data);
      }).catch(err => {
        console.log(err);
      });

    // 超时控制
    // setTimeout(() => {
    //   abort.abort();
    // }, 5000);
    
    // 中断控制
    // btn.onclick = () => {
    //   abort.abort();
    // }

    // 进度条
    // fetch("http://localhost:3000/txt")
    //   .then(async (res) => {
    //     // 克隆一份响应对象
    //     const response = res.clone();
    //     // 总数据大小
    //     const contentLength = res.headers.get("Content-Length");
    //     // 读取流一次后,就会被消费掉,所以需要重新获取
    //     const reader = res.body.getReader(); // 读取流
    //     let current = 0;
    //     while (true) {
    //       const { done, value } = await reader.read();
    //       if (done) {
    //         break;
    //       }
    //       current += value.length || 0;
    //       div.innerText = (current / contentLength * 100).toFixed(2) + "%";
    //     }
    //     return response.text();
    //   })
    //   .then(data => {
    //     console.log(data);
    //   })
    //   .catch(err => {
    //     console.log(err);
    //   });
  </script>
</body>
</html>
import express from "express";
import fs from "node:fs";
import cors from "cors";

const app = express();

// 处理跨域
app.use(cors());

// 支持Fetch的各种请求方式
app.use(function (req, res, next) {
  // res.setHeader("Access-Control-Allow-Methods", "*");
  res.setHeader("Access-Control-Allow-Methods", "GET POST PUT DELETE");
  next();
});

app.get("/txt", (req, res) => {
  const txt = fs.readFileSync("./index.txt", "utf-8");
  res.send(txt);
});

app.delete("/txt", (req, res) => {
  const txt = fs.readFileSync("./index.txt", "utf-8");
  res.send(txt);
});


app.listen(3000, () => {
  console.log("正在监听3000端口...");
});

SSE

全称为服务器推送(server-sent events),需要后端配合使用

应用场景:大屏可视化、ChatGPT

SSE 为单工通信,即服务器推送消息给客户端 但是客户端不能推送消息给服务器

WebSocke 全双工通信 双方都可以推送消息

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>
<body>
  <div id="div"></div>
  <script>
    const div = document.getElementById('div');
    
    // sse 服务器推送 server-sent events
    const sse = new EventSource('http://localhost:3000/sse');

    // 默认为 message, 也可以自定义事件名
    // sse.addEventListener("leo", function(event) {
    //   div.innerHTML = event.data;
    // });
    sse.addEventListener("message", function(event) {
      div.innerHTML = event.data;
    });
  </script>
</body>
</html>
import express from "express";
import cors from "cors";

const app = express();

// 处理跨域
app.use(cors());

app.get("/sse", (req, res) => {
  res.writeHead(200, {
    "Content-type": "text/event-stream", // 核心
    "Connection": "keep-alive", // 保持长连接,自动重连
  });

  // 每隔一秒发送一个消息
  setInterval(() => {
    // res.write("event: leo\n");
    res.write(`data: ${new Date().toLocaleTimeString()}\n\n`);
  }, 1000);
});

app.listen(3000, () => {
  console.log("正在监听3000端口...");
});

WebSocket

可以使用ws库实现

安装依赖

npm i ws

前后端代码

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>
<body>
  <div id="div"></div>
  <button id="btn">发送数据给后端</button>
  <script>
    const div = document.getElementById('div');
    const btn = document.getElementById('btn');
    
    // WebSocket
    const ws = new WebSocket('ws://localhost:3000');
    // 连接成功事件监听
    ws.addEventListener("open", (event) => {
      console.log('连接成功');
    });
    ws.addEventListener("message", (event) => {
      div.innerHTML = event.data;
    })
    // 注意不可以发送对象,需要转换成字符串或者二进制
    btn.addEventListener('click', () => {
      ws.send(JSON.stringify({
        type: 'message',
        data: 'Hello World'
      }));
    });
    ws.close();
  </script>
</body>
</html>
import express from "express";
import cors from "cors";
import { WebSocketServer } from "ws";

const app = express();

// 处理跨域
app.use(cors());

const server = app.listen(3000, () => {
  console.log("正在监听3000端口...");
});

// WebSocket 需要依附在 HTTP 服务器上
const wss = new WebSocketServer({ server });

wss.on("connection", (ws) => {
  console.log("有新的 WebSocket 连接");
  setInterval(() => {
    ws.send(new Date().toString());
  });
  ws.on("message", (msg) => {
    console.log("收到消息:" + msg);
  });
})