Ajax 全接触

"Ajax 学习笔记"

Posted by 刘一凡 on January 22, 2016

“AJAX 是一种在无需重新加载整个网页的情况下,能够更新部分网页的技术。 ”

AJAX 是什么 ?

  • AJAX = 异步 JavaScript 和 XML
  • AJAX 是一种用于创建快速动态网页的技术
  • 通过在后台与服务器进行少量数据交换,AJAX 可以使网页实现异步更新。这意味着可以在不重新加载整个网页的情况下,对网页的某部分进行更新。

XMLHttpRequest 对象

XMLHttpRequest 是 AJAX 的基础,用于在后台和服务器交换数据,这样可以在不刷新页面的情况下,加载页面。

创建 XMLHttpRequest 对象

为了应对所有的现代浏览器,包括 IE5 和 IE6,需要检查浏览器是否支持 XMLHttpRequest 对象,如果支持,则创建 XMLHttpRequest 对象,如果不支持,则创建 ActiveXObject:

var request ;
if(window.XMLHttpRequest){       
    // IE7+, Firefox, Chrome, Opera, Safari
    request = new XMLHttpRequest();
} else {
    // IE5, IE6
    request = new ActiveXObject("Microsoft.XMLHTTP");
}

HTTP 请求

HTTP 是计算机通过网络进行通信的规则,主要是使客户端能够向服务器发起一种HTTP请求。

一个完整的 HTTP 请求过程,通常有 7 个步骤:

  1. 建立 TCP 连接
  2. Web 浏览器向 Web 服务器发送请求命令
  3. Web 浏览器发送请求头部信息
  4. Web 服务器应答
  5. Web 服务器发送应答头信息
  6. Web 服务器向浏览器发送数据
  7. Web 服务器关闭 TCP 连接

一个HTTP请求一般由四部分组成:

  1. HTTP请求的方法或动作,比如 GET 或者 POST
  2. 正在请求的 URL
  3. 请求头,包含一些客户端的环境信息,身份验证
  4. 请求体,请求正文,包含客户提交的查询字符串信息,表单信息

GET

  • 一般用于信息获取
  • 使用 URL 传递参数
  • 对所发送的信息数量也有限,一般是2000个字符

POST

  • 一般用于修改服务器上的资源
  • 对所发送信息的数量无限制

XMLHttpReques 发送请求

open(method, url, async)
method 发送的方法 : GET、POST
url 请求的地址
async 请求同步/异步, 默认为 true

send(string)

例如:

request.open("GET", "get.php", true)
request.send();

// POST 请求
request.open("POST", "create.php", true)
// 设置 HTTP 的头信息
request.setRequestHeader("Content-type", "application/x-www-form-urlencoded")
request.send("name=王二狗&sex=男")

XMLHttpRequest 取得响应

readyState 属性

0 : 请求未初始化, open 还没有调用
1 : 服务器连接已建立, open 已经调用
2 : 请求已接收, 也就是接收到头信息
3 : 请求处理中, 也就是接收到响应主体
4 : 请求完成, 且响应已就绪, 响应完成

// 创建 XHR 对象
var request = new XMLHttpRequest();

// 调用 open 方法
request.open("GET", "get.php", true);

// send 一些数据
request.send();

// 监听这个过程,判断响应是否完成
request.onreadystatechange = function(){
    if(request.readyState === 4 && request.status === 200){
        // do something
    }
}

AJAX 简单的例子

纯 HTML 页面,用来实现员工查询和新建页面
php 页面,用来实现查询员工和新建员工的后台接口

服务器端的代码

<?php
//设置页面内容是html编码格式是utf-8
header("Content-Type: text/plain;charset=utf-8"); 
//header("Content-Type: application/json;charset=utf-8"); 
// header("Content-Type: text/xml;charset=utf-8"); 
//header("Content-Type: text/html;charset=utf-8"); 
//header("Content-Type: application/javascript;charset=utf-8"); 

//定义一个多维数组,包含员工的信息,每条员工信息为一个数组
$staff = array
    (
        array("name" => "洪七", "number" => "101", "sex" => "男", "job" => "总经理"),
        array("name" => "郭靖", "number" => "102", "sex" => "男", "job" => "开发工程师"),
        array("name" => "黄蓉", "number" => "103", "sex" => "女", "job" => "产品经理")
    );

