快速进入AJAX开发

互联网 | 编辑: uker编辑2 2007-03-11 00:00:00转载
   在这篇文章里,你将建立一个简单的AJAX(Asynchronous JavaScript and XML,异步JavaScript和XML)。这个练习适合于大多数没有耐心从ASAP开始编码的读者,但是假定你已经熟悉JavaScript,PHP和XML。

    你将创建一个名为quickstart简单的AJAX Web应用。在这个应用中,用户要填写他/她的名字,同时在用户填写的过程中服务器及时返回响应。图1显示了用户装载的初始界面,index.html(注意当请求quickstart Web文件夹时,index.html被默认装载,即使文件名没有被显示得提及)。


图1


     当用户在输入的时候,以一定的间隔异步联系服务器以检查它此时能否识别。服务器自动地被呼叫,大概一秒钟一次。这就说明了我们不需要在输入完后点击按钮(如Send按钮)来进行验证(这种方法或许不适合实际的登入机制,但是确实能很好地展示AJAX的一些功能)。
根据不同的输入名字,服务器返回的信息会不同,参见图2中的例子。


图2


可以在本文后面的资源中得到这个例子。

    大概看第一眼的时候觉得没有什么特别之处。我们刻意把第一个例子写的简单,使得它易于理解。这个应用的特别之处就是自动显示来自服务器的消息而不需要打断用户的动作(消息在用户输入名字的同时被显示)。显示这些数据不需要页面重新装载,即使一个服务器连接被建立以获得这些数据。使用非AJAX Web开发技术完成这并不是一个简单的任务。

这个应用包含下面三个文件:
1.        index.html是用户请求的初始HTML文件
2.        quickstart.js是包含了JavaScript代码的文件,随着index.html在客户端的装载而装载。当需要服务器端的功能时,这个文件就处理向服务器发送异步请求
3.        quickstart.php是驻留在服务器上的PHP脚本,处理来自客户端quickstart.js文件里JavaScript代码的请求。
图3 显示了运行该应用时发生的动作:


图3

    1到5步是典型的HTTP请求。在做出这个请求后,用户需要等待直到页面被装载。通过使用XMLHttpRequest对象服务器在后台被访问。在这段时间内,用户可以继续正常使用这个页面,就像它是一个桌面程序。从服务器重新获得数据和用这些数据更新页面不需要刷新和重载。

    现在是时候在你自己的机子上实现这个代码了。在开始之前,确定你已经像附录A中显示的那样准备好工作环境。附录A中指导你如何安装、设置PHP和Apache,以及如何安装本书中例子用到的数据库(在这个quickstart例子中你不需要数据库)。[编注:附录A不包含在本篇文章中。]

 开始行动——Quickstart AJAX

1. 在附录A中,指导你建立一个Web服务器并创建一个名为ajax的Web文件夹。在这个文件夹里存放了这本书的代码。
1.        在ajax目录下创建一个名为quickstart的子目录。
2.        在quickstart目录下,创建一个名为index.html的文件,在文件中添加如下代码:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
   <head>
      <title>AJAX with PHP: Quickstart</title>
      <script type="text/javascript" src="quickstart.js"></script>
   </head>
   <body onload='process()'>
      Server wants to know your name:
      <input type="text" id="myName" />
      <div id="divMessage" />
   </body>
</html>



3.        创建一个名为quickstart.js的文件,并添加如下代码:

// stores the reference to the XMLHttpRequest object
var xmlHttp = createXmlHttpRequestObject();


// retrieves the XMLHttpRequest object
function createXmlHttpRequestObject()
{
   // will store the reference to the XMLHttpRequest object
   var xmlHttp;
   // if running Internet Explorer
   if(window.ActiveXObject)
   {
      try
      {
         xmlHttp = new ActiveXObject("Microsoft.XMLHTTP");
      }
      catch (e)
      {
         xmlHttp = false;
      }
   }
   // if running Mozilla or other browsers
   else
   {
      try
      {
         xmlHttp = new XMLHttpRequest();
      }
      catch (e)
      {
         xmlHttp = false;
      }
   }
   // return the created object or display an error message
   if (!xmlHttp)
      alert("Error creating the XMLHttpRequest object.");
   else
      return xmlHttp;
}


