高性能JavaScript——7、Ajax

数据传输

Ajax从最基本层面来说,是一种与服务器通信而无须重载页面的方法;数据可以从服务器获取或者发送给服务器。有多种不同的方法建立这种通信通道,每种方法都有各自的优点和限制。

请求数据

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

  1. XMLHttpRequest (XHR)
  2. Dynamic script tag insertion
  3. iframes
  4. Comet
  5. Multipart XHR

现代高性能JavaScript使用的是:XHR、动态脚本注入和Multipart XHR,其他两种往往在极端情况下使用,不做讨论。

XMLHttpRequest
    var url = '/data.php';
    var params = [
     'id=934875',
     'limit=20'
    ];
    var req = new XMLHttpRequest();
    req.onreadystatechange = function() { 
        if (req.readyState === 4) {
        var responseHeaders = req.getAllResponseHeaders(); // 获取响应头信息
        var data = req.responseText; // 获取数据
        // 数据处理。。。
        }
    }
    
    req.open('GET', url + '?' + params.join('&'), true);
    req.setRequestHeader('X-Requested-With', 'XMLHttpRequest'); // 设置请求头信息
    req.send(null); // 发送请求

XHR是最常用也最强大的数据请求技术,它应当成为你的首选

动态脚本注入

这种技术克服了XHR的最大限制:跨域请求数据。

    var scriptElement = document.createElement('script');
    scriptElement.src = 'http://any-domain.com/javascript/lib.js';
    document.getElementsByTagName_r('head')[0].appendChild(scriptElement);

但是与XHR相比,动态脚本注入提供的控制是有限的:

  • 你不能设置请求的头信息
  • 参数传递也只能使用GET方式,而不是 POST方式
  • 你不能设置请求的超时处理或重试
  • 你必须等待所有数据都已返回,才可以访问它们
  • 你不能访问请求的头信息
  • 不能把整个响应消息作为字符串来处理

使用这种技术从那些你无法直接控制的服务器上请求数据时需要小心。JavaScript没有任何权限和访问控制的概念, 因此你使用动态脚本注入添加到页面中的任何代码都可以完全控 制整个页面。 包括修改任何内容, 把用户重定向到其他网站, 甚至跟踪用户在页面上的操作井发送数据到第三方服务器。 引入外部来源的代码时务必多加小心。

Multipart XHR

MXHR是一项最新的技术,它允许客户端只用一个HTTP请求就可以从服务器向客户端传送多个资源。它通过在服务器将资源(css文件、HTML片段、JavaScript代码等)打包成双方约定的字符串分割的长字符串并发送给客户端。

由于MXHR响应消息的体积越来越大, 因此有必要在每个资源收到时就立刻处理, 而不是等到整个响应消息接收完成。 这可以通过监听readyState为3的状态来实现 ,当readyState为3的状态、 第一次触发时, 启动一个定时器, 每隔15毫秒检查一次响应中的新数据。 数据片段会被收集起来, 直到发现一个分隔符, 然后就把遇到分隔符之前收集的所有数据作为一个完整的资源进行处理。

这个技术有一些缺点,其中最大的缺点是这种方式获取的资源不能被浏览器缓存。

尽管有这些缺点, 但某些情况下 MXHR依然能显著提升页面的整体性能:

  1. 页面包含了大量其他地方用不到的资源,即不需要缓存,尤其是图片;
  2. 网站已经在每个页面中使用一个独立打包的JavaScript或CSS文件以减少HTTP请求;因为对每个页面来说这些文件都是唯一的,所以不需要缓存中读取数据,触发重载页面;

发送数据

当数据只需要发送到服务器时,有两种广泛使用的技术:XHR和信标(beacons)。

XMLHttpRequest

虽然XHR主要用于从服务器获取数据,但也可以将数据传回服务器。

  • GET
    适用于传输少量数据。因为对于少量数据而言,GET请求只需要往服务器发送一个数据包;而POST至少要发送两个数据包(头信息和正文)。

  • POST
    POST适合发送大数据到服务器。因为它不关心额外数据包的数量;另一个原因是IE对URL长度有限制(2048个字符),URL太长了就不能使用GET请求。

Beacons

这个技术非常类似动态脚本注入。它使用JavaScript创建一个新的Image对象,并把src属性设外服务器上脚本的URL。该URL包含了我们要通过GET请求传回的键值对数据。

    var url = '/status_tracker.php';
    var params = [
     'step=2',
     'time=1248027314'
    ];
    (new Image()).src = url + '?' + params.join('&');

服务器会接收到数据并保存下来,它无须向客户端发送任何回馈信息,因此没有图片会实际显示出来。

这是给服务器回传信息最有效的方式。它的性能消耗很小,而且服务端的错误完全不会影响到客户端。

你可以接收服务器返回的数据, 但只局限于非常少的几种方式:

  • 一种方式是监听Image对象的load事件, 它会告诉你服务器是否成功接收数据。
  • 你还可以检查服务器返回的图片的宽度和高度(如果返回的是图片)并使用这些数字通知你服务器的状态。举个例子,宽度为1表示“成功”, 为2表示“重试”。

