MK
摩柯社区 - 一个极简的技术知识社区
AI 面试

JavaScript Web编程中的AJAX应用

2021-07-163.5k 阅读

JavaScript Web编程中的AJAX应用

AJAX基础概念

AJAX,即“Asynchronous JavaScript and XML”(异步 JavaScript 和 XML),是一种创建交互式网页应用的网页开发技术。尽管名字中有 XML,实际上数据交换常用 JSON 格式,因为 JSON 更轻量且易于在 JavaScript 中解析。AJAX 允许网页在不重新加载整个页面的情况下,与服务器进行数据交换并更新部分网页内容。这极大地提升了用户体验,因为用户无需等待整个页面刷新,就可以看到更新后的信息。

在传统的网页请求中,每当用户点击一个链接或提交一个表单,整个页面都会被发送到服务器,服务器处理请求后返回一个全新的页面。这种方式在处理频繁交互时效率低下,而且用户体验不佳。而 AJAX 通过在后台与服务器进行少量数据交换,仅更新页面的特定部分,避免了整个页面的重新加载。

AJAX核心对象:XMLHttpRequest

在 JavaScript 中,实现 AJAX 功能主要依赖于 XMLHttpRequest 对象(在现代浏览器中也支持 fetch API,但我们先从 XMLHttpRequest 讲起)。XMLHttpRequest 是 AJAX 的核心,它提供了在后台与服务器通信的能力。

创建 XMLHttpRequest 对象

在不同的浏览器环境下,创建 XMLHttpRequest 对象的方式略有不同。在现代浏览器(IE7+、Firefox、Chrome、Safari、Opera)中,可以直接使用 new XMLHttpRequest() 来创建对象。而在旧版本的 Internet Explorer(IE5 和 IE6)中,需要使用 ActiveXObject 来创建。以下是兼容不同浏览器的代码示例:

function createXHR() {
    if (typeof XMLHttpRequest != 'undefined') {
        return new XMLHttpRequest();
    } else if (typeof ActiveXObject != 'undefined') {
        if (typeof arguments.callee.activeXString != 'string') {
            var versions = ['MSXML2.XMLHTTP.6.0', 'MSXML2.XMLHTTP.3.0', 'MSXML2.XMLHTTP'];
            for (var i = 0, len = versions.length; i < len; i++) {
                try {
                    new ActiveXObject(versions[i]);
                    arguments.callee.activeXString = versions[i];
                    break;
                } catch (ex) {
                    // 忽略异常,继续尝试下一个版本
                }
            }
        }
        return new ActiveXObject(arguments.callee.activeXString);
    } else {
        throw new Error('No XHR object available');
    }
}

使用 XMLHttpRequest 对象发送请求

创建好 XMLHttpRequest 对象后,就可以使用它来发送请求。发送请求主要涉及到 open()send() 方法。

  1. open() 方法:用于初始化一个请求,它接受三个参数:请求方法(如 GETPOST 等)、请求的 URL 和一个布尔值表示是否异步处理请求(默认为 true)。例如:
var xhr = createXHR();
xhr.open('GET', 'example.php', true);
  1. send() 方法:用于实际发送请求。如果是 GET 请求,send() 方法通常不需要传入参数;如果是 POST 请求,则需要传入要发送的数据。例如:
// GET 请求
xhr.send(null);

// POST 请求
var data = 'name=John&age=30';
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
xhr.send(data);

POST 请求中,设置 Content-Type 头信息非常重要,它告诉服务器发送的数据格式。常见的 Content-Typeapplication/x-www-form-urlencoded(用于表单数据)和 application/json(用于 JSON 数据)。

处理响应

当服务器返回响应时,XMLHttpRequest 对象会触发 readystatechange 事件。我们可以通过监听这个事件来处理响应。readyState 属性表示请求的状态,它有以下几种取值:

  • 0:未初始化。尚未调用 open() 方法。
  • 1:启动。已调用 open() 方法,但尚未调用 send() 方法。
  • 2:发送。已调用 send() 方法,并且已经接收到部分响应头。
  • 3:接收。正在接收响应体。
  • 4:完成。响应已完全接收。

readyState 变为 4status200(表示成功)时,我们可以获取服务器返回的数据。以下是完整的示例代码:

var xhr = createXHR();
xhr.open('GET', 'example.php', true);
xhr.onreadystatechange = function() {
    if (xhr.readyState === 4) {
        if (xhr.status === 200) {
            var responseText = xhr.responseText;
            console.log('服务器响应:', responseText);
        } else {
            console.log('请求失败,状态码:', xhr.status);
        }
    }
};
xhr.send(null);

