ajax原理简介以及简单demo演示
如今web应用上,ajax技术是大行其道。ajax框架层出不穷,prototype,dojo,jquery,mootools,dwr,buffalo,ext,yui,spry。。。
ajax框架的出现,在提升开发生产效率的同时,也让不少同学不明其内在原理,仅仅成为了某些框架的使用者。
(对于产品生产是好事,对于技术追求是坏事)
本文不涉及任何ajax框架的使用,本文仅通过一个模拟需求,在不使用任何ajax框架的前提下,以demo演示的方式,
向大家介绍ajax的原理以及应用场景。
ajax全称是: Asynchronous JavaScript And XML。
其本意是,通过javascript技术(JavaScript),通过异步http请求方式(Asynchronous),得到XML文本内容(XML)之后,通过javascript技术局部刷新web页面内容。
从广义的概念看,只要符合“异步请求,局部刷新web页面”的技术,都可以成为ajax。
未必一定要使用javascript,一般情况下,大多数client端脚本代码都可以;返回内容也未必一定要是xml,目前json格式,更为流行。
如何异步请求内容呢?
以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 );
}
参数一,url:表明异步请求的资源地址
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 );
}
参数二,func:表明请求结束后,采用什么函数对请求结果内容进行回调处理
其实,这一个js代码,就诠释了ajax的全部含义--异步请求资源,将得到的资源内容,使用指定的function进行处理。
所以,ajax很简单,大家千万别被如今层出不穷的ajax框架给吓怕了。要了解ajax的原理,就只要参看这段代码即可。
如今的一些框架,仅仅在此基础上,是封装了一些公用的函数,方便开发人员调用。(当然,说说简单,其实所谓的这些函数,大大方便了开发人员使用ajax技术。具体请参看ajax framework的官方介绍。)
特别说明:这个xmlhttpost方法改进了 simple-ajax。在原基础上,将回调方法作为参数传递。
解释了原理性的内容之后,接下来,以一个模拟的应用场景,demo说明ajax的使用,以及它的主要应用场景。
模拟场景:
目录选择,即当选择一个目录的时候,需要显示这个目录下的所有子目录。
首先,我们来虚拟一个目录结构,如下:
那么,要实现目录选择,有三个方式:
1)页面初始化的时候,服务端将所有的目录信息都put到页面中。
优点:选择操作简单,有了全部的目录信息,做选择操作,都可以使用js完成,无需和服务端进行交互
缺点:当目录信息很大的时候,比如有上万个节点,整个目录信息有1m左右大小,那么要渲染这个页面,估计得20秒左右(视网速)
并且,很可能用户仅仅只要选择有限的几个节点就可以,比如上万个节点中选择6-7个节点,那么浪费太大了;
2)页面初始化的时候,服务端将当前需要的节点信息put到页面上,一旦有选择操作,重新刷新页面。
优点:选择操作简单,对于节点信息,每次取需要的内容,不存在浪费现象
缺点:每次都要刷新整个页面,除节点信息外,其他不变的东西都需要重新从服务端取,增加无谓的消耗。
3)页面初始化的时候,服务端将当前需要的节点信息put到页面上,一旦有选择操作,只刷新节点相关的内容;
优点:每次只load需要的信息,局部刷新页面内容,不存在任何浪费现象
缺点:需要异步请求数据,每次请求都需要和服务器交互,选择操作稍显复杂(异步请求,局部刷新)
通过这三种方式做对比,发现ajax主要适用的场景如下:
1)整体内容量大(几百k,几m,甚至几十m),而页面只需要其中一小部分信息即可;
2)数据显示,只涉及一个页面中部分数据信息的变动;
特别说明:至于使用ajax性能如何,需要对1,3两个情况做性能测试,权衡使用。
针对第三种方案,
首先需要一个取节点资源的url,
演示代码中,为了演示方便,使用php语言,而非使用主要语言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 ( "" );
}
?>
该文件中,写死了目录结构(一般情况下,往往根据树对象,动态取得需要的节点)。
$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 ( "" );
}
?>
通过js,动态请求节点信息,部分刷新页面内容:
<
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( " 请选择 " , " -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( " 上一级 " ,tree.parentId));
}
}
document.getElementById( " l " ).innerHTML = " 当前位置: " + tree.name;
}
function nodeSelectAjax(id) {
var TREE_NODE_URL = " tree_node.php " ;
var url = TREE_NODE_URL + " ?id= " + id;
xmlhttpPost(url,nodeSelect);
}
</ script >
nodeSelectAjax,异步请求节点资源
// 模拟需求js
var nodeSelect = function (text) {
var tree = toJsonObje(text);
var options = document.getElementById( " tree " ).options;
options.length = 0 ;
options.add( new Option( " 请选择 " , " -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( " 上一级 " ,tree.parentId));
}
}
document.getElementById( " l " ).innerHTML = " 当前位置: " + tree.name;
}
function nodeSelectAjax(id) {
var TREE_NODE_URL = " tree_node.php " ;
var url = TREE_NODE_URL + " ?id= " + id;
xmlhttpPost(url,nodeSelect);
}
</ script >
nodeSelect,回调函数,根据请求信息,局部刷新页面
至于请求资源信息格式,任何方式都可以,只要client端能解析就行。
目前json格式,比较流行。
最后,附上java使用json库,生成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());
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
工程运行:http server with php supported
ubuntu firefox下测试通过
其他:
不知道是不是ie的bug,居然不支持 select.innerHTML = value的方式
只能通过select.options.add(new Option("content","value") 动态往select中添加选项。