65.9K
CodeProject 正在变化。 阅读更多。
Home

AJAX 和 PHP: 构建响应式 Web 应用程序 - 第一章: AJAX 和 Web 应用程序的未来

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.69/5 (29投票s)

2006年4月5日

CPOL

36分钟阅读

viewsIcon

179735

AJAX 世界的快速介绍。

book image

作者 Cristian Darie, Bogdan Brinzarea, Filip Cherecheş-Toşa, Mihai Bucica
标题 AJAX 与 PHP:构建响应式 Web 应用程序
出版社 Packt Publishing Ltd.
出版日期 2006 年 3 月
ISBN 1904811825
价格 31.49 美元
页数 275

引言

“电脑,画个机器人!” 我的小表弟对他见过的第一台电脑说。(由于我指示它不听陌生人的话,电脑对这个命令不予理睬。)如果你像我一样,你的第一反应会是“多傻啊”或“多好笑啊”——但这是一个错误。我们受过教育和建模的大脑已经学会了在一定程度上与电脑协作。人们正在接受教育以适应电脑,以弥补电脑理解人类能力的不足。(另一方面,人类也无法很好地适应自己,但那是另一回事了。)

这个小故事与人们本能地使用电脑的方式息息相关。在一个理想的世界里,那个口头命令就足以让电脑取悦我的表弟。在过去几年里,技术的用户友好性已经得到了很大的发展,但在我们拥有真正智能的电脑之前,还有很长的路要走。在那之前,人们需要学习如何与电脑协作——有些人甚至爱上了只有微小命令提示符的黑屏。

并非偶然,许多人的电脑使用习惯是由具有允许直观(和愉快的)人机交互用户界面的软件驱动的。这可能解释了鼠标右键的流行,拖放等花哨功能的奇妙之处,或者那个简单的文本框,它可以在短短 0.1 秒内(或者它声称如此)为你搜索整个互联网的内容。软件行业(或者说其中盈利的部分)已经看到、分析并学习了。现在市场上充满了带有闪亮按钮、图标、窗口和向导的程序,人们正在为此支付大量金钱

软件行业所学到的是,红色跑车中强大引擎的等价物是软件的可用性和可访问性。当从商业角度来看是好的东西,从人类角度来看也是好的时候,这是非常棒的,因为商业利润或多或少与客户满意度成正比。

我们计划在这本书中非常实用和简洁,但在回到你最喜欢的任务(编写代码)之前,值得退后一小步,只是为了记住我们在做什么以及为什么这样做。我们热爱技术,热爱每一次按键敲击发出的声音,所以很容易忘记技术存在的真正原因是为了服务人类,让他们的居家生活更有趣,工作更高效。

理解人们大脑的工作方式将是构建终极软件应用程序的关键。虽然我们离那一步还很远,但我们确实理解的是,最终用户需要直观的用户界面;他们并不真正在乎他们运行的是什么操作系统,只要他们获得的功能是他们所期望的。这是一个非常重要的细节需要牢记,因为许多程序员即使在与最终用户打交道时也倾向于用技术术语思考和说话(尽管在一个典型的开发团队中,程序员不直接与最终用户交互)。如果你不同意,试着回想一下你和非技术人员交谈时说过多少次“数据库”这个词。

通过观察人们在使用计算机系统时的需求和习惯,“软件可用性”一词应运而生——指的是满足用户界面期望、理解其工作性质并据此构建软件应用程序的艺术

从历史上看,可用性技术主要应用于桌面应用程序,原因很简单,因为当时网络应用程序所需的工具尚未问世。然而,随着互联网的日益成熟,它所催生的技术也越来越强大。

现代互联网技术不仅使您能够建立更好的在线形象,还允许构建更好的内部网/专用应用程序。拥有友好的网站对于在线业务至关重要,因为互联网永不休眠,客户经常迁移到看起来更好或感觉速度更快的下一个“大事件”。同时,能够构建友好的网络界面为内部网软件解决方案提供了替代选择,而这些解决方案以前主要作为桌面应用程序构建。

与桌面应用程序相比,构建用户友好的软件一直以来都比网络应用程序容易,原因很简单,因为网络被设计为传输文本和图像的工具,而非复杂功能。在过去几年中,随着越来越多的软件服务和功能通过网络提供,这个问题变得尤为突出。

