编写高性能的JavaScript-Ajax数据传输之MXHR(4)
动态载入脚本
这个技术的特点克服了XHR最大的缺点:跨域访问。使用如下:
var scriptElement = document.createElement('script');
scriptElement.src = 'http://any-domain.com/javascript/lib.js';
document.getElementsByTagName('head')[0].appendChild(scriptElement);
动态载入脚本不像XHR那样可以随意设置请求头部,参数只能通过GET发送,只有脚本从服务器完全返回以后你才能访问脚本。
由于这种方式的响应结果是可执行的JS代码,不需要特殊的处理和分析,因而性能十分可观。不过你仍然需要注意,使用这种方式时你对服务器完全没有控制,你无法获得脚本载入期间的信息。而且你也要意识到动态载入的脚本一旦加载完成,它就能够对本页面的数据内容进行修改、转发或者重定向,所以使用时需要格外小心。
更多信息可以看这里:http://book.51cto.com/art/200812/99672.htm
Mulipart XHR
这个是以上Ajax请求数据几种方式中最新的技术。MXHR的特点是,只使用一个请求来从服务器将资源发送给客户端,只要组装好了不论是CSS文件、脚本文件、HTML片段还是base64编码的图片。客户端以字符串的形式接受,然后根据MIME类型和其它传输的信息来解析。
示例-使用一个请求下载多个图片资源:
var req = new XMLHttpRequest();
req.open('GET', 'rollup_images.php', true);
req.onreadystatechange = function() {
if (req.readyState == 4) {
splitImages(req.responseText);
}
};
req.send(null);
首先请求页面,然后将响应数据利用方法解析。当然这需要服务器首先要将图片信息转化为base64编码的字符串,同时你需要指定一个连接符号,以便在客户端分割这些字符串。
假定分割符是unidcode编码的1
function splitImages(imageString) {
var imageData = imageString.split("\u0001");
var imageElement;
for (var i = 0, len = imageData.length; i < len; i++) {
imageElement = document.createElement('img');
imageElement.src = 'data:image/jpeg;base64,' + imageData[i];
document.getElementById('container').appendChild(imageElement);
}
}
任何能够在JS中通过字符串处理的数据类型都可以使用这种方式传输。以下是几个常用函数:
function handleImageData(data, mimeType) {
var img = document.createElement('img');
img.src = 'data:' + mimeType + ';base64,' + data;
return img;
}
function handleCss(data) {
var style = document.createElement('style');
style.type = 'text/css';
var node = document.createTextNode(data);
style.appendChild(node);
document.getElementsByTagName('head')[0].appendChild(style);
}
function handleJavaScript(data) {
eval(data);
}
当MXHR传输的数据越来越庞大时,最好的方式是接受一部分数据以后立刻开始处理,而不是等待全部数据加载完成再处理。通过监听XHR的readyState 3来处理。
var req = new XMLHttpRequest();
var getLatestPacketInterval, lastLength = 0;
req.open('GET', 'rollup_images.php', true);
req.onreadystatechange = readyStateHandler;
req.send(null);
function readyStateHandler{
if (req.readyState === 3 && getLatestPacketInterval === null) {
// Start polling.
getLatestPacketInterval = window.setInterval(function() {
getLatestPacket();
}, 15);
}
if (req.readyState === 4) {
// Stop polling.
clearInterval(getLatestPacketInterval);
// Get the last packet.
getLatestPacket();
}
}
function getLatestPacket() {
var length = req.responseText.length;
var packet = req.responseText.substring(lastLength, length);
processPacket(packet);
lastLength = length;
}
一旦readyState 3第一次触发,一个计时器启动。每隔15ms检测新数据,当找到一个事先约定的分隔符之后收集数据,然后处理这部分数据。
更完美的实现比较复杂但是值得研究,你可以看看这里:http://techfoolery.com/mxhr/
这个技术的缺点就是获取的数据你无法缓存,比如图片、CSS文件等;另外就是老版本的IE不支持监听readyState 3,也不支持data:URLs。(IE8支持,IE6和IE7需要变通方法)
不过由于它的优点实在是吸引人,所以这些情况你最好使用MXHR:
1)如果这个页面包含很多其他页面不需要的资源,尤其是图片;
2)如果站点的每个页面的脚本或CSS都已经组装为一个独特的请求,亦即站点内跨页面访问没有共同的请求。
减少HTTP请求能够全面提升页面的性能,尤其是你需要传输100张图片,只用一个请求通常能为站点的用户体验带来质的飞跃。测试页面:http://techfoolery.com/mxhr/
PS:content is from <High Performance JavaScript> by Nicolas Zakas