在上述代码中,当 readyState4status200 时,通过 xhr.responseText 获取服务器返回的文本数据。如果需要获取 XML 格式的数据,可以使用 xhr.responseXML 属性,它会返回一个 Document 对象,可以通过 DOM 操作来处理 XML 数据。

AJAX请求方法:GET和POST

GET请求

GET 请求是最常见的 AJAX 请求方法。它将参数附加在 URL 后面,以 ? 分隔,参数之间用 & 连接。例如:http://example.com/api?name=John&age=30。 优点:

  • 简单:易于理解和实现。
  • 缓存:可以被浏览器缓存,适合请求不需要经常更新的数据,例如静态资源文件。

缺点:

  • 长度限制:URL 长度有限制,不同浏览器限制不同,但一般都比较短,所以不能传输大量数据。
  • 安全性:参数暴露在 URL 中,不太适合传输敏感信息,如密码等。

以下是一个简单的 GET 请求示例:

function getRequest() {
    var xhr = createXHR();
    var url = 'example.php?param1=value1&param2=value2';
    xhr.open('GET', url, true);
    xhr.onreadystatechange = function() {
        if (xhr.readyState === 4 && xhr.status === 200) {
            console.log(xhr.responseText);
        }
    };
    xhr.send(null);
}

POST请求

POST 请求将数据放在请求体中发送,而不是像 GET 那样附加在 URL 后面。这使得它更适合传输大量数据和敏感信息。 优点:

  • 无长度限制:可以传输大量数据,因为数据不依赖于 URL 的长度。
  • 安全性:数据在请求体中,相对 GET 请求更安全,适合传输敏感信息。

缺点:

  • 复杂性:相比 GET 请求,需要设置 Content-Type 等请求头,稍微复杂一些。
  • 缓存:一般不会被浏览器缓存,每次请求都会向服务器发送数据。

以下是一个简单的 POST 请求示例,发送 JSON 格式的数据:

function postRequest() {
    var xhr = createXHR();
    var url = 'example.php';
    var data = {name: 'John', age: 30};
    xhr.open('POST', url, true);
    xhr.setRequestHeader('Content-Type', 'application/json');
    xhr.onreadystatechange = function() {
        if (xhr.readyState === 4 && xhr.status === 200) {
            console.log(xhr.responseText);
        }
    };
    xhr.send(JSON.stringify(data));
}

在上述代码中,先将 JavaScript 对象转换为 JSON 字符串,然后设置 Content-Typeapplication/json,最后通过 send() 方法发送数据。

AJAX在实际项目中的应用场景

表单提交

在传统的表单提交中,整个页面会刷新,用户体验较差。使用 AJAX 可以在不刷新页面的情况下提交表单数据,并获取服务器的响应。例如,一个用户注册表单,当用户点击提交按钮时,可以通过 AJAX 将表单数据发送到服务器进行验证和保存,然后根据服务器返回的结果在页面上显示相应的提示信息。

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>AJAX 表单提交</title>
</head>

<body>
    <form id="registerForm">
        <label for="name">姓名:</label>
        <input type="text" id="name" name="name" required><br>
        <label for="email">邮箱:</label>
        <input type="email" id="email" name="email" required><br>
        <input type="submit" value="提交">
    </form>
    <div id="message"></div>

    <script>
        document.getElementById('registerForm').addEventListener('submit', function (e) {
            e.preventDefault();
            var xhr = createXHR();
            var url ='register.php';
            var formData = new FormData(this);
            xhr.open('POST', url, true);
            xhr.onreadystatechange = function () {
                if (xhr.readyState === 4 && xhr.status === 200) {
                    document.getElementById('message').innerHTML = xhr.responseText;
                }
            };
            xhr.send(formData);
        });
    </script>
</body>

</html>

在上述代码中,当表单提交时,通过 preventDefault() 方法阻止表单的默认提交行为。然后创建 XMLHttpRequest 对象,将表单数据封装到 FormData 对象中,设置请求头并发送 POST 请求。最后根据服务器返回的响应更新页面上的提示信息。

实时数据更新

许多网站需要实时显示最新的数据,例如股票价格、实时消息等。通过 AJAX,可以定时向服务器发送请求获取最新数据,并更新页面的相应部分。以下是一个简单的实时获取服务器时间的示例:

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>实时数据更新</title>
</head>