因此,许多技术被开发出来(并且仍在开发中),以增加网络应用程序的华丽灯光、可访问性和强大功能。值得注意的例子包括 Java Applet 和 Macromedia Flash,它们要求用户在其网络浏览器中安装单独的库。

通过网络提供功能

Web 应用程序是指其功能在 Web 服务器上处理,并通过互联网或内部网等网络交付给最终用户的应用程序。最终用户使用瘦客户端(Web 浏览器)运行 Web 应用程序,该客户端知道如何显示和执行从服务器接收的数据。相比之下,桌面应用程序基于厚客户端(也称为富客户端或胖客户端),该客户端执行大部分处理。

Web 应用程序不断发展,梦想有一天它们能像其成熟(且强大)的亲戚——桌面应用程序那样看起来和表现。现在,任何与人类交互的计算机软件的行为甚至比以往任何时候都更加重要,因为如今的计算机用户群比过去更加多样化,那时用户也具有技术素养。现在您需要向销售部经理 Cindy 展示精美的报告,并且需要向销售人员 Dave 提供易于使用的数据输入表单。

因为最终用户的满意度至关重要,所以您构建的软件应用程序必须令所有与其交互的用户满意。就 Web 应用程序而言,当应用程序的界面和行为不再揭示功能是由本地桌面提供还是通过光纤或无线传输时,它们的成熟演进过程才算完成。过去,通过 Web 提供可用界面一直存在问题,原因很简单,因为人们在桌面应用程序中使用的功能(例如拖放以及在同一窗口中同时执行多项任务)是不可能实现的。

构建 Web 应用程序的另一个问题是标准化。如今,所有可 Web 访问的内容都必须至少通过两到三个浏览器进行验证,以确保所有访问者都能充分利用您的网站。

Web 应用程序的优势

是的,尝试通过 Web 提供功能时会遇到很多麻烦。但是,为什么一开始要费心尝试这样做,而不是构建普通的桌面应用程序呢?嗯,即使 Web 应用程序在用户友好性方面存在当前问题,它们也获得了极大的普及,因为它们提供了相对于桌面应用程序的一些主要技术优势。

  • Web 应用程序易于交付且成本低廉。通过 Web 应用程序,公司可以降低负责在用户机器上安装软件的 IT 部门的成本。对于 Web 应用程序,用户只需要一台带有可用 Web 浏览器和互联网或内部网连接的计算机。
  • Web 应用程序易于升级且成本低廉。软件的维护成本一直很高。由于升级现有软件与安装新软件类似,因此上述 Web 应用程序的优势也适用于此。一旦服务器上的应用程序升级,每个人都会获得新版本。
  • Web 应用程序对最终用户具有灵活的要求。只需将您的 Web 应用程序安装在服务器上——任何现代操作系统都可以——您就可以通过互联网/内网在任何 Mac、Windows 或 Linux 机器上使用它,等等。如果应用程序构建得当,它将在任何现代 Web 浏览器(例如 Internet Explorer、Mozilla Firefox、Opera 或 Safari)上运行良好。
  • Web 应用程序使集中式数据存储更容易实现。当有多个需要访问相同数据的位置时,将所有数据存储在一个地方比在每个位置拥有单独的数据库要容易得多。这样,您可以避免潜在的数据同步操作,并降低安全风险。

在本书中,我们将进一步探讨如何利用现代网络技术构建更好的网络应用程序,充分发挥网络提供的可能性。但在深入细节之前,让我们上一堂简短的历史课。

自 1990 年以来构建网站

虽然互联网的历史更长一些,但 1991 年是超文本传输协议(HTTP)发明的一年,该协议至今仍用于在互联网上传输数据。在其最初的几个版本中,它除了打开和关闭连接之外,并没有做太多事情。HTTP 的后续版本(1.0 版本于 1996 年发布,1.1 版本于 1999 年发布)成为了我们现在都了解和使用的协议。

HTTP 和 HTML

HTTP 得到所有 Web 浏览器的支持,并且它非常出色地完成了其设计目的——检索简单的 Web 内容。无论您使用您喜欢的 Web 浏览器请求 Web 页面,都假定使用 HTTP 协议。因此,例如,当您在 Firefox 的地址栏中输入 www.mozilla.org 时,它默认会假定您指的是 http://www.mozilla.org