信标是向服务器回传数据最快且最有效的方式。服务器根本不需要发送任何响应正文, 因此你也无须担心客户端下载数据。
唯一的缺点是你能接收到的晌应类型是有限的。 如果你需要返回大量数据给客户端, 那么请使用XHR。 如果你只关心发送数据到服务器(可能需
要极少的返回信息), 那么请使用图片信标。

数据格式

当考虑数据传输技术肘, 你必须考虑这些因素: 功能集 、 兼容性、 性能以及方向(发送给服务器还是从服务器接收)。 当考虑数据格式时, 唯一需要比较的标准就是速度

一种可能下载更快,而另一种可能解析更快。

XML

    <?xml version="1.0" encoding='UTF-8'?>
    <users total="4">
        <user id="1"> 
            <username>alice</username>
            <realname>Alice Smith</realname>
            <email>alice@alicesmith.com</email>
        </user>
        <user id="2">
            <username>bob</username>
            <realname>Bob Jones</realname>
            <email>bob@bobjones.com</email>
        </user>
        <user id="3">
            <username>carol</username>
            <realname>Carol Williams</realname>
            <email>carol@carolwilliams.com</email>
        </user>
        <user id="4">
            <username>dave</username>
            <realname>Dave Johnson</realname>
            <email>dave@davejohnson.com</email>
        </user>
    </users>

优化版本:

    <?xml version="1.0" encoding='UTF-8'?>
    <users total="4">
        <user id="1-id001" username="alice" realname="Alice Smith" email="alice@alicesmith.com" />
        <user id="2-id001" username="bob" realname="Bob Jones" email="bob@bobjones.com" />
        <user id="3-id001" username="carol" realname="Carol Williams" email="carol@carolwilliams.com" />
        <user id="4-id001" username="dave" realname="Dave Johnson" email="dave@davejohnson.com" />
    </users>

简化版的XML更有效率,但是比那些最快的格式依然慢上一个数量级。在高性能Ajax中,XML没有立足之地。

JSON

JSON数据就是一段可执行的JavaScript代码。
在JavaScript中可以简单地使用eval()或者JSON.parse 来解析JSON字符串。
代码中使用eval是 很危险的,特别是用它执行第三方的JSON数据(其中可能包含恶意代码)时。尽可能使用JSON.parse()方法解析字符串本身。

   [
        {"id":1, "username":"alice", "realname": "Alice Smith", "email":"alice@alicesmith.com"},
        {"id":2, "username":"bob", "realname": "Bob Jones", "email":"bob@bobjones.com"},
        {"id":3, "username":"carol", "realname": "Carol Williams","email":"carol@carolwilliams.com"},
        {"id":4, "username":"dave", "realname": "Dave Johnson", "email":"dave@davejohnson.com"}
    ]

简化版

    [
        { "i": 1, "u": "alice", "r": "Alice Smith", "e": "alice@alicesmith.com" },
        { "i": 2, "u": "bob", "r": "Bob Jones", "e": "bob@bobjones.com" },
        { "i": 3, "u": "carol", "r": "Carol Williams", "e": "carol@carolwilliams.com" },
        { "i": 4, "u": "dave", "r": "Dave Johnson", "e": "dave@davejohnson.com" }
    ]

进一步简化

[
    [ 1, "alice", "Alice Smith", "alice@alicesmith.com" ],
    [ 2, "bob", "Bob Jones", "bob@bobjones.com" ],
    [ 3, "carol", "Carol Williams", "carol@carolwilliams.com" ],
    [ 4, "dave", "Dave Johnson", "dave@davejohnson.com" ]
]

JSONP
事实上,JSON可以被本地执行会导致几个重要的性能影响。 当使用XHR 时,JSON数 据被当成字符串返回。 该字符串紧接着被eval()转换成原生对象。 然而, 在使用动态脚本注入时,JSON数据被当成另一个JavaScript文件并作为原生代码执行。

文件大小和下载耗时与XHR测试基本相同,而解析速度几乎快了10倍。

有一种情形要避免使用JSON-P(与性能无关),因为JSON-P必须是可执行的JavaScript, 它可能被任何人调用并使用动态脚本注入技术插入到任何网站。 而另 方面,JSON在eval 前是无效的JavaScript,使用XHR时它只是被当作字符串获取。 所以不要把任何敏感数据编码在JSON-P中, 因为你无桂确认它是否保持着私有调用状态, 即便是带有甚至随机 URL或做了cookie判断。

应该使用 JSON吗?

JSON对Web开发人员来说最重要的是, 它是性能表现最好的数据格式, 因为它不仅传输尺寸小, 而且解析速度快。JSON 是高性能 Ajax 的基础, 尤其在使用动态脚本注入时。

HTML

一种可考虑的技术是在服务器端构建好整个HTML再传回客户端,JavaScript可以通过innerHTML属性把它插入页码相应位置。

