[译]将基于web的php程序迁移到ajax

文章分类:misc  查看次数:466 + 122

原文地址:click here

网站开发通常是杂乱的。近几年来,我们的工具箱塞满了各种各样的工具,通常他们难以理解,并且很难结合到一块。网页代码已经成为一个历史问题。一个普通的web页总是包含了html,javascript,服务端代码。用户界面交织着各种逻辑规则和客户端-服务端通信。在大多数的编成环境里,我们使用文档的API说明,所以我们只需传递几个参数,然后就能得到我们想要的结果了。而web编程,我们通常需要hack,比如生成隐藏表单或者载入新的页面,就因为一些小的改变。难道就不能将这个过程变得更加合理吗?

这篇文章彻底改变了基于数据库的表单设计。我们先来看看过时的代码——混合了html,javascript,php。然后通过先进的技术,比如,和先进的工具,比如jquery,来重构这些代码。好处是

  • 将动态内容与静态内容分开来
  • 将内容,样式和处理过程分开来
  • 通过函数来调用服务端代码
  • 页面的部分内容更新,而不是重新加载整个页面
  • 更快速地开发,提高了代码的可维护性
  • 加速载入过程,缓存优化


过时的代码编写

首先是html,然后是表单,javascript,最后是php代码。在表单里填入几个变量,然后在提交给服务端。或者在javascript里生成一个很长的url,javascript几乎是不可调试的。核心的变量,比如"window"并不是该语言的一部分。而且还有浏览器之间的差异。幸好w3c定义了dom,而且出现了许多跨浏览器的dhtml库。

有些动态页面很简单,你查询一次数据库然后格式化输出结果,但通常比这个更复杂。通过数据库生成下拉菜单或者选择按钮。有些页面涵盖多个表单,使得维护更加麻烦,每个表单的提交将重新生成页面。