互联网的标准文档类型是超文本标记语言 (HTML),它由网络浏览器能够理解、解析和显示的标记组成。HTML 是一种描述文档格式和内容的语言,主要由静态文本和图像构成。HTML 的设计目的并非用于构建具有交互内容或用户友好界面的复杂网络应用程序。当您需要通过 HTTP 访问另一个 HTML 页面时,您需要启动一个完整的页面重新加载,并且您请求的 HTML 页面必须在请求之前以静态文档的形式存在于指定位置。很明显,这些限制并不能真正鼓励构建任何有趣的东西。

尽管如此,HTTP 和 HTML 仍然是一对非常成功的组合,Web 服务器和 Web 客户端(浏览器)都理解它们。它们是当今互联网的基础。图 1.1 展示了用户使用 HTTP 协议从互联网请求 Web 页面时的简单事务

图 1.1 一个简单的 HTTP 请求

你需要记住的三点:

  1. HTTP 事务总是发生在网络客户端(发出请求的软件,例如网络浏览器)和网络服务器(响应请求的软件,例如 Apache 或 IIS)之间。从现在起,在这本书中,当我们说“客户端”时,我们指的是网络客户端,当我们说“服务器”时,我们指的是网络服务器。
  2. 用户是指使用客户端的人。
  3. 即使 HTTP(及其安全版本 HTTPS)可以说是互联网上最重要的协议,但它也不是唯一的协议。各种 Web 服务器使用不同的协议来完成各种任务,这些任务通常与简单的 Web 浏览无关。在本书中,我们最常使用的协议是 HTTP;当我们说“Web 请求”时,除非明确提及其他协议,否则我们假定它是使用 HTTP 协议的请求。

当然,HTTP-HTML 组合的功能非常有限——它只允许用户从互联网检索静态内容(HTML 页面)。为了弥补功能的不足,开发了多种技术。

虽然我们现在讨论的所有网络请求仍然使用 HTTP 协议传输数据,但数据本身可以在网络服务器上动态构建(例如,使用来自数据库的信息),并且这些数据可以包含不仅仅是纯 HTML,从而允许客户端执行某些功能,而不仅仅是显示静态页面。

使网络变得更智能的技术可分为以下两大类

  • 客户端技术使网络客户端能够完成比显示静态文档更有趣的事情。通常,这些技术是 HTML 的扩展,并不会完全取代它。
  • 服务器端技术是指使服务器能够存储逻辑以动态构建网页的技术。

PHP 和其他服务器端技术

服务器端 Web 技术使 Web 服务器能够做的事情远不止仅仅返回请求的 HTML 文件,例如执行复杂的计算、进行面向对象编程、使用数据库等等。

想象一下亚马逊要处理多少数据才能为每个访客计算个性化的产品推荐,或者谷歌在搜索其庞大数据库以满足您的请求时要处理多少数据。是的,服务器端处理是引发网络革命的引擎,也是如今互联网如此有用的原因。

需要记住的重要一点是,无论服务器端发生什么,客户端接收到的响应都必须是客户端能够理解的语言(显然)——例如 HTML,它有很多限制,正如前面提到的。

PHP 是用于实现服务器端逻辑的技术之一。第 3 章将作为 PHP 的介绍,在本书中构建 AJAX 案例研究时,我们将使用 PHP。不过,最好知道 PHP 有许多竞争对手,例如 ASP.NET(活动服务器页面,微软的 Web 开发技术)、Java Server Pages (JSP)、Perl、ColdFusion、Ruby on Rails 等。它们中的每一个都有自己的方式允许程序员构建服务器端功能。

PHP 不仅是一种服务器端技术,还是一种脚本语言,程序员可以用它来创建 PHP 脚本。图 1.2 显示了对名为 index.php 的 PHP 页面的请求。这一次,服务器不是返回 index.php 的内容,而是执行 index.php 并返回结果。这些结果必须是 HTML 或客户端能够理解的其他语言。

图 1.2:客户端请求 PHP 页面

在服务器端,您通常还需要一个数据库服务器来管理您的数据。在本书的案例研究中,我们将使用 MySQL,但概念与其他任何服务器相同。您将在第 3 章学习使用数据库和 PHP 的基础知识。

然而,即使使用能够构建定制的数据库驱动响应的 PHP,浏览器仍然显示的是静态、无聊且不太智能的网页文档。

