如今web應(yīng)用上,ajax技術(shù)是大行其道。
ajax框架層出不窮,prototype,dojo,jquery,mootools,dwr,buffalo,ext,yui,spry。。。
ajax框架的出現(xiàn),在提升開(kāi)發(fā)生產(chǎn)效率的同時(shí),也讓不少同學(xué)不明其內(nèi)在原理,僅僅成為了某些框架的使用者。
(對(duì)于產(chǎn)品生產(chǎn)是好事,對(duì)于技術(shù)追求是壞事)
本文不涉及任何ajax框架的使用,本文僅通過(guò)一個(gè)模擬需求,在不使用任何ajax框架的前提下,以demo演示的方式,
向大家介紹ajax的原理以及應(yīng)用場(chǎng)景。
ajax全稱是:
Asynchronous JavaScript And XML。
其本意是,通過(guò)javascript技術(shù)(JavaScript),通過(guò)異步http請(qǐng)求方式(Asynchronous),得到XML文本內(nèi)容(XML)之后,通過(guò)javascript技術(shù)局部刷新web頁(yè)面內(nèi)容。
從廣義的概念看,只要符合“異步請(qǐng)求,局部刷新web頁(yè)面”的技術(shù),都可以成為ajax。
未必一定要使用javascript,一般情況下,大多數(shù)client端腳本代碼都可以;返回內(nèi)容也未必一定要是xml,目前json格式,更為流行。
如何異步請(qǐng)求內(nèi)容呢?
以javascript代碼作演示,如下:
function xmlhttpPost(url,func) {
var xmlHttpReq = false;
var self = this;
// Mozilla/Safari
if (window.XMLHttpRequest) {
self.xmlHttpReq = new XMLHttpRequest();
}
// IE
else if (window.ActiveXObject) {
self.xmlHttpReq = new ActiveXObject("Microsoft.XMLHTTP");
}
self.xmlHttpReq.open('POST', url, true);
self.xmlHttpReq.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
self.xmlHttpReq.onreadystatechange = function() {
if (self.xmlHttpReq.readyState == 4) {
func(self.xmlHttpReq.responseText);
}
}
self.xmlHttpReq.send(null);
}
參數(shù)一,url:表明異步請(qǐng)求的資源地址
參數(shù)二,func:表明請(qǐng)求結(jié)束后,采用什么函數(shù)對(duì)請(qǐng)求結(jié)果內(nèi)容進(jìn)行回調(diào)處理
其實(shí),這一個(gè)js代碼,就詮釋了ajax的全部含義--異步請(qǐng)求資源,將得到的資源內(nèi)容,使用指定的function進(jìn)行處理。
所以,ajax很簡(jiǎn)單,大家千萬(wàn)別被如今層出不窮的ajax框架給嚇怕了。要了解ajax的原理,就只要參看這段代碼即可。
如今的一些框架,僅僅在此基礎(chǔ)上,是封裝了一些公用的函數(shù),方便開(kāi)發(fā)人員調(diào)用。(當(dāng)然,說(shuō)說(shuō)簡(jiǎn)單,其實(shí)所謂的這些函數(shù),大大方便了開(kāi)發(fā)人員使用ajax技術(shù)。具體請(qǐng)參看ajax framework的官方介紹。)
特別說(shuō)明:這個(gè)xmlhttpost方法改進(jìn)了
simple-ajax。在原基礎(chǔ)上,將回調(diào)方法作為參數(shù)傳遞。
解釋了原理性的內(nèi)容之后,接下來(lái),以一個(gè)模擬的應(yīng)用場(chǎng)景,demo說(shuō)明ajax的使用,以及它的主要應(yīng)用場(chǎng)景。
模擬場(chǎng)景:
目錄選擇,即當(dāng)選擇一個(gè)目錄的時(shí)候,需要顯示這個(gè)目錄下的所有子目錄。
首先,我們來(lái)虛擬一個(gè)目錄結(jié)構(gòu),如下:
那么,要實(shí)現(xiàn)目錄選擇,有三個(gè)方式:
1)頁(yè)面初始化的時(shí)候,服務(wù)端將所有的目錄信息都put到頁(yè)面中。
優(yōu)點(diǎn):選擇操作簡(jiǎn)單,有了全部的目錄信息,做選擇操作,都可以使用js完成,無(wú)需和服務(wù)端進(jìn)行交互
缺點(diǎn):當(dāng)目錄信息很大的時(shí)候,比如有上萬(wàn)個(gè)節(jié)點(diǎn),整個(gè)目錄信息有1m左右大小,那么要渲染這個(gè)頁(yè)面,估計(jì)得20秒左右(視網(wǎng)速)
并且,很可能用戶僅僅只要選擇有限的幾個(gè)節(jié)點(diǎn)就可以,比如上萬(wàn)個(gè)節(jié)點(diǎn)中選擇6-7個(gè)節(jié)點(diǎn),那么浪費(fèi)太大了;
2)頁(yè)面初始化的時(shí)候,服務(wù)端將當(dāng)前需要的節(jié)點(diǎn)信息put到頁(yè)面上,一旦有選擇操作,重新刷新頁(yè)面。
優(yōu)點(diǎn):選擇操作簡(jiǎn)單,對(duì)于節(jié)點(diǎn)信息,每次取需要的內(nèi)容,不存在浪費(fèi)現(xiàn)象
缺點(diǎn):每次都要刷新整個(gè)頁(yè)面,除節(jié)點(diǎn)信息外,其他不變的東西都需要重新從服務(wù)端取,增加無(wú)謂的消耗。
3)頁(yè)面初始化的時(shí)候,服務(wù)端將當(dāng)前需要的節(jié)點(diǎn)信息put到頁(yè)面上,一旦有選擇操作,只刷新節(jié)點(diǎn)相關(guān)的內(nèi)容;
優(yōu)點(diǎn):每次只load需要的信息,局部刷新頁(yè)面內(nèi)容,不存在任何浪費(fèi)現(xiàn)象
缺點(diǎn):需要異步請(qǐng)求數(shù)據(jù),每次請(qǐng)求都需要和服務(wù)器交互,選擇操作稍顯復(fù)雜(異步請(qǐng)求,局部刷新)
通過(guò)這三種方式做對(duì)比,發(fā)現(xiàn)ajax主要適用的場(chǎng)景如下:
1)整體內(nèi)容量大(幾百k,幾m,甚至幾十m),而頁(yè)面只需要其中一小部分信息即可;
2)數(shù)據(jù)顯示,只涉及一個(gè)頁(yè)面中部分?jǐn)?shù)據(jù)信息的變動(dòng);
特別說(shuō)明:至于使用ajax性能如何,需要對(duì)1,3兩個(gè)情況做性能測(cè)試,權(quán)衡使用。
針對(duì)第三種方案,
首先需要一個(gè)取節(jié)點(diǎn)資源的url,
演示代碼中,為了演示方便,使用php語(yǔ)言,而非使用主要語(yǔ)言java;
tree_node.php
<?php
$id = $_GET['id'];
if("1" == $id) {
echo("{\"id\":1,\"parentId\":-1,\"name\":\"1-1\",\"children\":[{\"id\":2,\"name\":\"2-1\"},{\"id\":3,\"name\":\"2-2\"},{\"id\":4,\"name\":\"2-3\"}]}");
} else if("2" == $id) {
echo("{\"id\":2,\"parentId\":1,\"name\":\"2-1\",\"children\":[]}");
} else if("3" == $id) {
echo("{\"id\":3,\"parentId\":1,\"name\":\"2-2\",\"children\":[]}");
} else if("4" == $id) {
echo("{\"id\":4,\"parentId\":1,\"name\":\"2-3\",\"children\":[{\"id\":5,\"name\":\"3-1\"},{\"id\":6,\"name\":\"3-2\"}]}");
} else if("5" == $id) {
echo("{\"id\":5,\"parentId\":4,\"name\":\"3-1\",\"children\":[]}");
} else if("6" == $id) {
echo("{\"id\":6,\"parentId\":4,\"name\":\"3-2\",\"children\":[{\"id\":7,\"name\":\"4-1\"}]}");
} else if("7" == $id) {
echo("{\"id\":7,\"parentId\":6,\"name\":\"4-1\",\"children\":[]}");
} else {
echo("");
}
?>
該文件中,寫(xiě)死了目錄結(jié)構(gòu)(一般情況下,往往根據(jù)樹(shù)對(duì)象,動(dòng)態(tài)取得需要的節(jié)點(diǎn))。
通過(guò)js,動(dòng)態(tài)請(qǐng)求節(jié)點(diǎn)信息,部分刷新頁(yè)面內(nèi)容:
<script type="text/javascript">
//模擬需求js
var nodeSelect = function(text) {
var tree = toJsonObje(text);
var options = document.getElementById("tree").options;
options.length = 0;
options.add(new Option("請(qǐng)選擇","-1"));
if(tree == null) {
return;
} else {
var children = tree.children;
for(i = 0; i < children.length; i++) {
var child = children[i];
options.add(new Option(child.name,child.id));
}
if(tree.parentId != "-1") {
options.add(new Option("上一級(jí)",tree.parentId));
}
}
document.getElementById("l").innerHTML = "當(dāng)前位置:" + tree.name;
}
function nodeSelectAjax(id) {
var TREE_NODE_URL = "tree_node.php";
var url = TREE_NODE_URL + "?id=" + id;
xmlhttpPost(url,nodeSelect);
}
</script>
nodeSelectAjax,異步請(qǐng)求節(jié)點(diǎn)資源
nodeSelect,回調(diào)函數(shù),根據(jù)請(qǐng)求信息,局部刷新頁(yè)面
至于請(qǐng)求資源信息格式,任何方式都可以,只要client端能解析就行。
目前json格式,比較流行。
最后,附上java使用json庫(kù),生成json格式的方法:
JSONObject node = new JSONObject();
node.put("id", 1);
node.put("parentId", -1);
node.put("name", "1-1");
JSONArray children = new JSONArray();
JSONObject c1 = new JSONObject();
c1.put("id", 2);
c1.put("name", "2-1");
JSONObject c2 = new JSONObject();
c2.put("id", 3);
c2.put("name", "2-2");
children.put(c1);
children.put(c2);
node.put("children", children);
System.out.println(node.toString());
ajax demo
工程文件編碼:utf-8
工程運(yùn)行:http server with php supported
ubuntu firefox下測(cè)試通過(guò)
其他:
不知道是不是ie的bug,居然不支持 select.innerHTML = value的方式
只能通過(guò)select.options.add(new Option("content","value") 動(dòng)態(tài)往select中添加選項(xiàng)。