//判断如果是get请求,则进行搜索;如果是POST请求,则进行新建
//$_SERVER是一个超全局变量,在一个脚本的全部作用域中都可用,不用使用global关键字
//$_SERVER["REQUEST_METHOD"]返回访问页面使用的请求方法
if ($_SERVER["REQUEST_METHOD"] == "GET") {
    search();
} elseif ($_SERVER["REQUEST_METHOD"] == "POST"){
    create();
}

//通过员工编号搜索员工
function search(){
    //检查是否有员工编号的参数
    //isset检测变量是否设置;empty判断值为否为空
    //超全局变量 $_GET 和 $_POST 用于收集表单数据
    if (!isset($_GET["number"]) || empty($_GET["number"])) {
        echo "参数错误";
        return;
    }
    //函数之外声明的变量拥有 Global 作用域,只能在函数以外进行访问。
    //global 关键词用于访问函数内的全局变量
    global $staff;
    //获取number参数
    $number = $_GET["number"];
    $result = "没有找到员工。";
    
    //遍历$staff多维数组,查找key值为number的员工是否存在,如果存在,则修改返回结果
    foreach ($staff as $value) {
        if ($value["number"] == $number) {
            $result = "找到员工:员工编号:" . $value["number"] . ",员工姓名:" . $value["name"] . 
                              ",员工性别:" . $value["sex"] . ",员工职位:" . $value["job"];
            break;
        }
    }
    echo $result;
}

//创建员工
function create(){
    //判断信息是否填写完全
    if (!isset($_POST["name"]) || empty($_POST["name"])
        || !isset($_POST["number"]) || empty($_POST["number"])
        || !isset($_POST["sex"]) || empty($_POST["sex"])
        || !isset($_POST["job"]) || empty($_POST["job"])) {
        echo "参数错误,员工信息填写不全";
        return;
    }
    //TODO: 获取POST表单数据并保存到数据库
    
    //提示保存成功
    echo "员工:" . $_POST["name"] . " 信息保存成功!";
}

客户端的实现

<!DOCTYPE HTML>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>Demo</title>
<style>
body, input, select, button, h1 {
    font-size: 28px;
    line-height:1.7;
}
</style>    
</head>

<body>

<h1>员工查询</h1>

<label>请输入员工编号:</label>
<input type="text" id="keyword" />
<button id="search">查询</button>
<p id="searchResult"></p>

<h1>员工新建</h1>
<label>请输入员工姓名:</label>
<input type="text" id="staffName" /><br>
<label>请输入员工编号:</label>
<input type="text" id="staffNumber" /><br>
<label>请选择员工性别:</label>
<select id="staffSex">
<option></option>
<option></option>
</select><br>
<label>请输入员工职位:</label>
<input type="text" id="staffJob" /><br>
<button id="save">保存</button>
<p id="createResult"></p>

<script>

// GET 请求

    document.getElementById("search").onclick = function(){
        // 发送 ajax 请求
        var request = new XMLHttpRequest();
        request.open("GET", "server.php?number=" + document.getElementById("keyword").value);
        request.send();
        request.onreadystatechange = function(){
            // 判断请求是否完成
            if(request.readyState === 4){
                // 判断请求是否成功
                if(request.status === 200){
                    document.getElementById("searchRequest").innerHTML = request.responseText;
                } else {
                    alert("发生错误" + request.status);
                }
            }
        }
    }

// POST 请求

    document.getElementById("save").onclick = function(){
        // 发送 ajax 查询请求并处理
        var request = new XMLHttpRequest();
        request.open("POST", "service.php");

        // 要发送的数据 data
        var data = "name=" + document.getElementById("staffName").value
                + "&number=" + document.getElementById('staffNumber').value
                + "&sex=" + document.getElementById('staffSex').value
                + "&job=" + document.getElementById('staffJob').value;
        // 这个必须设置,告诉浏览器处理的是表单
        request.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
        request.send(data);
        request.onreadystatechange = function(){
            // 判断请求是否完成
            if(request.readyState === 4){
                // 判断请求是否成功
                if(request.status === 200){
                    document.getElementById("createRequest").innerHTML = request.responseText;
                } else {
                    alert("发生错误" + request.status);
                }
            }
        }
    }
</script>
</body>
</html>

以上这个例子是用纯文本的方式来写的,不太灵活,我们现在一般使用 json 的方式来数据,json的方式比较灵活,易于解析。

JSON

JSON : JavaScript 对象表示法( JavaScript Object Notation )

基本概念

JSON 是储存和交换文本信息的语法,采用键值对的方式来组织,易于编写和阅读,也易于机器解析
JSON 是独立于语言的,不管什么语言都可以解析 json,只要按照 json 方式来解析就行