在 Web 客户端上对更智能、更强大功能的需求催生了一套独立的、称为客户端技术的技术。如今的浏览器不仅知道如何解析简单的 HTML,让我们看看它们是如何做到的。

JavaScript 和其他客户端技术

各种客户端技术在许多方面都有所不同,从它们被 Web 客户端加载和执行的方式开始。JavaScript 是一种脚本语言,其代码以纯文本编写,可以嵌入到 HTML 页面中以增强其功能。当客户端请求 HTML 页面时,该 HTML 页面可以包含 JavaScript。所有现代 Web 浏览器都支持 JavaScript,无需用户在系统上安装新组件。

JavaScript 是一种独立的语言(理论上它不与 Web 开发绑定),它受大多数 Web 客户端在任何平台上支持,并且具有一些面向对象的功能。JavaScript 不是一种编译语言,因此不适用于密集计算或编写设备驱动程序,并且它必须完整地到达客户端浏览器才能被解释,因此它也不安全,但当用于网页时,它表现良好。

有了 JavaScript,开发者终于可以构建带有雪花飘落的网页,实现客户端表单验证,这样用户就不会因为忘记提供所有详细信息(如密码或信用卡号),或者电子邮件地址格式不正确而导致整个页面重新加载(从而意外丢失所有输入的数据)。然而,尽管 JavaScript 潜力巨大,但它从未被持续用于使网络体验真正用户友好,与桌面应用程序的用户体验类似。

其他流行的客户端功能实现技术是 Java applet 和 Macromedia Flash。Java applet 使用流行且强大的 Java 语言编写,并通过需要单独安装在系统上的 Java 运行时执行。Java applet 当然是更复杂项目的首选,但它们已经失去了曾经在 Web 应用程序中拥有的流行度,因为它们消耗了大量的系统资源。有时它们甚至需要很长的启动时间,并且对于简单 Web 应用程序的较小需求来说,它们通常过于庞大和强大。

Macromedia Flash 拥有非常强大的工具,用于创建动画和图形效果,它是通过网络交付此类程序的实际标准。Flash 也要求客户端安装浏览器插件。基于 Flash 的技术变得越来越强大,并且不断有新的技术出现。

将 HTML 与服务器端技术和客户端技术相结合,可以构建非常强大的 Web 解决方案。

缺少了什么?

既然有各种选择,为什么会有人想要新的东西呢?缺少了什么?

正如本章开头所指出的,技术之所以存在是为了满足现有的市场需求。而市场的一部分希望向 Web 客户端提供更强大的功能,而无需使用 Flash、Java applet 或其他被认为对于某些目的来说过于华丽或笨重的技术。对于这些情况,开发人员通常使用 HTML、JavaScript 和 PHP(或另一种服务器端技术)创建网站和 Web 应用程序。此场景的典型请求如图 1.3 所示,它展示了一个 HTTP 请求,响应由使用 PHP 编程构建的 HTML 和 JavaScript 组成。

图 1.3:HTTP、HTML、PHP 和 JavaScript 协同工作

这个场景的隐含问题是,每次客户端需要从服务器获取新数据时,都必须发起一个新的 HTTP 请求来重新加载页面,从而冻结用户的活动。页面重新加载是当前场景中的新弊端,而 AJAX 应运而生,为我们提供了解决方案。

理解 AJAX

AJAX 是Asynchronous JavaScript and XML(异步 JavaScript 和 XML)的缩写。如果你觉得这听起来没什么,我们同意。简单来说,AJAX 可以解读为“增强型 JavaScript”,因为它本质上提供了一种技术,允许客户端 JavaScript 在后台进行服务器调用并根据需要检索额外数据,更新页面的特定部分而无需导致页面完全重新加载。图 1.4 以视觉方式展示了当访问者请求一个典型的支持 AJAX 的网页时会发生什么

图 1.4:典型的 AJAX 调用

从长远来看,AJAX 旨在在执行用户请求的操作时,在客户端功能和服务器功能之间实现更好的平衡。直到现在,客户端功能和服务器端功能一直被视为独立的功能片段,一次只工作一个来响应用户的操作。AJAX 提供了通过允许客户端和服务器在用户正在页面上工作时在后台进行通信来平衡客户端和服务器之间负载的解决方案。

