《高性能JavaScript》第七章 Ajax
7.1 數(shù)據(jù)傳輸
Ajax從最基本層面來說,是一種與服務(wù)器通信而無須重載頁面的方法矮烹;數(shù)據(jù)可以從服務(wù)器獲取或者發(fā)送給服務(wù)器。有多種不同的方法建立這種通信通道嘴办,每種方法都有各自的優(yōu)點和限制茸塞。
7.1.1 請求數(shù)據(jù)
五種常用技術(shù)向服務(wù)器請求數(shù)據(jù):
- XMLHttpRequest (XHR)
- Dynamic script tag insertion
- iframes
- Comet
- Multipart XHR
現(xiàn)代高性能JavaScript使用的是:XHR
、動態(tài)腳本注入
和Multipart XHR
描沟,其他兩種往往在極端情況下使用飒泻,不做討論。
XMLHttpRequest
XHR是目前最常用的技術(shù)吏廉,它允許異步發(fā)送和接收數(shù)據(jù)泞遗。
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(); // 獲取響應(yīng)頭信息
var data = req.responseText; // 獲取數(shù)據(jù)
// 數(shù)據(jù)處理。席覆。刹孔。
}
}
req.open('GET', url + '?' + params.join('&'), true);
req.setRequestHeader('X-Requested-With', 'XMLHttpRequest'); // 設(shè)置請求頭信息
req.send(null); // 發(fā)送請求
注意:由于XHR提供了高級的控制,所以瀏覽器對其增加了一些限制娜睛。你不能使用XHR從外域請求數(shù)據(jù),而且低版本IE不僅不支持“流”卦睹,也不會提供
readyState
為3的狀態(tài)畦戒。
動態(tài)腳本注入
這種技術(shù)克服了XHR的最大限制:跨域請求數(shù)據(jù)。
var scriptElement = document.createElement('script');
scriptElement.src = 'http://any-domain.com/javascript/lib.js';
document.getElementsByTagName_r('head')[0].appendChild(scriptElement);
但是與XHR相比结序,動態(tài)腳本注入提供的控制是有限的障斋。不能設(shè)置請求頭,只能用GET
,不能設(shè)置超時處理等垃环。最后一點特別重要邀层,不能使用純XML
、純JSON
或其他格式數(shù)據(jù)遂庄,無論哪種格式寥院,都必須封裝在一個回調(diào)函數(shù)里。
var scriptElement = document.createElement('script');
scriptElement.src = 'http://any-domain.com/javascript/lib.js';
document.getElementsByTagName_r('head')[0].appendChild(scriptElement);
function jsonCallback(jsonString) {
var data = ('(' + jsonString + ')');
// 數(shù)據(jù)處理涛目。秸谢。。
}
注意:使用這種技術(shù)從那些你無法直接控制的服務(wù)器上請求數(shù)據(jù)時需要小心霹肝。JavaScript沒有任何權(quán)限和訪問控制的概念估蹄,因此你使用動態(tài)腳本注入添加到頁面的任何代碼都可以完全控制整個頁面。引入外部來源的代碼時務(wù)必多加小心沫换。
Multipart XHR
MXHR是一項最新的技術(shù)臭蚁,它允許客戶端只用一個HTTP請求就可以從服務(wù)器向客戶端傳送多個資源。它通過在服務(wù)器將資源打包成雙方約定的字符串分割的長字符串并發(fā)送給客戶端讯赏。
這個技術(shù)有一些缺點垮兑,其中最大的缺點是這種方式獲取的資源不能被瀏覽器緩存。但某些情況下待逞,MXHR依然能顯著提高整體性能:
- 頁面包含了大量其他地方用不到的資源甥角,即不需要緩存,尤其是圖片识樱;
- 網(wǎng)站已經(jīng)在每個頁面中使用一個獨立打包的JavaScript或CSS文件以減少HTTP請求嗤无;因為對每個頁面來說這些文件都是唯一的,所以不需要緩存中讀取數(shù)據(jù)怜庸,觸發(fā)重載頁面当犯;
7.1.2 發(fā)送數(shù)據(jù)
有時并不關(guān)心接收數(shù)據(jù),只需要向服務(wù)器發(fā)送數(shù)據(jù)割疾。當數(shù)據(jù)只需要發(fā)送到服務(wù)器時嚎卫,有兩種技術(shù)使用廣發(fā):XHR
和信標(beacons)
。
XMLHttpRequest
雖然XHR主要用于從服務(wù)器獲取數(shù)據(jù)宏榕,但也可以將數(shù)據(jù)傳回服務(wù)器拓诸。
GET
:適用于傳輸少量數(shù)據(jù)。因為對于少量數(shù)據(jù)而言麻昼,GET請求只需要往服務(wù)器發(fā)送一個數(shù)據(jù)包奠支;而POST至少要發(fā)送兩個數(shù)據(jù)包(頭信息和正文)。
POST
:POST適合發(fā)送大數(shù)據(jù)到服務(wù)器抚芦。因為它不關(guān)心額外數(shù)據(jù)包的數(shù)量倍谜;另一個原因是IE對URL長度有限制(2048個字符)迈螟,URL太長了就不能使用GET請求。
Beacons
這個技術(shù)非常類似動態(tài)腳本注入尔崔。它使用JavaScript創(chuàng)建一個新的Image
對象答毫,并把src
屬性設(shè)外服務(wù)器上腳本的URL。該URL包含了我們要通過GET請求傳回的鍵值對數(shù)據(jù)季春。
var url = '/status_tracker.php';
var params = [
'step=2',
'time=1248027314'
];
(new Image()).src = url + '?' + params.join('&');
服務(wù)器接收到數(shù)據(jù)并保存下來洗搂,無須向客戶端返回任何信息,因此沒有圖片會顯示出來鹤盒。
7.2 數(shù)據(jù)格式
當考慮數(shù)據(jù)傳輸技術(shù)時蚕脏,我們必須考慮:功能集、兼容性侦锯、性能及方向驼鞭。而當考慮數(shù)據(jù)格式時,唯一需要比較的標準就是:速度尺碰。
7.2.1 XML
當Ajax最開始流行時挣棕,它選擇XML作為數(shù)據(jù)格式。當時它有很多優(yōu)勢:極佳的通用性亲桥、格式嚴格洛心、易于操作。那時JSON還沒有正式作為交換格式题篷,幾乎所有的服務(wù)端語音都有操作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>
可以對其進行優(yōu)化:
<?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>
性能優(yōu)化:簡化版的XML更有效率,但是比那些最快的格式依然慢上一個數(shù)量級番枚。在高性能Ajax中法严,XML沒有立足之地。
7.2.2 JSON
JSON是一種JavaScript對象和數(shù)組直接量編寫的輕量級且易于解析的數(shù)據(jù)格式葫笼。
[
{"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" ]
]
在不斷的簡化過程中深啤,可讀性越來越差,也更脆弱路星。但是文件尺寸卻小得多:大約只有標準JSON的一半溯街。解析也必須按照數(shù)據(jù)的順序進行。
function parseJSON(responseText) {
var users = [];
var usersArray = ('(' + responseText + ')');
for (var i = 0, len = usersArray.length; i < len; i++) {
users[i] = {
id: usersArray[i][0],
username: usersArray[i][1],
realname: usersArray[i][2],
email: usersArray[i][3]
};
}
return users;
}
7.2.3 HTML
一種可考慮的技術(shù)是在服務(wù)器端構(gòu)建好整個HTML再傳回客戶端洋丐,JavaScript可以通過innerHTML
屬性把它插入頁碼相應(yīng)位置呈昔。但問題是HTML是一種臃腫的數(shù)據(jù)格式,比XML更復雜友绝。因此堤尾,作為一種數(shù)據(jù)格式,它既緩慢九榔,又臃腫。
7.2.4 自定義格式
理想的數(shù)據(jù)格式應(yīng)該只包含必要的結(jié)構(gòu),以便你可以分解出每個獨立的字段哲泊。
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
這種格式非常簡潔剩蟀,“數(shù)據(jù)/結(jié)構(gòu)”比例相當高,比其他任何格式都高(除了純文本)切威。只需要簡單地調(diào)用字符串split()
方法并傳入分隔符作為參數(shù)即可育特,復雜一點的加上循環(huán)就好了。JavaScript中的循環(huán)和split()
方法都是相當快的先朦。
7.2.5 數(shù)據(jù)格式總結(jié)
通常來說缰冤,數(shù)據(jù)格式越輕量級越好,JSON
和字符分隔的自定義
是最好的喳魏。
7.3 Ajax性能指南
7.3.1 緩存數(shù)據(jù)
最快的Ajax請求就是沒有請求棉浸。
可以有兩種方法來實現(xiàn):
- 在服務(wù)端,設(shè)置HTTP頭信息以確保響應(yīng)會被瀏覽器緩存刺彩;
- 在客戶端迷郑,把獲取到的信息存儲在本地,以避免再次請求创倔。
7.3.2 了解Ajax類庫的局限
所有得JavaScript庫都允許你訪問一個Ajax對象嗡害,它屏蔽瀏覽器之間的差異,給你一個統(tǒng)一的接口畦攘。大多數(shù)情況下這非常好霸妹,因為它使你可以關(guān)注你的項目,而不是那些古怪的瀏覽器上XHR的工作細節(jié)知押。然而叹螟,為了給你一個統(tǒng)一的接口,這些庫必須簡化接口朗徊,因為不是所有瀏覽器都實現(xiàn)了每個功能首妖。這使得你不能訪問XMLHttpRequest的完整功能。
性能優(yōu)化:直接操作XHR對象減少了函數(shù)的開銷爷恳,進一步提升了性能有缆。但是,如果放棄Ajax類庫温亲,那么你可能在一些古怪的瀏覽器上遇到一些問題棚壁。
歡迎大佬糾錯指導,歡迎同行交流學習栈虚。
GitHub:https://github.com/Code4GL
簡書:http://www.reibang.com/u/7f5541a6b6d2
知乎:https://www.zhihu.com/people/code4gl/activities
公眾號:code_everything
QQ:771841496
郵箱:guanli1991@163.com