写点什么

高性能 JavaScriptの六 -- 老生常谈 Ajax

用户头像
空城机
关注
发布于: 2021 年 05 月 23 日
高性能 JavaScriptの六 -- 老生常谈Ajax

Ajax

Ajax 全称:Asynchronous Javascript And XML (异步 JavaScript 和 XML)


有关 Ajax 的博客其实数不胜数了,我之前写过的博客中也存在一些 Ajax 的内容

Ajax老东西了

不过 Ajax 也是高性能 JavaScript 的基础知识,可以通过延迟下载体积较大的资源文件来使得页面加载速度更快。

通过异步方式在客户端和服务端之间传输数据,甚至可以只用一个 HTTP 请求就获取整个页面的资源(肯定不是赞成这种方式


数据传输

Ajax 从最基本层面上: 一种与服务器通信而无须重载页面的方法

请求数据

五种常用技术用于向服务器请求数据:

  • XMLHttpRequest(XHR)

  • Dynamic script tag insertion 动态脚本注入

  • iframes

  • Comet

  • Multipart XHR


当然在目前来说,只有三种比较常用,那就是 XHR、动态脚本注入和 Multipart XHR

XMLHttpRequest(XHR)

这是目前最常用的技术,允许异步发送和接收数据


MDN介绍

XMLHttpRequest 是 Ajax 的核心,所有主流浏览器对它都提供了完善的支持,也就是说不支持 XHR 的都是非主流浏览器

浏览器不支持XHR就不是主流了

使用范例:

let url = '接口名称';let params = ['id=123', 'name=456']  //请求参数const req = new XMLHttpRequest();req.onreadystatechange = function(){    if(req.readyState === 4) {  //判断响应状态是否成功        let responseHeaders = req.getAllResponseHeaders(); //获取响应头信息        let data = req.responseText; //获取响应数据        // 数据处理    }}// 这是get方法&des=get方法的参数直接在接口后面追加req.open('GET',url + '?' + params.join('&'), true);req.setRequestHeader('X-Requested-with','XMLHttpRequest');//设置请求头信息req.send();//发送请求
复制代码


readyState 状态值有五种状态 (这和 status 状态码还是有区别的):

  • 0 - (未初始化)还没有调用 send()方法

  • 1 - (载入)已调用 send()方法,正在发送请求

  • 2 - (载入完成)send()方法执行完成,已经接收到全部响应内容

  • 3 - (交互)正在解析响应内容,接收到部分信息,但不是所有

  • 4 - (完成)响应内容解析完成,可以在客户端调用了, 接收到了所有信息


status 是 XMLHttpRequest 对象的一个属性,表示响应的 HTTP 状态码

举个例子:404,也就是我们常说的和谐之光,实际指的是没有发现文件或者 URL 没有找到对应页面


区别:

  • readyState,是指运行请求所经历过的几种状态,无论访问是否成功都将响应的步骤

  • status,是指无论访问是否成功,由 HTTP 协议根据所提交的信息,服务器所返回的 HTTP 头信息代码


由于 XHR 提供了高级的控制,所以浏览器增加了一些使用 XHR 的限制

XHR越强,限制越多

最出名的限制就是同源策略了,不能使用 XHR 从外域请求数据


至于低版本的 IE 不仅不支持“流”,也不会提供 readyState 为 3 的状态,从服务器传回的数据会被当作字符串或者 XML 对象,导致处理大量数据变慢。 不过这一点我认为可以略过了,这里低版本 IE 指 IE6 及以下,如果现在做的项目还需要兼容这些浏览器,那真的也没什么可说的了

readyState兼容性


注意(下面主要是从性能上说)

  • 不管直接使用 XHR 还是 Ajax 请求数据时,都需要在 POST 和 GET 中选择一下

  • 如果是不需要改变服务器状态,那么建议使用 GET 请求。 GET 好处是请求数据会被缓存起来,如果需要多次请求同一数据,会有助于提升性能

  • 当 URL 加上参数的长度超过或接近 2048 个字符时,用 POST 获取数据比较好。IE 的 url 最大长度是 2083 个字节,可以用于 GET 传递数据的长度是 2048 个字节


动态脚本注入

这种技术最大的好处,就是<font color=#F00>相较于 XHR 来说,能够进行跨域请求


因为这是一个 Hack,所以不需要实例化一个对象,可以直接用 JavaScript 创建一个新的脚本标签,并设置 src 属性为不同域的 URL。

let scriptElement = document.createElement('script');scriptElement.src = '路径';document.getElementsByClassName('head')[0].appendChild(scriptElement);
复制代码


这一点其实大部分人也看到过,前端的跨域解决方案之一就是可以使用 script 标签进行跨域


你以为这种方式解决跨域就好了吗?



  • 和 XHR 相比,动态脚本注入控制有限,参数传递也只能使用 GET 方式,而不是 POST 方式

  • 并且不能设置请求超时处理或重试,所以就算请求失败,那你也不知道

  • 这里没有 readyState,数据必须全部返回后才可以访问

  • 不能访问请求的头部信息,也不能把整个响应消息作为字符串来处理


这缺点好多/(ㄒoㄒ)/~~

吐血


注意

虽然缺陷比较多,但是这项技术的速度非常快,响应消息是作为 JavaScript 执行的。

引入外部 JavaScript 代码时一定要多加小心,这种技术从你无法直接控制的服务器上请求数据时可能会有危险,JavaScript 没有任何权限和访问控制的概念,谁知道你引入的到底是什么东西

Multipart XHR

MXHR 允许客户端用一个 http 请求就从服务器想客户端传送多个资源。


Multipart XHR 通过在服务器将资源打包成一个由双方约定的字符串分割的长字符串发送到客户端,然后在客户端解析这个长字符串,根据 mime-type 类型和传入的其他信息解析出每个资源


这个其实也是基于 XHR 的变种吧?

信标(beacons)

这项技术类似于动态脚本注入


使用 JavaScript 创建一个新的 Image 对象, 并把 src 属性设置为服务器上的脚本 URL.。该 URL 包含了我们要通过 GET 传回的键值对数据。信标无法发送 POST 请求


请注意并没有创建 img 元素或把它插入 DOM

var url = '接口名称';var params = [数据]var beacon = new Image();beacon.src = url + '?' + params.join('&');// 用load事件监听服务器是否成功接收数据// 可以在服务端做出不同的响应,比如返回x宽的空白图片代表成功接收,y宽的空白图片代表接收失败beacon.onload = function() {    if (this.width == x) {        // 成功    } else if (this.width == y){        // 失败,请重试    }}beacon.onerror = function() {    // 出错,稍后重试并创建另一个信标}
复制代码



信标的好处是性能消耗比较小,而且服务器端的错误不会影响到客户端,数据传输很快但是,能够接收到的响应类型是有限的,而且传输的数据量也很小,数据量大一些还是用 XHR 吧


范例:HTML 页面:

<!DOCTYPE html><html lang="en"><head>    <meta charset="UTF-8">    <meta http-equiv="X-UA-Compatible" content="IE=edge">    <meta name="viewport" content="width=device-width, initial-scale=1.0">    <title> beacons </title></head><body>    <button onclick="beaconbtn()"> 信标 </button>    <script>        function beaconbtn() {            var url = 'http://localhost:3001/goodsList.png';            var params = ['id=123', 'name=空城机', 'data=测试数据是否能够在后台接收']  //发送的数据            var beacon = new Image();            beacon.src = url + '?' + params.join('&');            // 用load事件监听服务器是否成功接收数据            // 可以在服务端做出不同的响应,比如返回x宽的空白图片代表成功接收,y宽的空白图片代表接收失败            beacon.onload = function() {                console.log(this)                if (this.width == 1) {                    // 成功                    console.log("服务器获取数据成功")                } else {                    // 失败,请重试                    console.log("服务器获取数据失败,请重试")                }            }            beacon.onerror = function() {                // 出错,稍后重试并创建另一个信标            }        }    </script></body></html>
复制代码


Node.js 服务器:

const fs = require('fs');const path = require('path');const url = require('url');const express = require('express');
var app = express();app.use('/',function(req, res) { //解析出请求的文件路径 var pathname = url.parse(req.url).pathname; console.log('接收到请求,请求的是:' + pathname); console.log('服务器接收:') console.log(req.query) fs.readFile(pathname.substr(1), function(err, data) { if (err) { console.log(err) } else { res.end(data) } }); })
//服务器本地主机的数字app.listen('3001',function(){ console.log("启动了。。。, 运行 http:localhost:3001/beacons.html ")})
复制代码


VS Code 终端获取的数据:


HTML 界面效果:


数据格式

当考虑数据传输技术时,你必须考虑这些因素:功能性、兼容性、性能以及方向(你发送给服务器还是服务器发送给你)


如果考虑数据格式,那唯一需要比较的标准就是速度,没有哪一种数据格式会始终比其他格式更好,但目前 JSON 最主流

XML

最初 Ajax 开始流行时,选择的是 XML 作为数据格式 PS:那时 JSON 还没正式作为交换格式


但是相比其他格式,XML 极其冗长,对 JavaScript 程序员解析时十分费力,不能轻易完成。最重要的是 XML 解析起来的性能你远比其他数据格式慢,在高性能 Ajax 中,XML 已经没有立足之地了


所以我建议,不是特别老的项目就不要用这个 XML 了,让前端轻松一点吧!!!


JSON

嗯,这是目前最流行的数据格式,其实也不需要我多说,大部分前后端数据交流应该都会用到

JSON 是一种使用JavaScript对象和数组直接来编写的轻量级且易于解析的数据格式


上面 JSON 的定义中已经把好处说的明明白白了,JSON 是高性能 Ajax 的基础

小知识:JavaScript 中可以使用 eval 来解析 JSON 字符串,但是在代码中使用eval其实很危险,特别是用于执行可能包含恶意代码的第三方 JSON 数据。 所以尽可能使用JSON.parse()来解析


HTML

这种数据格式与 XML 类似,JavaScript 可以将较大的数据结构转换为简单的 HTML,但是服务器处理更快,服务器可以处理好整个 HTML 传回客户端。

但是这项技术的缺点也十分明显,数据更加臃肿,在网络中传输速度会变慢

自定义格式

理想的数据格式应该只包含必要结构,可以自己定义一种这样的格式,简单地把数据用分隔符连接起来

Jojo;data;hello world;the;people;new;
复制代码

这种方式速度最快,可以使用 split 分割字符串的方式进行。 但这里需要确认的是分割的字符是设定好的,不然可能会分割出错


Ajax 性能指南

最快的 Ajax 请求就是没有请求


有两种主要方法可以避免发送不必要的请求:

  • 在服务端,设置 HTTP 头信息以确保你的响应会被浏览器缓存

  • 在客户端,把获取到的信息存储到本地,从而避免再次请求


第一种技术使用最简单而且好维护,第二种技术给你最大的控制权

设置 HTTP 头信息

如果希望 Ajax 响应能被浏览器缓存,那么必须使用 GET 方式发送请求


还需要在响应中发送正确的 HTTP 头部信息

设置 Expires 是最简单确保浏览器缓存 Ajax 响应的方式


本地数据存储

这种方式就是比较手工了

把从服务器接收到的数据存到本地,比如说 cookie、local storage


这里就不多介绍了,可以参考以下文章:

Cookie、Session、AJAX、JSON

浅谈cookie、sessionStorage 和 localStorage



小节

一些准则有利于加速 Ajax:

  • 减少请求数,可以通过合并 JavaScript 和 CSS 文件,或者使用提过一次 Multipart XHR

  • 缩短页面加载时间,待主要内容加载后,才使用 Ajax 获取次要文件

  • 确保代码错误不输送给用户,服务端要处理好错误

  • 使用一些比较成熟的 Ajax 类库,比如 jQuery.ajax,或者自己编写底层 Ajax 方法代码


顺便推荐一波别人的讲解 狗头.jpg:教你怎么用ajax来进行交互(入门必看)!!!

发布于: 2021 年 05 月 23 日阅读数: 36
用户头像

空城机

关注

曾经沧海难为水,只是当时已惘然 2021.03.22 加入

业余作者,在线水文 主要干前端的活,业余会学学python 欢迎各位关注,互相学习,互相进步

评论

发布
暂无评论
高性能 JavaScriptの六 -- 老生常谈Ajax