用一个简单的例子来解释,考虑一下网络表单,用户被要求输入一些数据(例如姓名、电子邮件地址、密码、信用卡等),这些数据在到达应用程序的业务层之前需要进行验证。没有 AJAX,有两种表单验证技术。第一种是让用户输入所有必需的数据,让他或她提交页面,然后在服务器上执行验证。在这种情况下,用户在等待新页面加载时会经历一段死区时间。另一种选择是在客户端进行验证,但这并不总是可能的(或可行的),因为它意味着在客户端加载过多数据(只需考虑是否需要验证输入的城市和国家是否匹配)。

在支持 AJAX 的场景中,Web 应用程序可以通过在后台进行服务器调用来验证输入的数据,而用户可以继续输入。例如,在用户选择一个国家后,Web 浏览器会调用服务器以动态加载该国家/地区的城市列表,而不会中断用户的当前活动。您将在第 4 章中找到 AJAX 表单验证的示例。

AJAX 可以发挥作用的例子不胜枚举。为了更好地感受和理解 AJAX 能为您做什么,请查看这些实时热门示例

  • Google Suggest 可帮助您进行 Google 搜索。其功能相当出色;请访问 Google 体验。类似功能由 Yahoo! 即时搜索提供。(您将在第 6 章中学习如何构建类似功能。)
  • GMail。GMail 现在非常流行,无需多言。其他基于网络的电子邮件服务,如 Yahoo! Mail 和 Hotmail,也紧随潮流,提供了基于 AJAX 的功能。
  • Google 地图雅虎地图Windows Live Local
  • 其他服务,例如 WritelyBasecamp

在本书的后续内容中,您会看到更多示例。

正如任何其他技术一样,AJAX 也可能被过度使用或误用。仅仅在您的网站上使用 AJAX 并不能保证您的网站会更好。如何充分利用这项技术取决于您。

所以,AJAX 旨在通过让网页在用户工作时透明地进行异步服务器调用,从而创建更具通用性和交互性的网络应用程序。AJAX 是网络开发者可以用来创建更智能的、在与人类交互时表现更好的网络应用程序的工具。

AJAX 所使用的技术已在所有现代网络浏览器中实现,例如 Mozilla Firefox、Internet Explorer 或 Opera,因此客户端无需安装任何额外模块即可运行 AJAX 网站。AJAX 由以下部分组成

  • JavaScript 是 AJAX 的核心要素,它允许您构建客户端功能。在您的 JavaScript 函数中,您将大量使用文档对象模型(DOM)来操作 HTML 页面的一部分。
  • XMLHttpRequest 对象使 JavaScript 能够异步访问服务器,以便用户可以继续工作,同时在后台执行功能。访问服务器简单地意味着向位于服务器上的文件或脚本发出简单的 HTTP 请求。HTTP 请求很容易发出,并且不会引起任何防火墙相关问题。
  • 需要服务器端技术来处理来自 JavaScript 客户端的请求。在本书中,我们将使用 PHP 来完成服务器端的工作。

对于客户端-服务器通信,各方需要一种方式来传递数据理解这些数据。传递数据是简单的一部分。访问服务器的客户端脚本(使用XMLHttpRequest对象)可以使用 GET 或 POST 发送名称-值对。使用任何服务器脚本读取这些值都非常简单。

服务器脚本只需通过 HTTP 发回响应,但与普通网站不同的是,响应将采用客户端上的 JavaScript 代码可以简单解析的格式。建议的格式是 XML其优点是得到广泛支持,并且有许多库可以轻松操作 XML 文档。但如果您愿意,也可以选择其他格式(甚至可以发送纯文本),XML 的流行替代品是 JavaScript 对象表示法(JSON)。

本书假设您已经熟悉 AJAX 组件,除了可能不太流行的 XMLHttpRequest 对象。不过,为了确保我们都理解一致,我们将在第 2 章和第 3 章中一起深入了解这些组件的工作方式以及它们如何协同工作。在此之前,本章的其余部分将着重于宏观概述,我们还将为最急切的读者编写一个 AJAX 程序。

AJAX 的任何组件都不是新的,也不是革命性的(或者至少是演进的),就像当前围绕 AJAX 的炒作可能暗示的那样:AJAX 的所有组件自 1998 年左右就已存在。AJAX 这个名称诞生于 2005 年,在Jesse James Garret 的文章中,并在 Google 的许多应用程序中使用后获得了极大的普及。

