前端如何获取数据,从 ajax 到 websocket

ajax 是什么

可以在不重新加载整个网页的情况下,对网页的某部分进行更新。(重新拍照理解为重新加载网页)

直白地说,就是没用AJAX的网页,你点一个按钮就要刷新一下页面,尽管新页面上只有一行字和当前页面不一样,但你还是要无聊地等待页面刷新。用了AJAX之后,你点击,然后页面上的一行字就变化了,页面本身不用刷。

AJAX只是一种技术,不是某种具体的东西。不同的浏览器有自己实现AJAX的组件。

手写 ajax
  1. 创建 ajax 对象
  2. 等待数据响应
  3. 调用 open
  4. 调用 send
let xhr = null
try {
  xhr = new XMLHttpRequest()
} catch (error) {
  xhr = new ActiveXObject('error')
}
//必须在调用open()方法之前指定onreadystatechange事件处理程序才能确保跨域浏览器兼容性问题
//只要readyState属性的值有变化,就会触发readystatechange事件
xhr.onreadystatechange = function() {
  if (xhr.readyState === 4) {
    //判断本次下载的状态码都是多少 304表示请求的资源没有被修改
    if ((xhr.status >= 200 && xhr.status < 300) || xhr.status === 304) {
      alert(xhr.responseText)
    } else {
      alert('Error' + xhr.status)
    }
  }
}

xhr.open('get', 'https://api.github.com/repos/vmg/redcarpet/issues?state=closed', true)
xhr.send()

这样写似乎也可以,但是总觉得少点什么?

啥,又要我封装,我能拒绝吗?可以

这答案出乎我意料啊,在拒绝之前,先看一个已经封装好的 promise 版的吧。

Ajax Promise 版
function ajax(method, url) {
  return new Promise((resolve, reject) => {
    const request = new XMLHttpRequest();
    request.open(method, url);
    request.onreadystatechange = () => {
      if (request.readyState === 4) {
        if (request.status === 200) {
          resolve(request.response);
        } else {
          reject(request);
        }
      }
    };
    request.send();
  });
}
ajax("get", "https://api.github.com/repos/vmg/redcarpet/issues?state=closed").then(response => {
  console.log(response);
});

接下来隆重登场,fetch

Fetch

Fetch API 提供了一个 JavaScript 接口,用于访问和操纵 HTTP 管道的一些具体部分,例如请求和响应。它还提供了一个全局 fetch() 方法,该方法提供了一种简单,合理的方式来跨网络异步获取资源。

这种功能以前是使用 XMLHttpRequest 实现的。Fetch 提供了一个更理想的替代方案,可以很容易地被其他技术使用,例如 Service Workers (en-US)。Fetch 还提供了专门的逻辑空间来定义其他与 HTTP 相关的概念,例如 CORS 和 HTTP 的扩展。

fetch('https://api.github.com/repos/vmg/redcarpet/issues?state=closed')
    .then(function (response) {
        return response.json();
    })
    .then(function (myJson) {
        console.log(myJson);
    });
fetch 实现并行请求

通过 promise.all 来实现并行请求

let p1 = new Promise((resolve, reject) => {
  fetch('https://api.github.com/users/suiboyu/starred').then(res =>
    res.json()
  ).then((res) => {
    resolve(res)
  })
})

let p2 = new Promise((resolve, reject) => {
  fetch('https://api.github.com/users/suiboyu/events/public').then(res =>
    res.json()
  ).then((res) => {
    resolve(res)
  })
})

Promise.all([p1, p2]).then((res) => {
  console.log(res)
})

到目前为止,还没有可以取消 fetch 的真正方法。 JavaScript 规范中添加了新的 AbortController,允许开发人员使用信号中止一个或多个 fetch 调用。

以下是取消 fetch 调用的工作流程:

  • 创建一个 AbortController 实例
  • 该实例具有 signal 属性
  • 将 signal 传递给 fetch option 的 signal
  • 调用 AbortController 的 abort 属性来取消所有使用该信号的 fetch
const controller = new AbortController();
const { signal } = controller;

fetch("https://api.github.com/users/suiboyu/events/public", { signal }).then(response =>
  response.json()
).then(res => {
  console.log(res)
}).catch(e => {
  if (e.name === 'AbortError') {
    console.warn(`中断 fetch 请求: ${e.message}`);
  }
});

// Abort request
controller.abort();

以上的方式方式只能实现浏览器发起请求,服务器返回数据,服务器不能主动返回数据。要实现实时数据交互只能是ajax轮询(让浏览器隔个几秒就发送一次请求,然后更新客户端显示。这种方式实际上浪费了大量流量并且对服务端造成了很大压力)。

轮询

实现原理:每隔一段时间发一次请求来获取最新数据,定时器发送 ajax 请求,DOM 操作更新页面数据。

缺点

  • 对服务器造成的压力比较大,耗费资源。请求太多太频繁,如果是访问量比较大的网站,就会造成压力了
  • 会有延迟,数据的实时性不高。并不是数据刚更新就能拿到并更新的,需要请求正好能拿到数据。
  • 数据看起来可能会有紊乱,同一时间你看到的数据和别人的不一样。页面打开开始计算的请求定时器开始时间不一样,对方拿到的可能是刚刚刷新的数据,而你还没去获取最新数据
