作為一個做網頁效能測試的公司,我們一直關注新的開發技術在提高網頁程式效能方面的影響。我們有不少使用者遇到僅僅是因為他們網頁的大小而影響其效能的問題。簡單說——頁面太大了不能在有限的頻寬條件下達到理想的效能。很多情況下,在不同網頁間包含的基本要素是相同的。例如,頁頭、頁尾、導航條都很少變化,在一些程式中甚至根本沒有變化。這點啟發我們,如果程式只更新頁面中需要改變的部分,將可以節省可觀的頻寬。
目標
為了驗證這個理論,我們決定看看是否能讓程式節約至少50%的頻寬。我們選擇了一個相當簡單的內部資料分析程式。程式由典型的網頁版面構成:中間是變化的內容部分。頁頭、頁尾和導航條部分都沒有任何變化。我們編輯了程式以便可以通過傳統的頁面重新整理方式和AJAX方式來訪問它。接下來我們用測試載入器(網頁效能分析器)記錄分析了兩種不同方式網頁的頻寬利用情況。
結果
實驗的第一個成果是有一點讓我們驚訝。談到AJAX體繫結構,我們本以為選擇一個合適的AJAX結構應用在我們的程式中會比較費事。在用一些流行的網頁架構做了一些簡單的實驗並考慮到javascript函數的危險性後,我們決定採用選取的一些簡單的javascrip函數來達到我們的目標。我們能夠從網上種類繁多的 Javascript/AJAX使用指南中得到我們需要的程式碼片段,通過不超過100行javascript代碼,我們把程式修改成利用AJAX的方式。不需要任何架構結構。
scenario/mode |
first-page size |
typical page size |
total bandwidth |
Page-refresh |
44k |
10k |
210k |
AJAX |
47k |
2.5k |
81k |
總頻寬節約>61%
節約的頻寬從何而來
下面是我們從測試載入器(網頁效能分析器)上抓下的,顯示了傳統和AJAX兩個不同版本的頁面傳輸資料大小。從可以看到URLs(連結)和大小,AJAX模式程式確實讓首頁變得更大。在我們的測試中,大約大了3K。這並不奇怪,因為這一頁包含了附加的javascript程式來驅動AJAX模式。如果選用AJAX架構結構,估計還會大不少。
不過最值得注意的是典型頁面的大小從平均10K左右下降到了平均2.5K左右——下降了75%
圖示1:傳統網頁重新整理模式需要的頻寬
(點擊可放大)
圖示2:AJAX模式需要的頻寬
(點擊可放大)
怎麼做到的
為了達到節約頻寬,我們對程式做了少量的修改
應用模式開關
首先我們增加了一個應用模式開關。用網頁程式描述符中用到了一個關聯參數來使程式能詢問到是使用AJAX還是網頁重新整理模式。注意並不是對所有程式都必需的。
HTML表單組件變化
下面我們編輯HTML代碼的表單(form)組件來改變表單提交機制。例如,下面是編輯前後下拉式功能表(select)組件的開始標籤的代碼:
<SELECT name="type" onChange="window.document.theform.submit()"> <SELECT name="type" onChange="submitForm()">
|
The SELECT element will now call a javascript function (see below) instead of using the browser to submit the form.
修改後下拉式功能表組件將調用javascript函數(看下面)來代替通過瀏覽器提交表單。
HTML 程式碼中添加SPAN 標籤包含住 FORM 標籤
為了標記HTML頁面中需要用伺服器返回的內容動態更新的部分,我們以將以javascript函數中標記參數來命名SPAN標籤。
Javascript函數
接下來我們寫一段代碼或者選取javjavascript函數來完成AJAX模式表單提交和頁面重新整理。
1. 建立一個包含提交內容的字串。
2. 提交內容到特定的URL,完成後調用回複回應程式法。
submitForm()
function submitForm()
{
var content = convertFormDataToPostContent(window.document.theform);
doPost('/office/UsageAnalyzer', content, 'processResult');
}
|
注意doPost()方法中的第三個參數:'processResult'。這是一個回複響應的“方法”。當非同步方法呼叫完成後,這個方法將會調用結果返回。
processResult()方法(下面會提到)的任務是用提交的內容來更新文檔。注意getElementById() 方法中的'content_area'參數同加入HTML的SPAN標籤中的ID參數是一樣的。
processResult()
function processResult(result)
{
document.getElementById('content_area').innerHTML = result;
}
|
將發送的內容提交到伺服器的工作相對來說比較簡單。建立一個瀏覽器request對象,提交內容並建立一個函數來響應處理伺服器返回的內容。這段代碼是從網上搜取的,頁可以很容易的在AJAX的文章和架構結構中找到。
doPost()
function doPost(url, content, callback_name)
{
var async_request = false;
// Mozilla/Safari
if (window.XMLHttpRequest)
{
async_request = new XMLHttpRequest();
async_request.overrideMimeType('text/xml');
}
// IE
else if (window.ActiveXObject)
{
async_request = new ActiveXObject("Microsoft.XMLHTTP");
}
async_request.open('POST', url, true);
async_request.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
async_request.onreadystatechange = function()
{
if (async_request.readyState == 4)
{
response_content = async_request.responseText;
eval(callback_name + '(response_content);');
}
}
async_request.send(content);
}
|
表單項轉換方法將表單中的各項內容串聯編碼成一定的格式提交。同樣的,這些代碼可以從相關的資源和互連網上得到。
convertFormDataToPostContent()
function convertFormDataToPostContent(form_name)
{
var content_to_submit = '';
var form_element;
var last_element_name = '';
for (i = 0; i < form_name.elements.length; i++)
{
form_element = form_name.elements[i];
switch (form_element.type)
{
// Text fields, hidden form elements
case 'text':
case 'hidden':
case 'password':
case 'textarea':
case 'select-one':
content_to_submit += form_element.name + '='
+ escape(form_element.value) + '&'
break;
// Radio buttons
case 'radio':
if (form_element.checked)
{
content_to_submit += form_element.name + '='
+ escape(form_element.value) + '&'
}
break;
// Checkboxes
case 'checkbox':
if (form_element.checked)
{
// Continuing multiple, same-name checkboxes
if (form_element.name == last_element_name)
{
// Strip of end ampersand if there is one
if (content_to_submit.lastIndexOf('&') ==
content_to_submit.length - 1)
{
content_to_submit = content_to_submit.substr(
0, content_to_submit.length - 1);
}
// Append value as comma-delimited string
content_to_submit += ',' + escape(form_element.value);
}
else
{
content_to_submit += form_element.name + '='
+ escape(form_element.value);
}
content_to_submit += '&';
last_element_name = form_element.name;
}
break;
}
}
// Remove trailing separator
content_to_submit = content_to_submit.substr(0, content_to_submit.length - 1);
return content_to_submit;
} |
結論
在每頁中都存在特定部分重複的程式中,用AJAX類方法只更新網頁中相關的部分能夠很好的節省頻寬。通過不到100行的javascript代碼我們將網頁程式轉換成採用AJAX的更新方法,大大的降低了(>60%)執行個體程式所需要的頻寬利用率。
未來的方向
測試更多的用我們在這裡提到的AJAX方法來實現的現實應用程式會很有意思。如果你有這樣的程式,請和我們聯絡。
對伺服器CPU資源的影響將會是有趣的研究。不過,我們的頁面中沒有需要資料庫查詢或其他進程處理工作,所以這個參考程式不一定是做此類測試的最好選擇。
編輯提示:英文原版請看