AJAX 的新颖之处在于,市场上首次有足够的能量鼓励标准化,并将这些能量集中到一个清晰的进化方向上。因此,许多 AJAX 库正在开发中,许多支持 AJAX 的网站也已出现。微软也通过其 Atlas 项目推动 AJAX 开发。

在构建新的 Web 应用程序时,AJAX 为您带来以下潜在好处

  • 它使创建更好、更具响应性的网站和网络应用程序成为可能。
  • 由于其流行性,它鼓励开发有助于开发者在执行常见任务时避免重复造轮子的模式。
  • 它利用现有技术。
  • 它利用了现有开发者的技能。
  • AJAX 的功能与 Web 浏览器提供的现有功能(例如,调整页面大小、页面导航等)完美集成。

AJAX 可以成功应用的常见场景有:

  • 启用即时服务器端表单验证,在页面最初加载时无法将所有所需数据传输到客户端进行验证的情况下非常有用。第 4 章包含一个表单验证案例研究。
  • 创建不需要外部库(如 Java 运行时环境或 Flash)的简单在线聊天解决方案。您将在第 5 章中构建这样一个程序。
  • 构建类似 Google Suggest 的功能,例如您将在第 6 章中构建的一个示例。
  • 更有效地利用其他现有技术的强大功能。在第 7 章中,您将使用可伸缩矢量图形 (SVG) 实现实时图表解决方案,在第 10 章中,您将使用外部 AJAX 库创建简单的拖放列表。
  • 编码响应式数据网格,动态更新服务器端数据库。您将在第 8 章中创建此类应用程序。
  • 构建需要从各种外部源实时更新的应用程序。在第 9 章中,您将创建一个简单的 RSS 聚合器。

AJAX 的潜在问题是

  • 因为页面地址在工作时不会改变,所以您无法轻松地为支持 AJAX 的页面添加书签。对于 AJAX 应用程序,书签具有不同的含义,具体取决于您的特定应用程序,通常意味着您需要以某种方式保存状态(想想桌面应用程序是如何发生的——那里没有书签功能)。
  • 搜索引擎可能无法索引您的 AJAX 应用程序网站的所有部分。
  • 浏览器中的“后退”按钮不会产生与传统 Web 应用程序相同的结果,因为所有操作都在同一页面内发生。
  • JavaScript 可以在客户端被禁用,这使得 AJAX 应用程序无法运行,因此,在可能的情况下,在您的网站上有一个备用计划是明智之举,以避免失去访问者。

最后,在您开始编写第一个 AJAX 程序之前,这里有一些链接可能会帮助您进入激动人心的 AJAX 世界

此列表绝非完整。如果您需要更多在线资源,Google 肯定会为您提供帮助。在接下来的章节中,您将看到更多链接,但这些链接将更具体地与您将要学习的特定技术相关。

使用 AJAX 和 PHP 构建一个简单的应用程序

那么,让我们编写一些代码吧!在接下来的页面中,您将构建一个简单的 AJAX 应用程序。

本练习面向最急于尽快开始编码的读者,但它假定您已经熟悉 JavaScript、PHP 和 XML。如果情况并非如此,或者您在任何时候觉得本练习过于困难,请随时跳到第 2 章。在第 2 章和第 3 章中,我们将更深入地研究 AJAX 技术和技巧,一切都将变得清晰。

您将在此处创建一个名为 quickstart 的简单 AJAX Web 应用程序,用户被要求输入他或她的姓名,服务器在用户输入时不断发回响应。图 1.5 显示了用户加载的初始页面 index.html。(请注意,当请求 quickstart Web 文件夹时,即使未明确提及文件名,index.html 也会默认加载。)

图 1.5:您的快速入门应用程序首页

当用户输入时,服务器会以固定的时间间隔异步调用,以查看它是否识别当前名称。服务器会自动调用,大约每秒一次,这解释了为什么我们不需要按钮(例如“发送”按钮)来通知何时完成输入。(这种方法可能不适用于真正的登录机制,但它非常适合演示一些 AJAX 功能。)

根据输入的名称,服务器的消息可能会有所不同;请参见图 1.6 中的示例。