HTML传输数据量明显偏大, 还需要较长的时间来解析。 这是因为把 HTML 插入DOM 的单一操作看似简单,尽管它只有一行代码,却需要大量时间把更多数
据载入页面。 与其他数据相比, 这些性能数据和其他格式的数据相比有 些许偏差, 因为最终的结果不是数据数组,而 是 显示在页面上的HTML元素。无论如何 ,它仍然说明了关于 HTML的一个事实:作为一 种数据格式, 它既缓慢, 又臃肿

自定义

理想的数据格式应该只包含必要的结构, 以便你可以分解出每一个独立的字段。你可以很容易地定义一 种这样的格式, 只须简单地把数据用分隔符连接起来

    1:alice:Alice Smith:alice@alicesmith.com;
    2:bob:Bob Jones:bob@bobjones.com; 
    3:carol:Carol Williams:carol@carolwilliams.com;
    4:dave:Dave Johnson:dave@davejohnson.com

这种格式非常简洁,“数据/结构”比例相当高,比其他任何格式都高(除了纯文本)。只需要简单地调用字符串split()方法并传入分隔符作为参数即可,复杂一点的加上循环就好了。JavaScript中的循环和split()方法都是相当快的。

XHR和动态脚本注入都可以使用这种格式。两种情况下都要解析字符串,因此没有实质上的性能差异。对于非常大的数据集,它是最快的格式,甚至在解析速度和平均加载时间上都能击败本地执行的JSON。当你需要在很短的时间内向客户端传送大量数据时可以考虑使 用这种格式。

格式总结

通常来说数据格式越轻量级越好,JSON和字符分隔的自定义格式是最好的。如果数据集很大并且对解析时间有要求,那么就从如下两种格式中做出选择:

  • JSON-P数据,使用动态、脚本注入获取。它把数据当作可执行JavaScript而不是字符串,解析速度极快。它能跨域使用,但涉及敏感数据时不应该使用它。
  • 字符分隔的自定义格式,使用XHR或动态脚本注入获取,用split()解析。这项技术解析大数据集比JSON-P稍快,而且通常文件尺寸更小。

性能指南

缓存数据

最快的Ajax 请求就是没有请求。 有两种主要的方法可避免发送不必要的请求:

  • 在服务端,设置HTTP头信息以确保响应会被浏览器缓存;
  • 在客户端,把获取到的信息存储在本地,以避免再次请求。

了解Ajax类库的局限

所有得JavaScript库都允许你访问一个Ajax对象,它屏蔽浏览器之间的差异,给你一个统一的接口。大多数情况下这非常好,因为它使你可以关注你的项目,而不是那些古怪的浏览器上XHR的工作细节。然而,为了给你一个统一的接口,这些库必须简化接口,因为不是所有浏览器都实现了每个功能。这使得你不能访问XMLHttpRequest的完整功能。

直接操作XHR对象减少了函数的开销,进一步提升了性能。但是,如果放弃Ajax类库,那么你可能在一些古怪的浏览器上遇到一些问题。

小结

高性能的Ajax包括以下方面:了解你项目的具体需求,选择正确的数据格式和与之匹配的传输技术。

作为数据格式:

  • 纯文本和HTML只适用于特定场合,但它们可以节省客户端的CPU周期。
  • XML被广泛应用而且支持良好,但是它十分笨重且解析缓慢。
  • JSON是轻量级的,解析速度快(被视为原生代码而不是字符串),通用性与XML相当。
  • 字符分隔的自定义格式十分 轻量,在解析大量数据集时非常快,但需要编写额外的服务端构造程序,并在客户端解析。

当从页面当前所处的域下请求数据时:

  • XHR提供了最完善的控制和灵活性。尽管它会把接收到的所有数据当成个字符串,且这有可能降低解析速度。
  • 另一方面,动态脚本注入允许跨域请求和本地执行JavaScript和JSON。但是它的接口不那么安全,而且还不能读取头信息或响应代码。
  • Multipart XHR可以用来喊少请求数,并处理一个响应中的各种文件类型,但是它不能缓存接收到的响应。
  • 当需要发送数据时,图片信标是一种简单而有效的方法。
  • XHR还可以用POST方法发送大量数据。

除了这些格式和传输技术,还有一些准则有助于加速你的Ajax:

  • 减少请求数,可通过合井JavaScript和css文件,或使用MXHR。
  • 缩短页面的加载时间,页面主要内容加载完成后,用Ajax获取那些次要的文件。
  • 确保你的代码错误不会输出给用户,并在服务端处理错误。
  • 知道何时使用成熟的Ajax类库,以及何时编写自己的底层Ajax代码。

Ajax为提升你的网站的潜在性能提供了广阔的空间, 因为许多网站大量使用异步请求, 而且它还提供了一些与它无关的问题的解决方案, 比如有过多的资源需要加载。 对XHR创造性地使用是一个反应迟钝且平谈无奇的页面与响应快速且高效的页面的区别所在;是一 个用户痛恨使用的站点与用户迷恋的站点的区别所在。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值