AJAX异步无刷新请求上传

740 阅读4分钟

这是我参与8月更文挑战的第24天,活动详情查看:8月更文挑战

1、传统请求交互的特点和问题

1-1、传统请求交互的特点

客户端请求 -> 服务端(node web server)处理请求 -> 处理数据(数据+模板的渲染) -> 返回渲染后的数据到前端。

问题:

  • 处理数据(数据+模板的渲染):服务端需要每次对数据和模板进行渲染、占用服务端的资源。

  • 如果当下获取的数据只是一个很小部分,那么为什么这一点数据,需要返回一整个渲染好的页面

  • 每次请求新的内容会通过浏览器重新发送一个请求(如果是通过浏览器发送,那么得到返回的数据的,浏览器会重载整个页面)

  • 服务端性能

  • 用户体验

2、异步无刷新

通过浏览器提供的一个 JS API , 它可以向服务端发送请求,这个请求返回的数据将被 JS 所接收,然后我们就可以通过 JS 来控制这个数据的渲染(DOM操作)。

现代浏览器支持多个具有 HTTPAPI

XMLHttpRequest

优势:出现较早,浏览器支持最好的 API

劣势:API 相对陈旧,一些现代 JS 语言特性需要封装(如:Promise)。

Fetch

优势:较新,对一些特性有天然良好的支持(如:PromiseCORS)。

劣势:兼容、缺少事件支持。

3、异步请求流程与实现 - XMLHttpRequest

XMLHttpRequest(XHR)对象用于与服务器交互。通过 XMLHttpRequest 可以在不刷新页面的情况下请求特定 URL ,获取数据。这允许网页在不影响用户操作的情况下,更新页面的局部内容。XMLHttpRequestAJAX 编程中被大量使用。

参考:developer.mozilla.org/zh-CN/docs/…

使用 XHR 与服务端通信的基本流程如下:

3-1、创建 XMLHttpRequest 对象

let xhr = new XMLHttpRequest();

3-2、配置请求参数

xhr.open(string method, string url, boolean async);

method:

HTTP 请求方法,支持:GETPOSTPUTPATCHDELETE ……。

url:

请求资源的 URL

async:

是否使用 异步模式 发送请求,默认为 true ,且推荐在主进程中是用 true

使用 false 表示使用 同步模式 ,会影响用户体验。

参考:developer.mozilla.org/zh-CN/docs/…

3-3、发送请求

只有调用 send 方法,请求才正式发出:

xhr.send();

3-4、监听请求相关事件

因为我们使用了 异步 的方式来发送请求,那么就需要通过 事件 回调的方式来得到相关的反馈。

我们知道,一次交互会包含两个阶段:请求(数据上传)响应(数据下载)

响应相关的事件:

loadstart:

接收到响应数据时触发。也可以使用 onloadstart 属性。

progress:

当请求接收到更多数据时,周期性地触发。也可以使用 onprogress 属性。

loadend:

当请求结束时触发, 无论请求成功 ( load) 还是失败 (aborterror)。也可以使用 onloadend 属性。

load:

XMLHttpRequest请求成功完成时触发。也可以使用 onload 属性。

timeout:

在预设时间内没有接收到响应时触发。也可以使用 ontimeout 属性。

abort:

当 request 被停止时触发,例如当程序调用 XMLHttpRequest.abort() 时。也可以使用 onabort 属性。

error:

当 request 遭遇错误时触发。也可以使用 onerror 属性。

xhr.onload = function() {
  console.log('请求完成了');
}

3-5、获取数据

xhr 对象还提供了许多的属性和方法,来帮助我们完成一些关键信息的获取:

readyState:

请求的状态码,参考:developer.mozilla.org/zh-CN/docs/…

同时还提供一个 readystatechange 事件来监听 readyState 的变化。

response:

返回的原始内容,取决于 responseType

responseType:

返回响应数据的类型,可设置。参考:developer.mozilla.org/zh-CN/docs/…

responseText:

返回转换成文本类型的响应数据。类似:responseType='text'response 的内容。

status:

响应状态码。

statusText:

响应状态文本。

timeout:

设置请求超时时间。

upload:

上传相关。

withCredentials:

跨域授权相关。

getAllResponseHeaders():

获取所有响应头信息。

getResponseHeader(string name):

获取指定头信息。

overrideMimeType(string mimeType):

覆写响应头信息。

setRequestHeader(string header, string value):

XMLHttpRequest.setRequestHeader() 是设置HTTP请求头部的方法。此方法必须在 open() 方法和 send() 之间调用。

abort():

取消请求。

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

4、请求配置参数与数据的设置

4-1、动态路由

...
let itemId = 1;
xhr.open('get', `/item/${itemId}`);
...

4-2、queryString

...
let page = 1;
let limit = 5;
xhr.open('get', `/items?page=${page}&limit=${limit}`);
...

4-3、正文

urlencoded

...
let username = 'zMouse';
let password = '123456';
xhr.open('post', '/login');

// 设置头信息
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
// 正文通过 send 方法传入
xhr.send(`username=${username}&password=${password}`);

json

...
let username = 'zMouse';
let password = '123456';
xhr.open('post', '/login');

// 设置头信息
xhr.setRequestHeader('Content-Type', 'application/json');
// 正文通过 send 方法传入
xhr.send( JSON.stringify( {username, password} ) );

form-data

...
let username = 'zMouse';
let password = '123456';
xhr.open('post', '/login');

// 使用 FormData 来构建数据
let fd = new FormData();
fd.append('username', username);
fd.append('password', password);
// 直接把 FormData 作为参数,xhr 会在发送请求的时候自动设置对应的 头信息 和 对应的数据格式
xhr.send( fd );

5、上传与下载

xhr 提供了一个 upload 属性,它是一个 XMLHttpRequestUpload 对象,用来表示上传的进度。

事件相应属性的信息类型
onloadstart获取开始
onprogress数据传输进行中
onabort获取操作终止
onerror获取失败
onload获取成功
ontimeout获取操作在用户规定的时间内未完成
onloadend获取完成(不论成功与否)
...
// <input type="file" id="avatar" />
let avatar = document.querySelector('#avatar');

avatar.onchange = function() {
  
  let xhr = new XMLHttpRequest();
  
  xhr.open('post', '/avatar');
  
  xhr.upload.onloadstart = function() {
    console.log('开始上传');
  }
  xhr.upload.onprogress = function(e) {
    console.log( `上传进度: ${e.loaded}/${e.total} ` );
  }
  xhr.upload.onload = function() {
    console.log(`上传成功`);
  }
  
  let fd = new FormData();
  // 获取 input 中选择的文件
  fd.append('avatar', avatar.files[0]);
  
  xhr.send(fd);
}

...