通常结构化一个应用程序包括:

  • 将所有的东西放到一个script里。 第一次运行的时候,查询数据库然后生成html代码,当再次请求时,表单传递几个命令(如add,delete)和参数(如person's id)给后台的php程序,脚本修改数据库,然后修改页面的相应内容。表单的内容将反应出数据库变化的结果
  • 使用两个脚本文件。 第一个查询数据库,并生成html代码。与上面的不同是:
    • 表单有一个iframe页来显示动态数据
    • 表单的action,存储于第二个脚本里
    • 表单的target是iframe的name

第二个看起来更好一些,但也有个问题,如果一个action改变了form的几个元素,那么整个页面需要被重新载入,我们需要一个第三项选择。

新的工具箱

无论是多么小的改变,只要提交了表单,就会重新生成一个页面(iframe可以看作一个迷你页面,而且大小不可改变) ,javascript的remoting技术将使这一切变得更加完美。
我认为最有用的贡献是:

  • innerHTML
  • XMLHttpRequest
  • JSON
  • New JavaScript libraries

innerHTML
这个差不多是所有的时下的浏览器都支持的一个属性。有了它我们可以得到并设置它的内容,而不需要刷新页面。比如

<p id="changeme">Now you see it.</p>
 

可以修改为

var obj = document.getElementById("changeme");
obj.innerHTML = "Now you don't";
 

产生的结果是

<p id="changeme">Now you don't</p>
 

虽然DOM有许多函数可以动态改变内容,但innerHTML不失为一个更快更好的方法,但很可惜innerHTML在IE浏览器的table里是只读的。

XMLHttpRequest

这是一个发送客户端请求到服务端的函数,IE5就已经包含了,几乎支持所有当前流行的浏览器。

在2005年开始备受关注,由于其异步获取内容的特性。

JSON

JavaScript Object Notation,将json代码解析成javascript的数据格式,(eval(json_string)),通常比解析XML更快。

JavaScript libraries

高质量的js库已经被开发出来了,为解决浏览器兼容和提高编程效率带来了很大的方便。这里我选择jQuery(我也选择jQuery,哈哈)
因为:

  • 体积小(压缩后只有19k)
  • 麻雀虽小,五脏俱全
  • 良好的文档说明和社区支持
  • 灵活性(她的选择语法包括了CSS1-3和XPATH,方便定位页面内容)
  • 结构化(非常容易扩展)

彻底改变

历史已经结束,良好的架构开始了

为了使表现更加简洁,我们的代码忽略了错误和安全问题,目的是为了展现jquery和技术是如何改变历史的。

现在来定义表单的需求

1、从people表中得到数据,主键是id
2、将名字以下拉菜单的形式出现
3、让用户选择一个用户
4、在表格里显示出该用户的相关数据

版本1:原始代码

在这个初始的版本里,我们将所有的内容都放在一个php页面里。
people1.php:

<?php
$cmd    = @$_REQUEST["cmd"];
$id     = @$_REQUEST["id"];
mysql_connect($server, $user, $password);
mysql_select_db("test");
?>
<html>
<head><title>Old Form</title>
<script>
// Get the selected user and retrieve his/her info
function user_info(sel)
        {
        var opt    = sel.options;
        var user_id    = opt[sel.selectedIndex].value;
        // Construct a GET URL, or create a hidden field for "cmd"
        var url  =  "people1.php?cmd=info&id=" + user_id;
        window.location.href = url;
        }
</script>
</head>
<body>
<form action="people1.php" method="post">
People<br>
<select name="people" onchange="user_info(this)">
<option value="">(select a person)
<?php
// Get all users and display every time script is called
$result = mysql_query("select id, fname, lname from people");
while ($row = mysql_fetch_array($result, MYSQL_ASSOC))
        echo "<option value='$row[id]'",
                $row["id"] == $id ? " selected" : "",
                ">$row[fname] $row[lname]\n";
?>
</select>
<?php
if ($cmd == "info")
        {
    $id     = mysql_real_escape_string($id);
        $result = mysql_query("select * from people where id='$id'");
        $info = mysql_fetch_array($result, MYSQL_ASSOC);
        }
else
        $info = array("fname"=>" ", "lname"=>" ", "dance"=>" ", "pie"=>" ");
echo <<< END
<br>
<table border=1>
<tr>
<td>First Name</td><td>Last Name</td><td>Dance</td><td>Pie</td></tr>
<tr>
<td>$info[fname]</td>
<td>$info[lname]</td>
<td>$info[dance]</td>
<td>$info[pie]</td>
</tr>
</table>

END;
?>
</form>
</body>
</html>
 

这个比较容易理解和书写。但是存在着很多问题。好处是所有的代码都写在了一个文件里,坏处跟好处是同一个理由。

新设计
下面是计划:

  • 将原代码分为三个文件:静态内容(html),客户端脚本文件(js),服务端代码(php)
  • 在html中包含jquery和脚本文件
  • 给所有动态内容添加一个特定id
  • 定义js函数,来连接客户端与服务端

我们可以通过两种方法来合并服务端返回的代码和html本身的代码

  • php返回格式化的html代码,然后通过innerHTML来呈现
  • php返回JSON格式的内容,javascript解析并将其插入到html中

我们两个都来试试

版本2,提交,html代码返回
在这个版本中,php生成options标签,返回到html,我们只需将它插入到相应的select标签就行了,现在以html代码开始,它只是个容器
people2.html

<html>
<head><title>New Form Version 1</title>
<script src="jquery.js"></script>
<script src="people2.js"></script>
</head>
<body>
People<br>
<select id="people">
</select>
<br>
<table border=1>
<thead>
<tr><td>First Name</td><td>Last Name</td><td>Dance</td><td>Pie</td></tr>
</thead>
<tbody id="info">
<tr>
<td> </td>
<td> </td>
<td> </td>
<td> </td>
</tr>
</tbody>
</table>
</body>
</html>
 

下面再来看看js代码,jquery的核心函数是$(),返回一个jquery object,她的参数可以有多种形式。我们只用下面两种

  • $(document):选择document DOM
  • $("#myid"):选择id为myid的元素

(为了帮助阅读,我已经格式化了代码)
people2.js

$(document).ready
    (
    function()
        {
        // Call this when the DOM is ready:
        $("#people").load("people2.php?cmd=init");
        // Call this when a person is selected:
        $("#people").change(function()
            {
            // get the user's id from the selected option:
            var user_id = $(":selected").val();
            $("#info").load("people2.php?cmd=info&id=" + user_id);
            });
        }
    );
 

这就是我们所需要的所有的js代码,下面再来看看php代码,有点类似于最初的代码,但它只是输出当前的html片段。
people2.php

  <?php
 
  $cmd    = @$_REQUEST["cmd"];
  $id    = @$_REQUEST["id"];
  mysql_connect($server, $user, $password);
mysql_select_db("test");
if ($cmd == "init")
        {
        $result = mysql_query("select id, fname, lname from people");
        echo "<option value=''>(select a person)\n";
        while ($row = mysql_fetch_array($result, MYSQL_ASSOC))
                echo "<option value='$row[id]'>$row[fname] $row[lname]\n";
        }
elseif ($cmd == "info")
        {
        $id     = mysql_real_escape_string($id);
        $result = mysql_query("select * from people where id='$id'");
        $info = mysql_fetch_array($result, MYSQL_ASSOC);
        echo <<< END
<tr>
<td>$info[fname]</td>
<td>$info[lname]</td>
<td>$info[dance]</td>
<td>$info[pie]</td>
</tr>

END;
        }
?>
 

版本3:提交,JSON返回

在这个版本里,php通过查询数据库建立了一个数组然后以JSON的格式返回,jquery将这个json字符串转换为jquery object,然后将它作为参数传给回调函数。然后通过这个函数来生成html,这个跟people2差不多。只不过名不一样
people3.html

<html>
<head><title>New Form Version 1</title>
<script src="jquery.js"></script>
<script src="people3.js"></script>
</head>
<body>
People<br>
<select id="people">
</select>
<br>
<table border=1>
<thead>
<tr><td>First Name</td><td>Last Name</td><td>Dance</td><td>Pie</td></tr>
</thead>
<tbody id="info">
<tr>
<td> </td>
<td> </td>
<td> </td>
<td> </td>
</tr>
</tbody>
</table>
</body>
</html>
 

下面我们将用不同的方法来填充内容,在这个版本里,我们调用getJSON ,通过三个参数

  • The URL
  • A dictionary of name:value pairs
  • A JavaScript callback function

一个get url将会通过url和name参数生成,返回的JSON字符串,将被转换为 JavaScript object,并传递给回调函数
people3.js

 $(document).ready
        (
        function()
                {
        // Call this when the DOM is ready:
                $.getJSON("people3.php",
                        { cmd : "init" },
                        make_menu);
        // Call this when a person is selected:
                $("#people").change(function()
                        {
                        var user_id = $(":selected").val();
                        $.getJSON("people3.php",
                                { cmd : "info", id: user_id  },
                                make_info);
                        });
                }
        );
function make_menu(obj)
        {
        var str = "";
        var len = obj.length;
        str += "<option value=''>(select a person)\n";
        for (var i = 0; i < len; i++)
                {
                var user = obj[i];
                str += "<option value='" + user["id"] + "'>" +
                        user["fname"] + " " +
                        user["lname"] + "\n";
                }
        $("#people").html(str);
        }
function make_info(info)
        {
        var str = "<tr>";
    // You can get each value as info.name or info["name"].
    // Let's get the first name using the first way.
        str += "<td>" + info.fname + "</td>";
        str += "<td>" + info["lname"] + "</td>";
        str += "<td>" + info["dance"] + "</td>";
        str += "<td>" + info["pie"] + "</td>";
        str += "</tr>\n";
        $("#info").html(str);
        }
 

php代码跟上一个版本非常像,只不过这次返回的是JSON字符串
people3.php

<?php
$cmd    = @$_REQUEST["cmd"];
$id     = @$_REQUEST["id"];
mysql_connect($server, $user, $password);
mysql_select_db("test");
if ($cmd == "init")
        {
        $result = mysql_query("select id, fname, lname from people");
        $user_array = array();
        while($row = mysql_fetch_array($result, MYSQL_ASSOC))
                $user_array[] = $row;
        echo json_encode($user_array);
        }
elseif ($cmd == "info")
        {
        $id = mysql_real_escape_string($id);
        $result = mysql_query("select * from people where id='$id'");
        $info = mysql_fetch_array($result, MYSQL_ASSOC);
        echo json_encode($info);
        }
?>
 

json在php5.2以后的版本中默认启用。如果是更早的版本,请参考manual section

评论

共3 条评论 to “[译]将基于web的php程序迁移到ajax”

  1. phppie on 2007-09-26 11:00 am
    Gravatar

    你真是与时俱进啊!
    这样的文章蛮有用的 谢谢

    [回复此评论]

  2. t4o0 on 2007-09-29 8:16 pm
    Gravatar

    我晕,留言竟然说没写**...

    再来一次

    偶一直都用php+json.js实现无刷新效果,现在想结合php+json.js+Jquery不知道有没再带上json.js的必要.博主指一下,偶菜得可以`呵呵`

    [回复此评论]

  3. lzyy on 2007-09-29 10:25 pm
    Gravatar

    恩,我也觉得json挺方便的,可以参考一下我的另一篇文章:
    http://www.live-my-life-with-yuyi.com/blog/?p=140

    [回复此评论]

发表评论