代码实现
function getData() {
  let xhr = null
  try {
    xhr = new XMLHttpRequest()
  } catch (error) {
    xhr = new ActiveXObject('error')
  }

  xhr.onreadystatechange = function () {
    if (xhr.readyState === 4) {
      if ((xhr.status >= 200 && xhr.status < 300) || xhr.status === 304) {
        alert(xhr.responseText)
      } else {
        alert('Error' + xhr.status)
      }
    }
  }

  xhr.open('get', 'https://api.github.com/repos/vmg/redcarpet/issues?state=closed', true)
  xhr.send()
}

window.onload = function () {
  setInterval(() => {
    getData()
  }, 2000);
}

长轮询

实现原理:请求进来,有数据就返回,没有就夯住(先不把请求响应给前端),直到有数据或者超时再返回(然后立即再发起一个请求过来)

在前端的表现就是请求处于 pending 状态

网页版微信就是利用长轮询实现的:登录微信后会有一个请求发到后端,一直等待(请求处于 pending 状态)后端返回数据,拿到后端数据之后又立马再发一个请求同样等待数据,就这样不停地等着拿数据(后端可能用的是 Queue,q.get() 取数据时没有数据就会夯在那里,等有数据了就接着执行后面的代码,响应给前端),如此往复也就能实现数据的实时获取了

这样做其实不太好,但网页版微信这么做是为了做兼容,ie 还不能很好地兼容 H5 的特性

好处

  • 可以降低延迟(设置一个超时时间,在这段时间内,一有数据就返回)

减少了一定的请求次数,把单纯依靠请求来获取数据变成等待数据主动返回、超时返回相结合(返回了立即再次发起请求等着获取最新数据)

缺点

  • 对服务器造成的压力依旧比较大,耗费资源
代码实现

这个实现方式一般觉察不出什么,相对延迟较低

function ajax(method, url) {
  return new Promise((resolve, reject) => {
    const request = new XMLHttpRequest();
    request.open(method, url);
    request.onreadystatechange = () => {
      if (request.readyState === 4) {
        if (request.status === 200) {
          resolve(request.response);
        } else {
          reject(request);
        }
      }
    };
    request.send();
  });
}

function getData() {
  ajax("get", "https://api.github.com/repos/vmg/redcarpet/issues?state=closed").then(response => {
    if (response !== '') {
      // 拿到数据就更新页面,让用户看到最新信息
      alert(response)
    }
    getData();
  });
}

window.onload = function () {
  getData();
}

Websocket

websocket是HTML5出的东西(协议),是一种全双工通信机制,两端可以及时地互发事件,互发数据,相互通信,只需要浏览器和服务器建立一次连接,服务器就可以主动推送数据到浏览器实现实时数据更新。

协议规定
  1. 连接的时候需要握手(是基于 HTTP 来发起握手的)
  2. 发送的数据需要加密(根据 websocket 协议去发送数据)
  3. 保持链接不断开

接下来我们使用 node + express 搭建后台服务器

mkdir node

npm init -y

npm install express --save

npm install nodejs-websocket
let express = require('express');
let app = express();
const io = require('nodejs-websocket');

io.createServer(connection => {
    console.log('new connection....')

    connection.on('text', function (data) {
        console.log("接收到的客户端消息:" + data);
        connection.sendText("服务器端返回数据:" + data)
    })

    connection.on('close', function (code, reason) {
        console.log('connection closed')
    })

    connection.on('error', () => {
        console.log('close')
    })
}).listen(3000);

// 创建web服务,设定端口号和ip地址 
app.listen(8081, function () {
    console.log("应用实例,访问地址为 http://localhost:8081")
})

前端如何获取数据,从 ajax 到 websocket_第1张图片

初始化前端页面

DOCTYPE html>
<html>

<head>
	<meta charset="utf-8" />
	<title>websocket测试title>
head>

<body>
	<button type="button" id="btn">按钮button>
	<input type="text" name="" id="demo" value="" />
	<div id="res">接收到的服务器端消息显示区域div>
body>

html>
<script src="http://libs.baidu.com/jquery/2.0.0/jquery.min.js">script>
<script type="text/javascript">
	// 打开一个 web socket,设定websocket服务器地址和端口
	var ws = new WebSocket("ws://127.0.0.1:3000");

	//开启连接open后客户端处理方法
	ws.onopen = function () {
		// Web Socket 已连接上,在页面中显示消息
		document.getElementById('res').innerHTML = "当前客户端已经连接到websocket服务器";

	};
	// 点击按钮时给websocket服务器端发送消息
	$('#btn').click(function () {
		var value = $('#demo').val();
		ws.send(value);
	})
	// 接收消息后客户端处理方法
	ws.onmessage = function (evt) {
		console.log(evt.data);
		$('#res').text(evt.data);
	};

	// 关闭websocket
	ws.onclose = function () {
		// 关闭 websocket
		alert("连接已关闭...");
	};

script>
body>

html>

当前端页面发送数据的时候,后台接受到数据,并返回前端页面数据。
前端如何获取数据,从 ajax 到 websocket_第2张图片

前端如何获取数据,从 ajax 到 websocket_第3张图片

你可能感兴趣的:(HTTP,ajax,前端,websocket)