// make asynchronous HTTP request using the XMLHttpRequest object
function process()
{
   // proceed only if the xmlHttp object isn't busy
   if (xmlHttp.readyState == 4 || xmlHttp.readyState == 0)
   {
      // retrieve the name typed by the user on the form
      name = encodeURIComponent(document.getElementById("myName").value);
      // execute the quickstart.php page from the server
      xmlHttp.open("GET", "quickstart.php?name=" + name, true);
      // define the method to handle server responses
      xmlHttp.onreadystatechange = handleServerResponse;
      // make the server request
      xmlHttp.send(null);
   }
   else
      // if the connection is busy, try again after one second
      setTimeout('process()', 1000);
}


// executed automatically when a message is received from the server
function handleServerResponse()
{
   // move forward only if the transaction has completed
   if (xmlHttp.readyState == 4)
   {
      // status of 200 indicates the transaction completed successfully
      if (xmlHttp.status == 200)
      {
         // extract the XML retrieved from the server
         xmlResponse = xmlHttp.responseXML;
         // obtain the document element (the root element) of the XML structure
         xmlDocumentElement = xmlResponse.documentElement;
         // get the text message, which is in the first child of
         // the the document element
         helloMessage = xmlDocumentElement.firstChild.data;
         // update the client display using the data received from the server
 document.getElementById("divMessage").innerHTML ='<i>' + helloMessage + '</i>';
         // restart sequence
         setTimeout('process()', 1000);
      }
      // a HTTP status different than 200 signals an error
      else
      {
         alert("There was a problem accessing the server: " + xmlHttp.statusText);
      }
   }
}



4.        创建一个名为qucikstart.php的文件,并添加如下代码:

<?php
// we'll generate XML output
header('Content-Type: text/xml');
// generate XML header
echo '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>';
// create the <response> element
echo '<response>';
// retrieve the user name
$name = $_GET['name'];
// generate output depending on the user name received from client
$userNames = array('CRISTIAN', 'BOGDAN', 'FILIP', 'MIHAI', 'YODA');
if (in_array(strtoupper($name), $userNames))
   echo 'Hello, master ' . htmlentities($name) . '!';
else if (trim($name) == '')
   echo 'Stranger, please tell me your name!';
else
   echo htmlentities($name) . ', I don\'t know you!';
// close the <response> element
echo '</response>';
?>



5.        现在你就可以使用你喜欢的浏览器,通过装载http://localhost/ajax/quickstart来访问新写的程序。装载这个页面,你将得到像图1和图2那样的页面。如果你在运行这个程序的过程中遇到任何问题,检查你是否按照附录A中描述的那样安装和配置。大多数错误的发生都是诸如输入之类的小错误。

刚才发生了什么?
    下面就是比较有趣的部分。你将理解在那些代码里发生了什么。让我们从用户首先交互的文件,index.html,开始。这个文件引用了一个神奇的名为quickstart.js的JavaScript文件,并且建立了一个非常简单的客户端交换。下面的代码片断是取自index.html,注意下用粗体标出的元素。

<body onload='process()'>
  Server wants to know your name:
   <input type="text" id="myName" />
   <div id="divMessage" />
</body>



    当页面装载的时候,quickstart.js中一个名为process()的函数就会被执行。这以某种方式引起<div>元素被服务器上的消息填充。在看process()函数内部发生了什么之前让我们看看在服务器内发生了什么。在服务器上,你有一个名为quickstart.php的脚本。它用来创建发往客户端的XML消息。这个XML消息包含一个<response>元素。这个元素封装了服务器发往客户端的消息:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<response>
   ... message the server wants to transmit to the client ...
</response>



    如果从客户端获取的信息是空的,那么返回的信息就是“Stranger,please tell me your name!”。如果名字是Cristian,Bogdan,Filip,Mihai或者Yoda,服务器返回“Hello,master<user name>!”。如果是其他的,那么消息就是“<user name>,I don't know you!”。所以如果Mickey Mouse输入它的名字,那么服务器返回的如下的XML结构:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<response>
   Mickey Mouse, I don't know you!
</response>



quickstart.php脚本的开头产生XML文档的头和<response>元素:

<?php
// we'll generate XML output
header('Content-Type: text/xml');
// generate XML header
echo '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>';
// create the <response> element
echo '<response>';



     加粗的部分把输出标记为XML文档,这很重要。因为客户端期待接受XML(如果头没有设置Content-Type为text/xml,那么客户端解析XML的API会抛出一个错误)。在设置头之后,代码就开始连接字符串生成XML响应。实际要返回给客户端的信息被封装在<response>元素内。这是个根元素。<response>元素里的信息是基于客户端通过GET参数传来的名字:

// retrieve the user name
$name = $_GET['name'];
// generate output depending on the user name received from client
$userNames = array('CRISTIAN', 'BOGDAN', 'FILIP', 'MIHAI', 'YODA');
if (in_array(strtoupper($name), $userNames))
   echo 'Hello, master ' . htmlentities($name) . '!';
else if (trim($name) == '')
   echo 'Stranger, please tell me your name!';
else
   echo htmlentities($name) . ', I don\'t know you!';
// close the <response> element
echo '</response>';
?>



    用户输入的内容(假定是用户的名字)通过使用GET参数从客户端传送到服务器端。当把这个信息回传给客户端时,我们使用了PHP里的函数htmlentities来取代HTML代码里的特殊字符(如&,>),以确保在浏览器里能够安全地显示这些信息,消除潜在的问题和安全风险。

注意:
    在服务器端为客户端格式化信息(而不是直接在客户端来做)在编写商业代码时实际上是个不好的做法。一般来讲,服务器的职责就是以普通的格式发送数据,而由接受端来处理安全和格式化事务。如果你想某一天你要往数据库中插入完全相同的信息,但是数据库需要不同的格式化的序列(在这种情况下,应该由数据库来格式化数据而不是服务器),这样似乎更有意义。对于quickstart,在PHP中格式化HTML可以使得我们保持代码简短,易于理解和解释。

    如果你好奇,想测试quickstart.php看看它产生了什么,你可以在你的浏览器里装载http://localhost/ajax/quickstart/quickstart.php?name=Yoda。在客户端通过GET传递参数的好处是在你的浏览器它可以简单地产生个类似的请求,因为GET只是意味着你在URL查询字符串中附加了name/value这么一对参数。你将得到类似下面的东西:

这个XML消息在客户端通过quickstart.js的handleServerResponse()方法处理。更明确地说,是下面几行代码提取了“Hello,master Yoda”消息:

// extract the XML retrieved from the server
xmlResponse = xmlHttp.responseXML;
// obtain the document element (the root element) of the XML structure
xmlDocumentElement = xmlResponse.documentElement;
// get the text message, which is in the first child of
// the document element
helloMessage = xmlDocumentElement.firstChild.data;



    这里xmlHttp是XMLHttpRequest的对象,用来在客户端调用服务器上的quickstart.php脚本文件。它的responseXML属性处理得到的XML文档。XML的结构天然就是层次的。XML文件的根元素被称为文档元素。在我们这个例子中,文档元素是<response>元素,它只包含了一个子元素,就是我们感兴趣的文本信息。一旦获取了这个文本信息,那么它将被显示在客户页面上,即通过DOM(Document Object Model)来访问index.html里的divMessage元素实现:

// update the client display using the data received from the server
document.getElementById('divMessage').innerHTML = helloMessage;

document是JavaScript里的默认对象,允许你对页面的HTML代码里的元素进行操作。
quickstart.js里的其他代码用来向服务器产生请求以或得XML消息。createXmlHttpRequestObject()方法创建并返回XMLHttpRequest对象。这个方法要比它本来的要长些,这是因为我们要保持它对各个浏览器兼容。XMLHttpRequest的实例,即xmlHttp在process()方法里被使用以向产生服务器产生异步请求:

// make asynchronous HTTP request using the XMLHttpRequest object
function process()
{
   // proceed only if the xmlHttp object isn't busy
   if (xmlHttp.readyState == 4 || xmlHttp.readyState == 0)
   {
      // retrieve the name typed by the user on the form
      name = encodeURIComponent(document.getElementById("myName").value);
      // execute the quickstart.php page from the server
      xmlHttp.open("GET", "quickstart.php?name=" + name, true);
      // define the method to handle server responses
      xmlHttp.onreadystatechange = handleServerResponse;
      // make the server request
      xmlHttp.send(null);
   }
   else
      // if the connection is busy, try again after one second
      setTimeout('process()', 1000);
}



你在这里看到的实际上是AJAX的核心部分,即向服务器产生异步请求的代码。

    为什么向服务器发异步请求很重要呢?异步请求,它们的本性就是在请求发出的时候不中断处理(还有用户的体验),直到响应返回。在事件驱动的模型中也应用了异步处理。这是图形用户界面构建的一个好方式。没有事件,你或许得实时检查用户是否点击了一个按钮或者改变了窗口大小。使用事件,当按钮被点击的时候会自动地触发相应的处理程序,同时你也可以在事件处理方法里采取相应的动作。在AJAX中,这个理论起作用当你向服务器发送了一个请求,在响应消息返回的时候你会被自动通知。

    如果你对这个应用如果使用异步请求工作好奇的话,你可以象下面这样把xmlHttp.open的第三个参数改为false,然后手动地调用handleServerResponse。如果你这样做了,假定你输入你的名字的输入框就会在连接服务器的时候锁定(在这个情况下,锁定的时间取决于连接速度,如果服务器就在本机上的话这个会是很短的)。

// function calls the server using the XMLHttpRequest object
function process()
{
   // retrieve the name typed by the user on the form
   name = encodeURIComponent(document.getElementById("myName").value);
   // execute the quickstart.php page from the server
   xmlHttp.open("GET", "quickstart.php?name=" + name, false);
   // make synchronous server request (freezes processing until completed)
   xmlHttp.send(null);
   // read the response
   handleServerResponse();
}



    process()方法是通过使用XMLHttpRequest对象来初始化一个服务器请求。然而这凑效仅仅是在XMLHttpRequest对象没有忙于其他请求。在我们这个例子里,如果服务器的响应超过了1秒钟,这种情况是可能发生的。服务器响应延迟大可能是由于网络连接非常慢。所以,在process()的开始需要验证创建新请求是否是可行的:

// make asynchronous HTTP request using the XMLHttpRequest object
function process()
{
   // proceed only if the xmlHttp object isn't busy
   if (xmlHttp.readyState == 4 || xmlHttp.readyState == 0)
   {



     如果连接很忙,我们使用setTimeout来在1秒钟之后重试(这个方法的第二个参数用来指定执行由第一个参数指定的代码前要等待的毫秒数):

  // if the connection is busy, try again after one second
   setTimeout('process()', 1000);



如果这行很清楚了,那么你就可以很安全地创建一个新请求。准备服务器请求但是不提交的代码如下:

  // execute the quickstart.php page from the server
   xmlHttp.open("GET", 'quickstart.php?name=' + name, true);



    第一个参数指定了向服务器发送用户姓名的方法。你可以在GET和POST之间选择。第二个参数是你要访问的服务器页面。当第一个参数是GET是,你以name/value对的形式在查询串中传递参数。如果你希望调用是异步的话,第三个参数就是true。当做异步调用的时候,你不必等待响应。相反,你可以另外定义一个方法,它在请求的状态发生改变的时候自动地被调用:

  // define the method to handle server responses
   xmlHttp.onreadystatechange = handleServerResponse;



    一旦你设定了这个选项,你就可以安静地休息了——在你的请求发生了任何变化的时候,handleServerResponse方法都会被执行。在一切都设置好之后,你可以通过调用XMLHttpRequest的send方法来初始化请求:

  // make the server request
   xmlHttp.send(null);
}



让我们来看看handleServerResponse方法:

// executed automatically when a message is received from the server
function handleServerResponse()
{
   // move forward only if the transaction has completed
   if (xmlHttp.readyState == 4)
   {
      // status of 200 indicates the transaction completed successfully
      if (xmlHttp.status == 200)
      {



    无论什么时候请求的状态发生了变化,handleServerResponse方法都会被多次执行。只有当xmlHttp.readyState的值为4时,服务器请求才完成,此时你就可以继续读取结果。你也可以检查HTTP传输,如果报告状态是200的话,就意味着在HTTP请求过程中没有出错。当这些条件满足后,你可以自由地读取服务器响应和向用户显示信息。在接受和使用服务器响应之后,通过使用setTimeout方法可以再次开始这个过程,也就是使process()方法在1秒钟之后被执行(注意,尽管这个是不必要的,甚至也不是AJAX特别要求的在你的客户端代码中要有重复性的任务):

     // restart sequence
      setTimeout('process()', 1000);



最后,我们再重复下用户装载页面之后发生了什么(你可以参考图3,它是对这一过程的图形描述):
1. 用户装载index.html(这个相当于图3中的1到4步)
2. 用户开始(或者继续)输入他/她的名字(这个相当于图3中的第5步)
3. 当quickstart.js中的process()方法被执行时,它将异步调用服务器端的quickstart.php脚本。用户输入的文本作为查询字符串的参数在调用的时候传送(它是通过GET被传递的)。指定handleServerResponse方法来处理请求状态的改变
4. 在服务器端执行quickstart.php。它合成一个封装了服务器要传输给客户端的信息的XML文档。
5. 在请求的状态发生了变化时,handleServerResponse方法都会被多次执行。它最后一次被调用实在响应信息被正确地接受了。读取XML文档,抽取其中的信息,然后在页面上显示。
6. 当有新消息从服务器端传来时,用户显示就被更新。但是用户可以继续输入而不被打断。在延迟1秒之后,从步骤2又重新开始这个过程。

总结
这篇文章是对AJAX的一个简介。为了学习如何创建AJAX应用,理解他们为什么有用和在哪里有用是很重要的。就像是其他技术一样,AJAX并不是所有问题的解决方法,它只是提供了解决其中一部分的途径。
AJAX把客户端和服务器端按照功能结合起来了,增强了用户对你的网站的体验。XMLHttpRequest对象是使得客户端JavaScript代码能够异步调用服务器其他页面的一个关键元素。

作者简介
Cristian Darie是一个软件工程师,涉猎许多领域,并且是许多技术书的作者,包括“Beginning E-Commerce”系列。从能够按键盘的年龄就开始用计算机工作直到现在。他最初感觉到编程的成就感是在12岁时在他的第一次编程比赛中得了一等奖。从那时起,Darie就开始获得许多类似的荣誉。他现在在读博士学位,研究的方向是分布式应用程序结构。你可以在他的个人网站www.cristiandarie.ro上联系到他。Bogdan Brinzarea在计算机科学方面有很深的背景。他拥有罗马尼亚布加勒斯特Politehnica大学自控和计算机系的硕士和学士学位。他还有法国巴黎Ecole Polytechnique计算机科学系的旁听证。他感兴趣的领域广泛,包括嵌入式编程,分布式和移动计算以及新的Web技术。目前,他是希腊国家银行下面Romaneasca Banca的渠道专家,主要负责网上银行项目,协调其他与应用安全相关的项目和在银行领域应用新技术。Filip Chereches-Tosa是一个坚定支持未来基于Web的软件的Web开发者。他9岁时就开始接触计算机,那时他第一次得到了带磁带驱动器的Commondore 64。回到罗马尼亚的家,他开设了一家Web开发公司,名为eXigo,主要从事基于Web的应用开发和Web设计。他目前在Oradea大学学习计算机科学,同时也是罗马尼亚PHP社区的成员。Mihai Bucica从12岁开始编程并参加编程比赛(在许多比赛中都取胜)。Bucica获得了罗马尼亚布勒加斯特Politehanica大学自控和计算机系的学士学位。他目前在构建各种电子市场之间的通讯软件。他和别人合作了Beginning PHP 5和MySQL E-Commerce两本书。你可以在他的个人网站上和他取得联系。


资源
这篇文章取自Cristian Darie,Bogdan Brinzarea,Filip Chereches-Tosa和Mihai Bucica编著的《AJAX and PHPBuilding Responsive Web Applications》(2006年3月Packt出版,ISBN 1904811825)的第一章“AJAX and the Future of Web Applications”。
http://www.packtpub.com/book/ajax_php

你可以在网上获得这个摘录的例子
http://ajaxphp.packtpub.com/ajax/quickstart

如果想了解更多相关信息以及详细咨询,欢迎点击中英网http://www.uker.net/,或发email至:echo@uker.netUKer.net资深编辑将为您详细解答。

相关阅读

每日精选

点击查看更多

首页 手机 数码相机 笔记本 游戏 DIY硬件 硬件外设 办公中心 数字家电 平板电脑