图 1.6:用户从 Web 应用程序收到即时回复

在线查看此示例

也许乍一看并没有什么非凡之处。我们特意将第一个例子保持简单,以便更容易理解。这个应用程序的特别之处在于,显示的消息是自动从服务器发出的,而不会中断用户的操作。(消息在用户输入姓名时显示)。页面不会重新加载以显示新数据,即使需要进行服务器调用才能获取该数据。使用非 AJAX Web 开发技术,这不是一个简单的任务。

该应用程序由以下三个文件组成

  1. index.html 是用户请求的初始 HTML 文件。
  2. quickstart.js 是一个包含 JavaScript 代码的文件,它与 index.html 一起加载到客户端。当需要服务器端功能时,该文件将处理向服务器发出异步请求。
  3. quickstart.php 是位于服务器上的 PHP 脚本,由客户端 quickstart.js 文件中的 JavaScript 代码调用。

图 1.7 显示了运行此应用程序时发生的操作

图 1.7:您的快速入门应用程序内部工作原理图

步骤 1 到 5 是一个典型的 HTTP 请求。发出请求后,用户需要等待页面加载。对于典型的(非 AJAX)Web 应用程序,每次客户端需要从服务器获取新数据时都会发生这样的页面重新加载。

步骤 5 到 9 演示了 AJAX 式调用——更具体地说,是一系列异步 HTTP 请求。服务器使用 XMLHttpRequest 对象在后台访问。在此期间,用户可以像使用普通桌面应用程序一样正常使用页面。为了从服务器检索数据并更新网页,不会出现页面刷新或重新加载。

现在是时候在您的机器上实现这段代码了。在继续之前,请确保您已按照附录 A 中的说明准备好工作环境,其中指导您如何安装和设置 PHP 和 Apache,以及设置本书示例中使用的数据库。(本快速入门示例不需要数据库。)

本书中的所有练习都假设您已按照附录 A 中的说明安装了您的机器。如果您以不同的方式设置了环境,您可能需要进行各种更改,例如使用不同的文件夹名称等等。

行动时刻——快速入门 AJAX
  1. 在附录 A 中,您会收到指示来设置一个 Web 服务器,并创建一个名为 ajax 的 Web 可访问文件夹,用于托管本书的所有代码。在 ajax 文件夹下,创建一个名为 quick-start 的新文件夹。
  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 = 
                                                '' + helloMessage + '';
          // 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. 创建一个名为 quickstart.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. 现在您应该能够使用您喜欢的网络浏览器加载 https:///ajax/quickstart 来访问您的新程序。加载页面后,您应该会得到如图 1.5 和 1.6 所示的页面。

如果您在运行应用程序时遇到任何问题,请检查您是否按照附录 A 中所述的安装和配置过程正确操作。大多数错误都是由于诸如拼写错误之类的小问题造成的。在第 2 章和第 3 章中,您将学习如何在 JavaScript 和 PHP 代码中实现错误处理。

刚刚发生了什么?

有趣的部分来了——理解这些代码中发生了什么。(请记住,我们将在接下来的两章中讨论更多的技术细节。)

让我们从用户首先与之交互的文件 index.html 开始。该文件引用了神秘的 JavaScript 文件 quickstart.js,并为客户端构建了一个非常简单的 Web 界面。在 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() 函数内部发生的事情之前,让我们先看看服务器端发生了什么。在 Web 服务器上,您有一个名为 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>

如果从客户端收到的用户名为空,消息将是“陌生人,请告诉我您的名字!”。如果名字是 Cristian、Bogdan、Filip、Mihai 或 Yoda,服务器将回复“你好,主人 <user name>!”。如果名字是其他任何内容,消息将是“<user name>,我不认识你!”。因此,如果米老鼠输入了他的名字,服务器将发回以下 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(用于在客户端解析 XML 的 API 如果标题未将 Content-Type 设置为 text/xml 将抛出错误)。设置标题后,代码通过连接字符串构建 XML 响应。要返回给客户端的实际文本封装在 <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 参数从客户端发送到服务器。在将此文本发送回客户端时,我们使用 htmlentities PHP 函数将特殊字符替换为它们的 HTML 代码(例如 & 或 >),确保消息将安全地显示在 Web 浏览器中,从而消除潜在的问题和安全风险。