<body>
    <div id="time"></div>

    <script>
        function getServerTime() {
            var xhr = createXHR();
            var url = 'time.php';
            xhr.open('GET', url, true);
            xhr.onreadystatechange = function () {
                if (xhr.readyState === 4 && xhr.status === 200) {
                    document.getElementById('time').innerHTML = '服务器时间:'+ xhr.responseText;
                }
            };
            xhr.send(null);
        }

        setInterval(getServerTime, 5000);
    </script>
</body>

</html>

在上述代码中,通过 setInterval() 函数每隔 5 秒调用一次 getServerTime() 函数,该函数通过 AJAX 获取服务器时间并更新页面上的显示。

动态加载内容

在单页应用(SPA)中,经常需要根据用户的操作动态加载不同的内容。例如,一个博客网站,当用户点击文章标题时,通过 AJAX 加载文章的详细内容,而不需要重新加载整个页面。

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>动态加载内容</title>
</head>

<body>
    <ul id="articleList">
        <li data-id="1">文章一</li>
        <li data-id="2">文章二</li>
    </ul>
    <div id="articleContent"></div>

    <script>
        document.getElementById('articleList').addEventListener('click', function (e) {
            if (e.target.tagName === 'LI') {
                var articleId = e.target.dataset.id;
                var xhr = createXHR();
                var url = 'article.php?id=' + articleId;
                xhr.open('GET', url, true);
                xhr.onreadystatechange = function () {
                    if (xhr.readyState === 4 && xhr.status === 200) {
                        document.getElementById('articleContent').innerHTML = xhr.responseText;
                    }
                };
                xhr.send(null);
            }
        });
    </script>
</body>

</html>

在上述代码中,当用户点击文章列表中的某个文章标题时,获取文章的 ID,通过 AJAX 请求服务器获取文章的详细内容,并在页面的指定区域显示。

AJAX的错误处理

在 AJAX 请求过程中,可能会出现各种错误,如网络故障、服务器响应错误等。正确处理这些错误对于提供良好的用户体验非常重要。

网络错误

网络错误通常发生在请求无法发送或无法接收响应时。在 XMLHttpRequest 对象中,可以通过监听 error 事件来捕获网络错误。例如:

var xhr = createXHR();
xhr.open('GET', 'example.php', true);
xhr.onerror = function() {
    console.log('网络错误,请求无法完成');
};
xhr.onreadystatechange = function() {
    if (xhr.readyState === 4) {
        if (xhr.status === 200) {
            console.log(xhr.responseText);
        } else {
            console.log('请求失败,状态码:', xhr.status);
        }
    }
};
xhr.send(null);

在上述代码中,当发生网络错误时,error 事件会被触发,在事件处理函数中可以记录错误信息或向用户显示友好的提示。

服务器响应错误

除了网络错误,服务器返回的状态码也可能表示请求失败。常见的非 200 状态码有 404(未找到资源)、500(服务器内部错误)等。在 readystatechange 事件处理函数中,可以根据 status 属性来处理服务器响应错误。例如:

xhr.onreadystatechange = function() {
    if (xhr.readyState === 4) {
        if (xhr.status === 200) {
            console.log(xhr.responseText);
        } else if (xhr.status === 404) {
            console.log('请求的资源未找到');
        } else if (xhr.status === 500) {
            console.log('服务器内部错误');
        } else {
            console.log('请求失败,状态码:', xhr.status);
        }
    }
};

通过对不同状态码的处理,可以向用户提供更具体的错误信息,帮助用户理解请求失败的原因。

跨域问题与解决方案

跨域的概念

当一个网页从一个域名的网页去请求另一个域名的资源时,如从 http://a.com 去请求 http://b.com 的资源,若这两个域名(包括子域名、端口)不一致,就会出现跨域问题。这是浏览器出于安全考虑实施的同源策略限制。同源策略要求协议、域名、端口都相同,否则就是跨域。

跨域解决方案

  1. JSONP(JSON with Padding):JSONP 是一种简单的跨域解决方案,它利用 <script> 标签没有跨域限制的特点。客户端创建一个 <script> 标签,将请求的 URL 作为 src 属性的值,服务器返回一段 JavaScript 代码,这段代码会调用客户端定义的回调函数,并将数据作为参数传递给回调函数。 以下是一个简单的 JSONP 示例:
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>JSONP 示例</title>
</head>

<body>
    <script>
        function handleResponse(data) {
            console.log('接收到的数据:', data);
        }
    </script>
    <script src="http://example.com/api?callback=handleResponse"></script>
</body>

</html>