**JSON 数据的书写方式 : 名称/值对
名称/值对组合中的名称写掐面(在双引号中),值对写在后面(同样在双引号中),中间用冒号隔开

比如:

{
    "person" : [
        { "name" : "Lyf", "age" : "20" },
        { "name" : "Job", "age" : "50"}
    ]
}

JSON 解析和格式化

eval()JSON.parse()

在代码中使用 eval是很危险的,尽可能的使用 JSON.parse() 方法解析字符串,该方法还可以捕捉 json 中的语法错误。

eval() 的方式解析

var jsonData = {
    "person" : [
        { "name" : "Lyf", "age" : "20" },
        { "name" : "Job", "age" : "50"}
    ]
};
var jsonObj = eval('(' + jsonData +')');
alert(jsonObj.person[0].name);          // Lyf

JSON.parse() 的方式

var jsonData = {
    "person" : [
        { "name" : "Lyf", "age" : "20" },
        { "name" : "Job", "age" : "50"}
    ]
};
var jsonObj= JSON.parse(jsonData);
alert(jsonObj.person[0].name);          // Lyf

JSON 在线格式化和校验网站 JSONLint

用 JSON 的方式改造之前的例子

服务器端稍稍改造

<?php
//设置页面内容是html编码格式是utf-8
header("Content-Type: text/plain;charset=utf-8"); 
//header("Content-Type: application/json;charset=utf-8"); 
//header("Content-Type: text/xml;charset=utf-8"); 
//header("Content-Type: text/html;charset=utf-8"); 
//header("Content-Type: application/javascript;charset=utf-8"); 

//定义一个多维数组,包含员工的信息,每条员工信息为一个数组
$staff = array
    (
        array("name" => "洪七", "number" => "101", "sex" => "男", "job" => "总经理"),
        array("name" => "郭靖", "number" => "102", "sex" => "男", "job" => "开发工程师"),
        array("name" => "黄蓉", "number" => "103", "sex" => "女", "job" => "产品经理")
    );

//判断如果是get请求,则进行搜索;如果是POST请求,则进行新建
//$_SERVER是一个超全局变量,在一个脚本的全部作用域中都可用,不用使用global关键字
//$_SERVER["REQUEST_METHOD"]返回访问页面使用的请求方法
if ($_SERVER["REQUEST_METHOD"] == "GET") {
    search();
} elseif ($_SERVER["REQUEST_METHOD"] == "POST"){
    create();
}

//通过员工编号搜索员工
function search(){
    //检查是否有员工编号的参数
    //isset检测变量是否设置;empty判断值为否为空
    //超全局变量 $_GET 和 $_POST 用于收集表单数据
    if (!isset($_GET["number"]) || empty($_GET["number"])) {

        // 这里改成 JSON 的形式
        echo '{"success":false,"msg":"参数错误"}';
        return;
    }
    //函数之外声明的变量拥有 Global 作用域,只能在函数以外进行访问。
    //global 关键词用于访问函数内的全局变量
    global $staff;
    //获取number参数
    $number = $_GET["number"];

    // 这里改成 JSON 的形式
    $result = '{"success":false,"msg":"没有找到员工。"}';
    
    //遍历$staff多维数组,查找key值为number的员工是否存在,如果存在,则修改返回结果
    foreach ($staff as $value) {
        if ($value["number"] == $number) {

            // 这里改成 JSON 的形式
            $result = '{"success":true,"msg":"找到员工:员工编号:' . $value["number"] . 
                            ',员工姓名:' . $value["name"] . 
                            ',员工性别:' . $value["sex"] . 
                            ',员工职位:' . $value["job"] . '"}';
            break;
        }
    }
    echo $result;
}

//创建员工
function create(){
    //判断信息是否填写完全
    if (!isset($_POST["name"]) || empty($_POST["name"])
        || !isset($_POST["number"]) || empty($_POST["number"])
        || !isset($_POST["sex"]) || empty($_POST["sex"])
        || !isset($_POST["job"]) || empty($_POST["job"])) {
        echo '{"success":false,"msg":"参数错误,员工信息填写不全"}';
        return;
    }
    //TODO: 获取POST表单数据并保存到数据库
    
    //提示保存成功

    // 这里改成 JSON 的形式
    echo '{"success":true,"msg":"员工:' . $_POST["name"] . ' 信息保存成功!"}';
}

?>

服务器端的代码

<script>

// GET 请求

document.getElementById("search").onclick = function(){
    // 发送 ajax 请求
    var request = new XMLHttpRequest();
    request.open("GET", "server.php?number=" + document.getElementById("keyword").value);
    request.send();
    request.onreadystatechange = function(){
        // 判断请求是否完成
        if(request.readyState === 4){
            // 判断请求是否成功
            if(request.status === 200){
                // 解析服务器端的数据
                var data = JSON.parse(request.responseText);
                if(data.success){
                    document.getElementById("searchRequest").innerHTML = data.msg;
                } else {
                    document.getElementById("searchRequest").innerHTML = "出现错误" + data.msg;
                }
            } else {
                alert("发生错误" + request.status);
            }
        }
    }
}

// POST 请求

document.getElementById("save").onclick = function(){
    // 发送 ajax 查询请求并处理
    var request = new XMLHttpRequest();
    request.open("POST", "service.php");

    // 要发送的数据 data
    var data = "name=" + document.getElementById("staffName").value
            + "&number=" + document.getElementById('staffNumber').value
            + "&sex=" + document.getElementById('staffSex').value
            + "&job=" + document.getElementById('staffJob').value;
    // 这个必须设置,告诉浏览器处理的是表单
    request.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
    request.send(data);
    request.onreadystatechange = function(){
        // 判断请求是否完成
        if(request.readyState === 4){
            // 判断请求是否成功
            if(request.status === 200){
                 // 解析服务器端的数据
                var data = JSON.parse(request.responseText);
                if(data.success){
                    document.getElementById("createRequest").innerHTML = data.msg;
                } else {
                    document.getElementById("createRequest").innerHTML = "出现错误" + data.msg;
                }
            } else {
                alert("发生错误" + request.status);
            }
        }
    }
}
</script>

用 jQuery 实现 AJAX

jQuery.ajax([settings])

type 类型, “POST” 或者 “GET”, 默认为 “GET”
url 发送请求的地址
data 是一个对象,连同请求发送到服务器的数据
dataType 预期服务器返回的数据类型。一般为 “json”
success 是一个方法,请求成功后的回调函数。传入返回后的数据,以及包含成功代码的字符串
error 是一个方法去,请求失败时调用的函数

用 jQuery 改写刚才的例子

// 引入 jQuery
<script src="http://apps.bdimg.com/libs/jquery/1.11.1/jquery.js"></script>
<script>
$(document).ready(function(){ 
    $("#search").click(function(){ 
        $.ajax({ 
            type: "GET",    
            url: "http://127.0.0.1:8080/ajaxdemo/serverjson2.php?number=" + $("#keyword").val(),
            dataType: "json",
            success: function(data) {
                if (data.success) { 
                    $("#searchResult").html(data.msg);
                } else {
                    $("#searchResult").html("出现错误:" + data.msg);
                }  
            },
            error: function(jqXHR){     
               alert("发生错误:" + jqXHR.status);  
            },     
        });
    });
    
    $("#save").click(function(){ 
        $.ajax({ 
            type: "POST",   
            url: "serverjson.php",
            data: {
                name: $("#staffName").val(), 
                number: $("#staffNumber").val(), 
                sex: $("#staffSex").val(), 
                job: $("#staffJob").val()
            },
            dataType: "json",
            success: function(data){
                if (data.success) { 
                    $("#createResult").html(data.msg);
                } else {
                    $("#createResult").html("出现错误:" + data.msg);
                }  
            },
            error: function(jqXHR){     
               alert("发生错误:" + jqXHR.status);  
            },     
        });
    });
});
</script>

处理跨域方式

一个域名地址的组成 : http://www.abc.com:8080/script/jquery.js

http:// 协议
www 子域名
abc.com 主域名
8080 端口号
script 请求资源地址

当协议、子域名、主域名、端口号中任意一个不相同,都算不同域
不同域之间互相请求资源,都算”跨域”

跨域

JavaScript 处于安全的考虑,不允许跨域调用其他页面的对象,比如 a.com 域名下的 js 无法操作 b.com 或者是 c.a.com 域名下的对象。

处理跨域的方式 一 — 代理

通过在同域名的 web服务器端创建一个代理:
北京服务器(域名 : www.beijing.com)
上海服务器(域名 : www.shanghai.com)

比如在北京的 web服务器的后台 (www.beijing.com/proxy-shanghaiservice.php) 来调用上海的服务器 (www.shanghai.com/service.php) 的服务,然后把响应结果返回给前端,这样前端就调用北京同域名的服务和调用上海服务效果一样了

处理跨域的方式 二 — JSONP

JSONP 可用于解决主流浏览器的跨域数据访问的问题

// 在 www.aaa.com 页面中
<script>
    function jsonp(json){
        alert(json(["name"]));
    }
</script>
<script src="http://www.bbb.com/jsonp.js"></script>

// 在 www.bbb.com 页面中
jsonp({"name" : "Lyf", "age" : "20"});

jQuery 的 $.ajax() 本身就支持 JSONP 的处理方式

JSONP 只支持 GET 请求,而且 JSONP 返回的是一个数组
使用 JSONP 改造刚才的例子

<script>
$(document).ready(function(){ 
    $("#search").click(function(){ 
        $.ajax({ 
            type: "GET",    
            url: "http://127.0.0.1:8080/ajaxdemo/serverjson2.php?number=" + $("#keyword").val(),

            // 这里把 json 改为 jsonp
            dataType: "jsonp",

            // 增加一个 jsonp 的属性, 值可以随意命名
            jsonp : "callback",
            success: function(data) {
                if (data.success) { 
                    $("#searchResult").html(data.msg);
                } else {
                    $("#searchResult").html("出现错误:" + data.msg);
                }  
            },
            error: function(jqXHR){     
               alert("发生错误:" + jqXHR.status);  
            },     
        });
    });
});
</script>

后端也要将相应的 jsonp 获取

<?php
//设置页面内容是html编码格式是utf-8
//header("Content-Type: text/plain;charset=utf-8"); 
header("Content-Type: application/json;charset=utf-8"); 
//header("Content-Type: text/xml;charset=utf-8"); 
//header("Content-Type: text/html;charset=utf-8"); 
//header("Content-Type: application/javascript;charset=utf-8"); 

//定义一个多维数组,包含员工的信息,每条员工信息为一个数组
$staff = array
    (
        array("name" => "洪七", "number" => "101", "sex" => "男", "job" => "总经理"),
        array("name" => "郭靖", "number" => "102", "sex" => "男", "job" => "开发工程师"),
        array("name" => "黄蓉", "number" => "103", "sex" => "女", "job" => "产品经理")
    );

//判断如果是get请求,则进行搜索;如果是POST请求,则进行新建
//$_SERVER是一个超全局变量,在一个脚本的全部作用域中都可用,不用使用global关键字
//$_SERVER["REQUEST_METHOD"]返回访问页面使用的请求方法
if ($_SERVER["REQUEST_METHOD"] == "GET") {
    search();
} elseif ($_SERVER["REQUEST_METHOD"] == "POST"){
    create();
}

//通过员工编号搜索员工
function search(){

    // 这里获取前端的 jsonp 的值
    $jsonp = $_GET["callback"];

    //检查是否有员工编号的参数
    //isset检测变量是否设置;empty判断值为否为空
    //超全局变量 $_GET 和 $_POST 用于收集表单数据
    if (!isset($_GET["number"]) || empty($_GET["number"])) {

        // 返回 jsonp 的值
        echo $jsonp . '({"success":false,"msg":"参数错误"})';
        return;
    }
    //函数之外声明的变量拥有 Global 作用域,只能在函数以外进行访问。
    //global 关键词用于访问函数内的全局变量
    global $staff;
    //获取number参数
    $number = $_GET["number"];
    $result = $jsonp . '({"success":false,"msg":"没有找到员工。"})';
    
    //遍历$staff多维数组,查找key值为number的员工是否存在,如果存在,则修改返回结果
    foreach ($staff as $value) {
        if ($value["number"] == $number) {
            $result = $jsonp . '({"success":true,"msg":"找到员工:员工编号:' . $value["number"] .
                            ',员工姓名:' . $value["name"] . 
                            ',员工性别:' . $value["sex"] . 
                            ',员工职位:' . $value["job"] . '"})';
            break;
        }
    }
    echo $result;
}

//创建员工
function create(){
    //判断信息是否填写完全
    if (!isset($_POST["name"]) || empty($_POST["name"])
        || !isset($_POST["number"]) || empty($_POST["number"])
        || !isset($_POST["sex"]) || empty($_POST["sex"])
        || !isset($_POST["job"]) || empty($_POST["job"])) {
        echo '{"success":false,"msg":"参数错误,员工信息填写不全"}';
        return;
    }
    //TODO: 获取POST表单数据并保存到数据库
    
    //提示保存成功
    echo '{"success":true,"msg":"员工:' . $_POST["name"] . ' 信息保存成功!"}';
}

?>