在服务器上为客户端格式化文本(而不是直接在客户端执行此操作)在编写生产代码时实际上是一种不好的做法。理想情况下,服务器的责任是以通用格式发送数据,而接收方的责任是处理安全和格式化问题。如果您考虑到有一天您可能需要将完全相同的文本插入数据库,但数据库需要不同的格式化序列(在这种情况下,数据库处理脚本也会执行格式化工作,而不是服务器),那么这更具意义。对于快速入门场景,在 PHP 中格式化 HTML 让我们能够使代码更短、更容易理解和解释。

如果您好奇地想测试 quickstart.php 并查看它生成了什么,请在您的 Web 浏览器中加载 https:///ajax/quickstart/quickstart.php?name=Yoda。通过 GET 从客户端发送参数的优点是使用 Web 浏览器模拟此类请求非常简单,因为 GET 仅表示您将参数作为名称/值对附加到 URL 查询字符串中。您应该会得到类似这样的内容

图 1.8:quickstart.php 生成的 XML 数据

这个 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 是用于从客户端调用服务器脚本 quickstart.phpXMLHttpRequest 对象。其 responseXML 属性提取检索到的 XML 文档。XML 结构本质上是分层的,XML 文档的根元素称为文档元素。在我们的例子中,文档元素是 <response> 元素,它包含一个子元素,即我们感兴趣的文本消息。一旦检索到文本消息,它就会使用 DOM 访问 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 对象的实例。此函数比它可能更长,因为我们需要使其跨浏览器兼容——我们将在第 2 章中讨论详细信息,现在重要的是了解它的作用。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 对象没有忙于进行另一个请求时,才可能实现这一点。在我们的案例中,如果服务器回复需要超过一秒,这种情况就会发生,这可能发生在互联网连接非常慢的情况下。因此,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 在一秒后重试(函数的第二个参数指定在执行第一个参数指定的代码片段之前等待的毫秒数

// 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);

第一个参数指定了用于向服务器发送用户名的L方法,您可以选择 GET 或 POST(在第 3 章中了解更多)。第二个参数是您要访问的服务器页面;当第一个参数是 GET 时,您将参数作为名称/值对发送到查询字符串中。第三个参数为 true 表示您希望异步进行调用。进行异步调用时,您不需要等待响应。相反,您定义另一个函数,当请求状态改变时自动调用

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

一旦设置了此选项,您就可以高枕无忧了——当您的请求发生任何事情时,系统将自动执行 handleServerResponse 函数。设置好一切后,您可以通过调用 XMLHttpRequestsend 方法来发起请求

    // 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() 函数在一秒后执行(请注意,在客户端代码中包含重复任务并非必要,也不是 AJAX 特有的)

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

最后,让我们重申用户加载页面后发生的情况(您可以参考图 1.7 进行可视化表示)

  1. 用户加载 index.html(这对应于图 1.7 中的步骤 1-4)。
  2. 用户开始(或继续)输入他或她的姓名(这对应于图 1.7 中的步骤 5)。
  3. quickstart.js 中的 process() 方法执行时,它会异步调用一个名为 quickstart.php 的服务器脚本。用户输入的文本作为查询字符串参数(通过 GET 方式)传递给调用。handeServerResponse 函数旨在处理请求状态更改。
  4. quickstart.php 在服务器上执行。它会组合一个 XML 文档,其中封装了服务器希望传输给客户端的消息。
  5. 客户端的 handleServerResponse 方法会随着请求状态的改变而多次执行。最后一次调用是当响应成功接收时。XML 被读取;消息被提取并显示在页面上。
  6. 用户显示器会更新服务器发来的新消息,但用户可以继续输入而不受任何干扰。一秒钟延迟后,进程从步骤 2 重新开始。

摘要

本章主要介绍了 AJAX 世界的快速入门。为了继续学习如何构建 AJAX 应用程序,了解它们为何以及何处有用至关重要。与任何其他技术一样,AJAX 并非所有问题的答案,但它提供了解决其中一些问题的方法。

AJAX 结合了客户端和服务器端功能,以增强您网站的用户体验。XMLHttpRequest 对象是使客户端 JavaScript 代码能够异步调用服务器页面 的关键元素。本章有意简短,可能给您留下了许多问题——这很好!准备好一本专门回答问题并展示许多有趣功能的书吧!

© . All rights reserved.