在上述代码中,服务器返回的内容应该是类似 handleResponse({message: 'Hello, JSONP!'}) 的 JavaScript 代码。 优点:

  • 兼容性好:支持古老的浏览器。
  • 简单易用:实现相对简单。

缺点:

  • 只支持 GET 请求:因为是通过 <script> 标签加载,所以只能发送 GET 请求。
  • 安全性问题:如果服务器被攻击,恶意脚本可能会被注入。
  1. CORS(Cross - Origin Resource Sharing):CORS 是一种现代的跨域解决方案,它通过在服务器端设置响应头来允许跨域请求。服务器需要设置 Access - Control - Allow - Origin 头信息,指定允许的源。例如,设置 Access - Control - Allow - Origin: http://a.com 表示允许 http://a.com 发起的跨域请求。如果要允许所有源,可以设置为 *
// Node.js 示例,使用 Express 框架设置 CORS 头
const express = require('express');
const app = express();

app.use((req, res, next) => {
    res.setHeader('Access - Control - Allow - Origin', '*');
    res.setHeader('Access - Control - Allow - Methods', 'GET, POST, PUT, DELETE');
    res.setHeader('Access - Control - Allow - Headers', 'Content - Type');
    next();
});

// 其他路由和处理逻辑

优点:

  • 支持所有请求方法:包括 GET、POST、PUT、DELETE 等。
  • 安全性高:相比 JSONP 更安全,因为是通过服务器设置响应头来控制跨域。

缺点:

  • 浏览器兼容性:一些古老的浏览器可能不支持。

现代 AJAX 替代方案:fetch API

fetch API 简介

fetch API 是现代 JavaScript 中用于进行网络请求的新接口,它提供了一种更简洁、更强大的方式来处理 AJAX 请求,并且基于 Promise,使得异步操作更加易于管理。与 XMLHttpRequest 相比,fetch API 的语法更加直观和简洁。

使用 fetch API 发送请求

  1. GET 请求
fetch('example.php')
   .then(response => response.text())
   .then(data => console.log(data))
   .catch(error => console.error('请求错误:', error));

在上述代码中,fetch() 函数接受一个 URL 作为参数,返回一个 Promisethen() 方法用于处理响应,第一个 then() 中的 response.text() 将响应解析为文本格式,第二个 then() 中获取解析后的数据并进行处理。catch() 方法用于捕获请求过程中的错误。

  1. POST 请求
var data = {name: 'John', age: 30};
fetch('example.php', {
    method: 'POST',
    headers: {
        'Content - Type': 'application/json'
    },
    body: JSON.stringify(data)
})
   .then(response => response.text())
   .then(data => console.log(data))
   .catch(error => console.error('请求错误:', error));

在 POST 请求中,通过 fetch() 函数的第二个参数配置请求方法、请求头和请求体。

处理响应

fetch API 的响应处理与 XMLHttpRequest 有所不同。fetch 返回的 Promise 会在请求完成(包括成功和失败)时被解决,而不是像 XMLHttpRequest 那样需要通过 readystatechange 事件来监听状态变化。fetch 响应对象提供了多种方法来解析不同格式的数据,如 response.text()(解析为文本)、response.json()(解析为 JSON)、response.blob()(解析为二进制大对象)等。

错误处理

fetch API 的错误处理通过 catch() 方法来实现。与 XMLHttpRequest 不同的是,fetch 只会在网络错误或请求被阻止时抛出错误,而不会像 XMLHttpRequest 那样根据服务器返回的状态码(如 404、500 等)抛出错误。因此,在处理响应时,需要手动检查响应状态码。例如:

fetch('example.php')
   .then(response => {
        if (!response.ok) {
            throw new Error('请求失败,状态码:'+ response.status);
        }
        return response.text();
    })
   .then(data => console.log(data))
   .catch(error => console.error('请求错误:', error));

在上述代码中,通过 response.ok 属性检查响应状态码是否为 200 - 299 之间的成功状态码,如果不是,则手动抛出错误。

总结

AJAX 在 JavaScript Web 编程中扮演着至关重要的角色,它极大地提升了网页的交互性和用户体验。从传统的 XMLHttpRequest 对象到现代的 fetch API,开发者有了更多的选择来实现高效的网络请求。无论是表单提交、实时数据更新还是动态加载内容,AJAX 都能发挥重要作用。同时,了解跨域问题及其解决方案,以及正确处理错误,对于构建健壮的 Web 应用至关重要。随着 Web 技术的不断发展,相信 AJAX 相关的技术也会不断演进,为开发者提供更强大的工具和更好的开发体验。