Ajax 原理
作为一个技术人员,我看到一项新技术,总是喜欢琢磨琢磨它内部是如何实现的。在对 Ajax 有了初步认识以后,自然想看看其内部机制,但是令我失望的是,至少介绍 Ajax 内部实现的文章少之又少,好容易找到一篇,却也只是简单列了列一些 javascript 代码,并且没什么解释,颇为郁闷。想想求人不如求己,况且自己研究的或许印象更深一些。于是找到了一个 AjaxPro,下来琢磨琢磨,只是对于 JavaScript 我实在知之甚少,不明白之处依然很多,不过还是想写出来,抛砖引玉,望高人们不吝指教。
一、使用的例子 本文使用的例子很简单,一个文本框,在其中敲入文字之后,下方就显示该文字并加上一个“(Hello from server)”。源码如下(有删节):
1
<%
@Pagelanguage="c#ClassNameKeyPressDemoInheritsSystem.Web.UI.Page
%>
2 < script runat ="server" language ="c#" > 3 4privatevoidPage_Load(objectsender,EventArgse) 5{ 6AjaxPro.Utility.RegisterTypeForAjax(typeof(KeyPressDemo)); 7} 8 9[AjaxPro.AjaxMethod] 10publicstringEchoInput(strings) 11{ 12returns+=(Hellofromserver); 13 14 15 </ script > 16 17 < form id ="Form1" method ="post" runat ="server" ></ form > 18 19 < div class ="content" > 20 < h1 > KeyPressDemoExamples </ h1 > 21 < p > PressanykeyinthetextboxandseetheechointheDIVelementontherightside. </ p > 22 < input type ="text" id ="myinput" onkeyup ="doTest1();" /> < div id ="mydisplay" > ----empty---- </ div > 23 < p >< i > Note,thatIdonotupdatethedisplayifarequestisrunningcurrently. </ i ></ p > 24 </ div > 25 26 < script type ="text/javascript" defer ="defer" > 27 28vartimernull; 29 30functiondoTest1(){ 31if(timer!=){ 32clearTimeout(timer); 33 34setTimeout(doTest1_next,100); 35 36 37doTest1_next(){ 38eledocument.getElementById(myinput); 39ASP.KeyPressDemo.EchoInput(ele.value,doTest1_callback); 40 41 42doTest1_callback(res){ 43mydisplay); 44ele.innerHTMLres.value; 45 46 47 </ script > 选用这个例子,是因为它比较简单,没有相关的 C# 文件。首先看看第17行,咦?怎么这个 form 里啥都没有?既然什么都没有?删掉它行不行?不行,绝对不行!看看网页打开后,这一行被扩展成了什么?
1
<
form
name
="Form1"
method
="post"
action
="keypress.aspx"
id
="Form1"
>
2 < div > 3 < script type ="text/javascript" src ="/ajaxdemo/ajaxpro/core.ashx" ></ script > 4 < script type ="text/javascript" src ="/ajaxdemo/ajaxpro/ASP.KeyPressDemo,App_Web_vxhzzzxr.ashx" ></ script > 5 </ form > 6 请注意这里链入的两个 javascript 文件,它们是 Ajax 得以运行的基础!删掉 form 那一行,它们就不会出现,自然不行了。这两行是如何产生的?那就是页面代码的第4至7行的 PageLoad 函数的功劳了。 好,那这两个 javascript 文件我们能看到不?看上去它们是服务端的,并且事实上是服务端动态生成的。不过稍有些了解浏览器工作原理的人就会知道,到 Local Settings 下的 Temporary Internet Files 目录下去找,肯定是有的,因为浏览器下载页面的时候会把与页面相关的文件都下过来。 二、Ajax ClientScript 的执行总体流程 好,有了源页面代码,又有了两个 ClientScript 文件,我们就可以分析客户端的执行流程了。以下是我画的一张简单的流程图: 我们一个一个地来分析。 三、HTML页面做了什么? 第一步,当我们在 TextBox 里输入字符后,将会触发 onkeyup 事件。它要执行 doTest1 方法。见页面代码里的第22行。 第二步,doTest1 方法使用 setTimeout 函数,设定了 100 毫秒后,执行 doTest1_next 方法。见页面代码里的第34行。 第三步,doTest1_next 方法调用了 ASP.KeyPressDemo.EchoInput 方法,它带有两个参数,第一个是我们在文本框中输入的值,当然是个字符串类型的了;第二个则是一个 callback 函数,请留心这个函数,它将于整个流程的最后执行。 好,我们知道页面的客户端无外乎就是 HTML 和 JavaScript,虽然 ASP.KeyPressDemo.EchoInput 方法酷似页面里我们自己用 C# 写的函数,但可以肯定的是它绝对是用 JavaScript 实现的。在哪儿呢?嗯,在我们从 Temporary Internet Files 目录下找到的 ASP.KeyPressDemo,App_Web_vxhzzzxr.ashx 里。 四、ASP.KeyPressDemo,App_Web_vxhzzzxr.ashx 的实现 这个文件很小,以下是它的全部源码:
1
addNamespace(
"
ASP
"
);
2 ASP.KeyPressDemo_class = Class.create(); 3 ASP.KeyPressDemo_class.prototype = ( new AjaxPro.Request()).extend( { 4EchoInput:function(s,callback){ 5returnthis.invoke("EchoInput",{"s":s},callback); 6}, 7initialize:function(){ 8this.url="/ajaxdemo/ajaxpro/ASP.KeyPressDemo,App_Web_vxhzzzxr.ashx"; 9} 10} ) 11 ASP.KeyPressDemo = new ASP.KeyPressDemo_class(); 12 啊哈,这下我们知道了,ASP.KeyPressDemo 其实是在这里用 JavaScript 定义的 ASP.KeyPressDemo_class 类的实例,EchoInput 则是它的一个方法。注意一下每3行,我们看到这个类是从 AjaxPro.Request 类继承的。什么什么?继承?有没有搞错?JavaScript 什么时候开始面向对象了而不是基于对象了?先摆下这个疑问,我们继续往下看。 EchoInput 方法的实现很简单,就是调用了一个 Invoke 方法。嗯,这个方法想必是从 AjaxPro.Request 类“继承”下来的。那它定义在哪儿?是了,还有一个 core.ashx 呢,它才是真正客户端实现 Ajax 技术的主角!这个文件太大,我们还是依照函数调用顺序慢慢来拆解罢。 五、Invoke 函数 Invoke 函数是核心所在,前面我画的流程图中已经简单地描述了它的主要流程。不过这个函数太重要了,这里还是列出它的全部源码:
1
AjaxPro.Request
=
Class.create();
2 AjaxPro.Request.prototype = ( new AjaxPro.Base()).extend( { 3invoke:function(method,data,callback){ 4varasync=typeofcallback=="function"&&callback!=AjaxPro.noOperation; 5varjson=AjaxPro.toJSON(data)+"/r/n"; 6 7if(AjaxPro.cryptProvider!=null) 8json=AjaxPro.cryptProvider.encrypt(json); 9 10this.callback=callback; 11 12if(async){ 13this.xmlHttp.onreadystatechange=this.doStateChange.bind(this); 14if(typeofthis.onLoading=="function")this.onLoading(true); 15} 16 17this.xmlHttp.open("POST",this.url,async); 18this.xmlHttp.setRequestHeader("Content-Type","application/x-www-form-urlencoded"); 19this.xmlHttp.setRequestHeader("Content-Length",json.length); 20this.xmlHttp.setRequestHeader("Ajax-method",method); 21 22if(AjaxPro.token!=null&&AjaxPro.token.length>0) 23this.xmlHttp.setRequestHeader("Ajax-token",AjaxPro.token); 24 25if(MS.Browser.isIE) 26this.xmlHttp.setRequestHeader("Accept-Encoding","gzip,deflate"); 27else 28this.xmlHttp.setRequestHeader("Connection","close");//MozillaBug#246651 29 30if(this.onTimeout!=null&&typeofthis.onTimeout=="function") 31this.timeoutTimer=setTimeout(this.timeout.bind(this),this.timeoutPeriod); 32 33this.xmlHttp.send(json); 34 35json=null; 36data=null; 37deletejson; 38deletedata; 39 40if(!async){ 41returnthis.createResponse(); 42} 43 44returntrue; 45} 46} ); 47
六、解释“继承”
1
Object.extend
=
function
(destination,source)
{
2for(propertyinsource){ 3destination[property]=source[property]; 4} 5returndestination; 6} 7 哈哈,所谓的“继承”,其实只是个属性拷贝而已。 前面我们看到了 this.xmlHttp 大显神威。那么它是哪儿来的?看看 AjaxPro.Request 类的 initialize 函数吧(有删节):
1
initialize:
function
(url)
{
2this.xmlHttp=newXMLHttpRequest(); 3} 4 是了,xmlHttp 只是 XMLHttpRequest 的一个实例。那么 XMLHttpRequest 的定义呢?
1
var
lastclsid
=
null
;
2 if ( ! window.XMLHttpRequest) { 3 4functiongetXmlHttp(clsid){ 5varxmlHttp=null; 6try{ 7xmlHttp=newActiveXObject(clsid); 8lastclsid=clsid; 9returnxmlHttp; 10}catch(ex){} 11} 12 13window.XMLHttpRequest=function(){ 14if(lastclsid!=null){ 15returngetXmlHttp(lastclsid); 16} 17 18varxmlHttp=null; 19varclsids=["Msxml2.XMLHTTP.6.0","Msxml2.XMLHTTP.5.0","Msxml2.XMLHTTP.4.0","Msxml2.XMLHTTP.3.0","Msxml2.XMLHTTP.2.6","Microsoft.XMLHTTP.1.0","Microsoft.XMLHTTP.1","Microsoft.XMLHTTP"]; 20 21for(vari=0;i<clsids.length&&xmlHttp==null;i++){ 22xmlHttp=getXmlHttp(clsids[i]); 23} 24 25if(xmlHttp==null){ 26returnnewIFrameXmlHttp(); 27} 28 29returnxmlHttp; 30} 31} 32 哦,原来是在这里真正创建的。说到底还是一个 ActiveXObject 啊。关于这个本文也不再多提。不过代码中还需要注意的一点是,
1
doStateChange:
function
()
{
2if(this.onStateChanged!=null&&typeofthis.onStateChanged=="function") 3try{this.onStateChanged(this.xmlHttp.readyState);}catch(e){} 4 5if(this.xmlHttp.readyState!=4) 6return; 7 8if(this.xmlHttp.status==200){ 9if(this.timeoutTimer!=null)clearTimeout(this.timeoutTimer); 10if(typeofthis.onLoading=="function")this.onLoading(false); 11 12this.xmlHttp.onreadystatechange=AjaxPro.noOperation; 13 14this.callback(this.createResponse()); 15this.callback=null; 16 17this.xmlHttp.abort(); 18} 19} , 20 如果 status 是 200,也就是 OK,那么清除掉超时处理函数,处理 onLoading 事件,最后使用 callback 调用 createResponse 函数。还记得如果不是异步的话,createResponse 将会直接调用而不是通过 doStateChange 吧。
1
createResponse:
function
()
{
2varr=newObject(); 3r.error=null; 4r.value=null; 5 6varresponseText=newString(this.xmlHttp.responseText); 7 8if(AjaxPro.cryptProvider!=null&&typeofAjaxPro.cryptProvider=="function") 9responseText=AjaxPro.cryptProvider.decrypt(responseText); 10 11eval("r.value="+responseText+";"); 12 13if(r.error!=null&&this.onError!=null&&typeofthis.onError=="function") 14try{this.onError(r.error);}catch(e){} 15 16responseText=null; 17 18returnr; 19} 如果前面的 json 也就是 Request 是加过密的,这里就需要对 responseText 进行解密。完了之后得到 r.value,r 将会被返回并提供给 callback 函数。本例中将最终传回 doTest1_callback,r 被传入它的 res 参数。最后更新文本框下的字符串,整个 Ajax ClientScript 的流程就差不多是完成了。 下载请点击:ASP.NET.AJAX深入浅出系列(赵劼).zip (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |