【AJAX】学习笔记

00- AJAX 介绍

AJAX = Asynchronous JavaScript and XML(异步的 JavaScript 和 XML)。

AJAX 不是新的编程语言,而是一种使用现有标准的新方法。

AJAX 最大的优点是在不重新加载整个页面的情况下,可以与服务器交换数据并更新部分网页内容。

AJAX 不需要任何浏览器插件,但需要用户允许 JavaScript 在浏览器上执行。

XMLHttpRequest 只是实现 Ajax 的一种方式。

 

github学习笔记文件https://github.com/kurohoneko/web/tree/main/AJAX 

 

 

1. XML 简介

XML是可扩展标记语言,被设计用来传输数据和存储数据,XML和HTML类似,不同的是HTML中都是预定义标签,而XML中没有预定义标签,全是自定义标签。

也就是说,我的个人信息数据可以用XML表示为:

<my>
    <name>一个名字</name>
    <age>18</age>
</my>

现在已经被JSON取代了。

2. JSON 简介

JSON 是一种轻量级的数据交换格式,它基于 ECMAScript (w3c制定的js规范)的一个子集,采用完全独立于编程语言的文本格式来存储和表示数据。简洁和清晰的层次结构使得 JSON 成为理想的数据交换语言。 易于人阅读和编写,同时也易于机器解析和生成,并有效地提升网络传输效率。

用JSON表示:

{"name":"一个名字", "age":18 }

转换:

JSON.stringify(xxx);       转换为字符串,比如用于转换为 JSON格式的字符串
JSON.parse(xxx);    转换为对象,比如转换为 JSON格式的对象
 

类似于 XML 的特性:

  • JSON 是纯文本

  • JSON 具有“自我描述性”(人类可读)

  • JSON 具有层级结构(值中存在值)

  • JSON 可通过 JavaScript 进行解析

  • JSON 数据可使用 AJAX 进行传输

相比 XML 的不同之处:

  • 没有结束标签

  • 更短

  • 读写的速度更快

  • 能够使用内建的 JavaScript eval() 方法进行解析

  • 使用数组

  • 不使用保留字

3. AJAX 的优点、缺点

优点:

  • 无需刷新页面就可以与服务器通信
  • 允许根据用户事件(鼠标、键盘、表单等)来更新部分页面内容

缺点:

  • 没有浏览历史,不能回退
  • 存在跨域问题(同源)
  • SEO不友好(搜索引擎 网页爬虫爬不到)

 

01- HTTP 协议请求报文与响应报文结构

HTTP协议 超文本传输协议,协议规定了浏览器与万维网服务器之间互相通信的规则。

主要规范了两部分内容:请求、响应。

请求报文(客户端向服务器请求)

  • 行           GET/POST     /s?ie=utf-8  HTTP/1.1
  • 头           Host: google.com

                        User-Agent: chrome 83 ....等等

  • 空行
  • 体           username=xxx   如果是GET请求就为空,如果是PSOT请求可以不为空(可有可无)

响应报文(服务器发给客户端)

  • 行           HTTP/1.1   200(响应状态码,如404 500)   OK(响应状态字符串,跟状态码对应,不需要单独设置)
  • 头           Content-Type: text/html;charset=utf-8

                        Content-length: 2048  ....等等

  • 空行
  • 体           (主要的返回结果)

        <html>

           <head> </head>

           <body> <h1> 字字字</h1> </body>

        </html>

 

02- Chrome 网络控制台查看通信报文

Network,会列出网页加载过程中所有发送的请求。点击 s?ie=utf-8.... 查看内容。

Headers(头) - Preview(响应体预览) - Response(响应体)- Initiatg - Timing - Cookies

【GET请求 与 POST请求】

 

Headers 

-Response Headers (响应头)

点击“view source” 后,显示原始的响应报文,响应行 HTTP/1.1 200 OK 与响应头。响应体在 Response(响应体) 里,显示HTML代码与内联的css样式等

 

-Request Headers (请求头)

【GET请求】默认不显示请求行,点击 “view source” 后,显示请求行,GET /s?ie=utf-8&...... HTTP/1.1,也就是:方法 + URL + HTTP协议版本。

【POST请求】点击 “view source” 后,显示请求行,显示 POST /login HTTP/1.1

 

-Query String Parameters(查询字符串参数)

对 URL 当中的参数进行解析,格式化,更加准确地看到参数情况。

 

-Form Data(请求体)

比如表单,点击提交按钮后,浏览器把HTTP的报文封装好,提交给目标指定服务器的端口。login_name=XXXX&login_password=123456

 

Preview

响应体预览,对 Response(响应体) 里的代码进行预览,可以看到网页

 

03- Node.js 安装

AJAX运行应用时需要一个服务端。

https://nodejs.org/zh-cn/download/

然后cmd,node -v 查看是否安装成功与安装的版本

 

04- Express 框架

 这里主要学前端 这个express可以不懂 就把他当成接收返回数据的后端服务器就行了

https://www.expressjs.com.cn/

在主工作文件夹右键,在集成终端中打开,输入如下:

npm init --yes

然后安装Express,还是右键打开终端,输入如下:

npm i express

然后编写 js 文件:

server.js

 // 这里主要学前端 这个express可以不懂 就把他当成接收返回数据的后端服务器就行了


// 1. 引入 express
const { response } = require('express');
const express = require('express');

// 2. 创建应用对象
const app = express();

// 3. 创建路由规则
// request 是对请求报文的封装
// response 是对相应报文的封装
// /server 是如果客户端浏览器向服务端请求时,如果url的路径 请求行的第二段内容 的路径是/server的话
//         就会执行回调函数里面的代码,并且由response做出响应
app.get('/server', (request, response) => {
    //设置响应头
    response.setHeader('Access-Control-Allow-Origin','*');

    //设置响应体
    response.send('HELLO 你好 AJAX');
    
});

// 4. 监听端口启动服务
app.listen(8000, ()=>{
    console.log("服务已经启动,8000 端口正在监听中...");
} )

然后右键 server.js 文件所在文件夹,打开终端,输入 node 文件名,再打开浏览器 127.0.0.1:8000/server,点击network,刷新查看。

-这个 /server 是什么意思?我初学大概猜测的是,这个路由和交换机路由差不多,如果url是xxx.com/server,那就是去往server那个页面,就执行'/server'的回调函数,来获取相对应的数据。比如,youtube.com/me,路由规则写'/me'并在回调函数里写入我的个人信息数据,好让服务器获取。【2023年2月12日 刚学一会儿】

 

05- AJAX 请求的基本操作

POST请求和GET请求大致相同。

1. GET 请求

编写客户端HTML文件:

客户端HTML代码.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>AJAX GET 请求</title>
    <style>
        #result {
            width: 200px;
            height: 100px;
            border: solid 1px #f00;
        }
    </style>
</head>
<body>
    <button>点击发送请求</button>
    <div id="result"></div>

    <script>
        // 获取 button 元素
        const btn = document.getElementsByTagName('button')[0];
        const result = document.getElementById("result");
        // 绑定事件
        btn.onclick = function() {
            // 1. 创建对象
            const xhr = new XMLHttpRequest();
            // 2. 初始化 设置请求方法和 url 
            xhr.open('GET', 'http://127.0.0.1:8000/server');
            // 3. 发送
            xhr.send();
            // 4. 事件绑定 处理服务端返回的结果
            // on 当...的时候
            // readystate 是 xhr 对对象中的属性,表示状态 0 1 2 3 4
            // 0 未初始化  1 open方法调用完毕 2 send方法调用完毕 3 服务端返回部分结果 4 服务端返回所有结果
            // change 改变
            xhr.onreadystatechange = function() {
                // 判断(服务端返回了所有的结果)
                if(xhr.readyState === 4){
                    // 判断响应状态码 200 404 500 ....
                    // 状态码只要是2xx 都是成功 所以范围在200到299
                    if(xhr.status >= 200 && xhr.status < 300){
                        // 处理结果 行 头 空行 体
                        // 1. 响应行
                        console.log(xhr.status); //状态码 200 404等
                        console.log(xhr.statusText); //状态字符串 OK
                        console.log(xhr.getAllResponseHeaders()); //所有的响应头
                        console.log(xhr.response); //响应体
                        
                        // 把响应结果呈现在div中 
                        result.innerHTML = xhr.response;
                    }else{
                    }
                }
            }
        }
    </script>
</body>
</html>

设置请求方法和url,xhr.open('GET', 'http://127.0.0.1:8000/server?name=q&pid=12345');

然后把 server.js 文件在终端运行后,打开html文件,点击发送请求按钮,div显示了 “HELLO 你好 AJAX”。可network刷新查看发送的请求报文。

 

2. AJAX 设置请求参数

在 url 后面添加参数,以 ? 号分隔,加参数名字与参数值,如有多个值,用 & 符号分隔。

例如name是q,ipd是12345,那么就是:xhr.open('GET', 'http://127.0.0.1:8000/server?name=q&pid=12345')

 

3. POST 请求

编写HTML,把GET改为POST。不建议使用GET发送请求体body,建议POST。HTTP 协议没有为 GET 请求的 body 赋予语义,也就是即不要求也不禁止 GET 请求带 body。HTTP GET 请求可以有 body 吗?

情景:当把鼠标放到div处时,发送POST请求。

AJAX发送POST请求.html

 <!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>AJAX 发送 POST 请求</title>
    <style>
        #result{
            width: 200px;
            height: 100px;
            border: solid 1px #f00;
        }
    </style>
</head>
<body>
    <div id="result"></div>
    <script>
        // 获取元素对象
        const result = document.getElementById("result");
        // 绑定事件
        result.addEventListener("mouseover", function() {
            console.log("鼠标移动至div了");
            // 1. 创建对象
            const xhr = new XMLHttpRequest();
            // 2. 初始化 设置类型与url
            xhr.open('POST', 'http://127.0.0.1:8000/server');
            // 3. 发送
            xhr.send();
            // 4. 事件绑定
            xhr.onreadystatechange = function(){
                // 判断
                if(xhr.readyState === 4){
                    if(xhr.status >= 200 && xhr.status < 300){
                        // 处理服务端返回的结果
                        result.innerHTML = xhr.response;
                    }
                }
            }
        })
    </script>
</body>
</html>

之前的 server.js 文件只需要添加 post 路由规则:(其中post不要大写!xxx.post 是正确的)

server.js 添加psot路由规则

 // 这里主要学前端 这个express可以不懂 就把他当成接收返回数据的后端服务器就行了

// 1. 引入 express
const { response, request } = require('express');
const express = require('express');

// 2. 创建应用对象
const app = express();

// 3. 创建路由规则
// request 是对请求报文的封装
// response 是对相应报文的封装
// /server 是如果客户端浏览器向服务端请求时,如果url的路径 请求行的第二段内容 的路径是/server的话
//         就会执行回调函数里面的代码,并且由response做出响应
app.get('/server', (request, response) => {
    //设置响应头
    response.setHeader('Access-Control-Allow-Origin','*');
    //设置响应体
    response.send('HELLO 你好 AJAX');
});

// 3.2 创建路由规则 POST
app.post('/server', (request, response) => {
    //设置响应头
    response.setHeader('Access-Control-Allow-Origin','*');
    //设置响应体
    response.send('HELLO AJAX 你好 post');
});

// 4. 监听端口启动服务
app.listen(8000, ()=>{
    console.log("服务已经启动,8000 端口正在监听中...");
} )

 

4. POST 设置请求体

在发送post请求时,xhr.send();  里面设置请求体内容,如:

// 3. 发送
            xhr.send('a=100&b=200');

请求体的格式内容非常灵活,可以是 a=100&b=200&c=300 也可以是 a:100&b:200&c:300 甚至是 12312131254 。但是不可以随便写,必须让服务端可以处理可以解析,在语法上可以随意写,但实际应用中最好按照特定格式: a=100&b=200&c=300

 

5. AJAX设置请求头

设置请求头需要在 请求行 的下面,发送 的上面,也就是在open后面加一个方法调用就可以了。

这个方法是专门设置请求头的:  xxx.setRequestHeader('','');

xhr.setRequestHeader('Content-Type','application/x-www-form-urlencoded');

application/x-www-form-urlencoded是浏览器默认的编码格式

也可以自定义请求头,其他类型的请求头,比如:  xxx.setRequestHeader('name','qqqq'); 但是在浏览器查看会报错,标红,因为name不是预定义的头,而Content-Type就是预定义的,

如果想发自定义请求头,需要在路由规则里设置请求头,response.setHeader('Access-Control-Allow-Headers','*');      

所有类型的头信息都可以接收

 // 3.2 创建路由规则 POST
app.post('/server', (request, response) => {
    //设置响应头
    response.setHeader('Access-Control-Allow-Origin','*');
    //响应头 所有类型的头信息都可以接收
    response.setHeader('Access-Control-Allow-Headers','*');
    //设置响应体
    response.send('HELLO AJAX 你好 post');
});

 但,还是不行,把post改成all,不管是get、post、什么类型的请求都可以

all 可以接收任意类型的请求 不管是get 还是post 还是什么的都可以

// 创建路由规则 all 可以接收任意类型的请求
app.all('/server', (request, response) => {
    //设置响应头
    response.setHeader('Access-Control-Allow-Origin','*');
    //响应头 所有类型的头信息都可以接收
    response.setHeader('Access-Control-Allow-Headers','*');
    //设置响应体
    response.send('HELLO AJAX 你好 post');
});

 

6. option 请求 (预检测)

 

option请求就是一次效验,后面还有一次才是真正的请求。

如果校验请求没有得到对应的结果,然后前端就不能发送 post 请求了。简单来说就是没有 potion路由,app.post()不行,改为 app.all(); 就可以了,可以接收任意类型的请求。

 

7. 服务端响应 JSON 数据

这次把url改成json-server,相应的服务端也得改。

客户端html:

客户端 - JSON 响应

 <!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>JSON 响应</title>
    <style>
        #result{
            width: 200px;
            height: 100px;
            border: solid 1px #f00;
        }
    </style>
</head>
<body>
    <div id="result"></div>
    <script>
        const result = document.getElementById("result");
        //绑定键盘按下事件
        window.onkeydown = function(){
            //发送请求
            const xhr = new XMLHttpRequest();
            //设置响应体数据的类型(2.自动转换)
            xhr.responseType = 'json';
            //初始化
            xhr.open('GET','http://127.0.0.1:8000/json-server');
            //发送
            xhr.send();
            //事件绑定
            xhr.onreadystatechange = function(){
                if(xhr.readyState === 4){
                    if(xhr.status >= 200 && xhr.status < 300){
                        // 把响应体内容数据 输出给div显示
                        //result.innerHTML = xhr.response;

                        // 1. 手动对数据转换
                        //let data = JSON.parse(xhr.response);
                        //console.log(data);  //data从字符串转换为对象
                        //result.innerHTML = data.name;

                        // 2. 自动转换
                        console.log(xhr.response); //对象
                        result.innerHTML = xhr.response.name;
                    }
                }
            }
        }
    </script>
</body>
</html>

 

服务端路由规则 /json-server 。

服务端js:

服务端 js代码

 // 1. 引入 express
const { response, request } = require('express');
const express = require('express');

// 2. 创建应用对象
const app = express();

// 3.4 创建路由规则 json-server
app.all('/json-server', (request, response) => {
    //设置响应头
    response.setHeader('Access-Control-Allow-Origin','*');
    //响应头 所有类型的头信息都可以接收
    response.setHeader('Access-Control-Allow-Headers','*');
    
    //响应一个数据
    const data = {
        name: 'hhhhhhhhhh'
    };
    //对对象格式data进行转换为字符串
    let str = JSON.stringify(data);

    //设置响应体
    //response.send('HELLO AJAX 你好 json-server');
    response.send(str);
});

// 4. 监听端口启动服务
app.listen(8000, ()=>{
    console.log("服务已经启动,8000 端口正在监听中...");
} );

 

其中,服务端响应体与客户端有不同的代码,如下:

服务端响应体:  response.send('HELLO AJAX 你好 json-server'); 

客户端把响应体内容输出显示:   result.innerHTML = xhr.response; ,可以直接输出内容到div 显示

 

如果涉及多个数据,比如对象的属性名与属性值,不能直接传递过去,因为 响应体 response.send()  只能传输字符串,所以需要转换字符串转对象对象转字符串

【服务端】:

//响应一个数据
    const data = {
        name: 'hhhhhhhhhh'
    };
//对对象格式data进行转换为字符串
    let str = JSON.stringify(data);
//设置响应体
    response.send(str);

【客户端】:
// 1.方法一: 手动对数据转换            // 手动转换时,和自动转换 let str = JSON.stringify(data); 冲突
  let data = JSON.parse(xhr.response);  // xhr.response 从字符串转换为对象
  console.log(data);                    // 显示 {"name":"hhhhhhhhhh"}
  result.innerHTML = data.name;         // 显示 hhhhhhhhhh

// 2.方法二: 自动转换
  console.log(xhr.response);            // 已经被上面的 let str = JSON.stringify(data); 转换为了对象格式
  result.innerHTML = xhr.response.name; // 显示 hhhhhhhhhh

 

06- nodemon 自动重启服务工具 安装及使用

 

每当修改服务端 js 代码时,都需要手动 ctrl + c 来停止服务,再 node xxx.js 启动服务。安装 nodemon 可以自动重启服务。

打开 npm官网 https://www.npmjs.com/ ,搜索 nodemon,点击 nodemon详情页, https://www.npmjs.com/package/nodemon

右键打开终端,输入命令安装nodemon:  

npm install -g nodemon

安装完了之后,即可运行 js 代码。例如:

nodemon 文件名
// 如果提示无法运行加载,则在前面加上 npx
npx nodemon 文件名

 现在开始,每当对代码做出修改保存,它都会自动重启。

 

07- IE 浏览器缓存问题(2022-6-16 IE停用)

 每次请求时,ie浏览器会有缓存问题,假如服务端更新数据,客户端可能获取的还是老的数据(缓存)。

所以,在 url 后面添加参数 时间戳,每次请求时,参数都是不一样的(时间戳),所以ie会调用服务端,以便获取最新数据。

// 初始化 设置类型与url,在后面加上参数 时间戳
    xhr.open('POST', 'http://127.0.0.1:8000/ie?t=' + Date.now() );

 

08- 请求超时与网络异常处理

 延时请求、请求超时、网络异常。

客户端:

// 给按钮绑定事件
        btn.addEventListener('click', function(){
            //创建对象
            const xhr = new XMLHttpRequest();

            //超时设置 2s 2秒
            xhr.timeout = 2000;   //canceled 取消
            //超时回调
            xhr.ontimeout = function(){
                alert("请求超时,请稍后重试");
            }
            //网络异常回调
            xhr.onerror = function(){
                alert("您的网络似乎出了一些问题");
            }

            //初始化 请求类型与url
            xhr.open("GET","http://127.0.0.1:8000/delay");
            //发送
            xhr.send();
            //事件绑定
            xhr.onreadystatechange = function(){
                if(xhr.readyState === 4){
                    if(xhr.status >= 200 && xhr.status < 300){
                        result.innerHTML = xhr.response;
                    }
                }
            }
        });

 服务端js:

// 3.5 延时响应
app.get('/delay', (request,response) => {
    //设置响应头 允许跨域
    response.setHeader('Access-Control-Allow-Origin','*');
    //响应数据
    setTimeout(() => {
        response.send("延时响应"); 
    }, 3000)
});

【延时】 单独给服务端的响应体设置 3000ms 的延时,点击按钮请求后,等待3秒后请求数据成功。

【超时】客户端 xhr.timeout = 2000; //超时设置 2秒,只要请求等待时间过了2秒,就出现 canceled 取消请求,因为服务器是延时3秒,所以请求失败。

【超时回调】设置超时回调,当超时了,就会触发回调函数。 xhr.ontimeout = function(){} 

【异常回调】网络异常时,触发回调函数。 xhr.onerror = function(){}   

 

09- 取消请求

 1.取消请求

AJAX的取消请求,手动在请求的过程中,在结果还没回来之前,取消请求。

xhr.abort();    执行此语句,取消当前请求(在结果返回前)。
 
// abort 取消请求
        btns[1].onclick = function(){
            xhr.abort();   //取消请求
        }

 2.请求重复发送 问题

 假如用户疯狂点击请求按钮,这对服务器增加的不必要的负载和消耗。所以,点第一次请求时,接着点了第二次请求,就立马取消第一次的,如果连点七次,就取消前六次的。

服务端js:

延时 3秒 的响应

 // 延时 3秒 响应
app.get('/delay', (request,response) => {
    //设置响应头 允许跨域
    response.setHeader('Access-Control-Allow-Origin','*');
    //响应数据
    setTimeout(() => {
        response.send("延时响应"); 
    }, 3000);
})

 客户端:

        const btns = document.querySelectorAll('button');
        let xhr = null;
        // 标识变量
        let isSending = false; // 是否正在发送AJAX请求

        btns[0].onclick = function(){
            //判断标识变量
            if(isSending) xhr.abort(); // 如果正在发送,则取消该请求,创建一个新的请求↓

            xhr = new XMLHttpRequest();
            // 修改 标识变量的值
            isSending = true;  // 相当于节流阀,true,是的,正在发送AJAX请求
            xhr.open("GET",'http://127.0.0.1:8000/delay');
            xhr.send();
            xhr.readyStatechange = function(){
                // 状态码===4,代表服务器已经返回全部结果,然后修改节流阀为false状态
                if(xhr.readyState === 4){
                    // 修改标识变量
                    isSending = false;
                }
            }
        }

 

 意思就是,先声明一个标识变量 isSending = false 表明是否正在发送请求,然后判断这个标识变量,如果 if(false) 则不执行 abort 取消,然后修改 isSending = true (此时正在发送请求),接着判断 xhr.readyStatechang 状态码 全等于 4,如果成立,也就是服务器返回了全部结果,最后修改标识变量 isSending = false,表明此时没有正在发送请求。

 如果正在发送,则isSending为true,直接 if(true) xhr.abort() 取消该请求,创建一个新的请求。

 

10- jQuery发送AJAX请求

如果没有jQuery本地文件,可以下载一个或引用公共cdn 点击https://www.bootcdn.cn/jquery/

三个方法:get、post、ajax。

$.get('http://127.0.0.1:8000/server', {a:100,b:200}, function(my_data) {}, 'json'  );       

每种方法都是接收四个参数,第一个参数是给谁发,第二个是发什么参数,第三个是回调函数,第四个是设置响应体什么类型的。也可以只接收三个参数。

GET请求 $.get() :

$('button').eq(0).click(function(){
    $.get('http://127.0.0.1:8000/jquery-server', {a:100,b:200}, function(data){
        console.log(data);
    });           // 显示的是字符串类型的
});
// jQuery发送AJAX请求
app.all('/jquery-server', (request,response) => {
    response.setHeader('Access-Control-Allow-Origin','*');
    response.send("Hello jQuery AJAX");
});

function(data){}  回调函数里的参数 data 是响应体,可以做一些操作。

 

POST请求 $.post():

$('button').eq(1).click(function(){
    $.post('http://127.0.0.1:8000/jquery-server', {a:100,b:200}, function(data){
        console.log(data);
    }, 'json');   // 设置响应体类型 json 。结果显示的就是对象
});
// jQuery发送AJAX请求
app.all('/jquery-server', (request,response) => {
    response.setHeader('Access-Control-Allow-Origin','*');
    const data = {name:'啊啊啊啊'};
    response.send(JSON.stringify(data));   // 把对象 转换为 字符串型
});

服务端先把对象转换为字符串型,然后客户端接收到的是字符串类型的。设置了响应体类型 'json' ,然后客户端接收到的就是对象类型的了。

简单来说,不加 json 接收的是字符串,加了 json 接收的就是对象。      

 get 和 post 请求的区别,在浏览器显示get是字符串,post是表单,post的请求参数不在url后面,在payload的Form Data。

 

AJAX请求 $.ajax():

 自定义程度比较强的可以使用 AJAX 请求,简单简约的可以使用 GET POST。

1.基本的

客户端:

$('button').eq(2).click(function(){
    //$.ajax({ // 参数 是对象形式的 });
    $.ajax({
        // url
        url: 'http://127.0.0.1:8000/jquery-server',
        // 参数
        data: {a:100,b:200,c:'三百多'},
        // 请求类型
        type: 'GET',
        // 成功的回调
        success: function(data){
            console.log(data);
        }
    });
});

服务端:

// jQuery发送AJAX请求
app.all('/jquery-server', (request,response) => {
    response.setHeader('Access-Control-Allow-Origin','*');
    response.send("Hello jQuery AJAX");
});

 

2. 更多的自定义

客户端:

$('button').eq(2).click(function(){
    $.ajax({
        // url
        url: 'http://127.0.0.1:8000/jquery-server',
        // 参数
        data: {a:100,b:200,c:'三百多'},
        // 请求类型
        type: 'GET',
        // 响应体结果类型
        dataType: 'json',
        // 成功的回调
        success: function(data){
            console.log(data);
        },
        // 超时时间
        timeout: 2000,
        // 失败的回调
        error: function() {
            alert("出错啦!");
        },
        // 头信息
        headers: {
            aaa: 300,
            aab: 333333
        }
    });
});

服务端:

// jQuery发送AJAX请求
app.all('/jquery-server', (request,response) => {
    response.setHeader('Access-Control-Allow-Origin','*');
    response.setHeader('Access-Control-Allow-Headers','*');
    const data = {name:'啊啊啊啊'};
    setTimeout(() => {
         response.send(JSON.stringify(data));
    }, 3000);
});

 

post请求是增加的功能 第二个参数应该是添加的新数据 第三个参数是设置请求报文

11- Axios 发送AJAX请求

axios目前是前端最热门的AJAX工具,vue 与 react 推荐的AJAX请求的工具包。

axios github: https://github.com/axios/axios

或者引用国内cdn:https://www.bootcdn.cn/axios/

 

1. get请求 axios.get(url [,config] )

语法:axios.get(url [,config] ),url 与 其他配置
数据返回和处理与jQuery不一样,jQuery返回数据用回调函数,而axios的返回数据的语法为:axios.get( url... ).then( value... );            
//GET请求
    axios.get('http://127.0.0.1:8000/axios-server', {
    //url参数
    params: {
        id:100,
        vip:7
    },
    //config 请求头信息
    headers: {
        name:'xxxx',
        n_age:20
        }
    }).then(value => {
        console.log(value);   //  axios与jQuery不一样,jQuery返回数据用回调函数。
    });
 服务端js代码
app.all('/axios-server', (request,response) => {
    response.setHeader('Access-Control-Allow-Origin','*');
    response.setHeader('Access-Control-Allow-Headers','*');
    response.send("Hello AJAX Axios");
});
 

2. psot请求 axios.post(url [, data [,config]] )

语法:axios.post(url [,data [,config]] ),url 、 请求体、其他配置。
 
baseURL 是对url路径做一个简化。
不管get还是post,配置 baseURL,设置默认的url可以更加简洁,也方便了后续修改url代码。
配置 baseURL : axios.defaults.baseURL = 'http://127.0.0.1:8000'; 
 
//配置 baseURL
axios.defaults.baseURL = 'http://127.0.0.1:8000';

axios.post('/axios-server', {
    // data 请求体
    username: 'admin XXXX',
    userpasw: '1234567890'
}, {
    // config 其他参数
    //url
    params: {
        id:200,
        vip:999
    },
    //请求头参数
    headers: {
        height: 290,
        weight: 290
    }
})
如果第二个参数(data)位置是一个对象的话,它就直接作为请求体了,所以,其他参数要写在请求体下面。 
请求体发的是一个 json 格式的字符串。
 

3. axios 的通用方式请求 axios()

语法格式: axios(url [,config])          
这个方式很好,很清晰,和协议层面贴合的非常近,和手写报文差不多,但是,这个语法还简单。
 
btns[2].onclick = function(){
    axios({
        //请求方法
        method: 'POST',
        //url
        url: '/axios-server',
        //url参数
        params: {
            pid:1234,
            pname:12121212
        },
        //头信息
        headers: {
            aaa:100,
            abb:200
        },
        //请求体参数
        data: {
            username: 'admin',
            userpasd: 'admin'
        }
    }).then(response => {                            // 返回数据 和 处理
        console.log(response);
        //响应状态码
        console.log(response.status);
        //响应状态字符串
        console.log(response.statusText);
        //响应头信息
        console.log(response.headers);
        //响应体
        console.log(response.data);
    })
};
预览:
 

12- fetch 函数 发送AJAX请求

fetch函数是属于全局对象的,可以直接调用,是原生js的。 MDN社区 文档介绍 fetch()
语法: fetch(url [,config])   
它返回一个 promise,这个 promise 会在请求响应后被 resolve,并传回 Response 对象。
需要调用两次then()。
客户端:
// fetch(url [,config] )
btn.onclick = function(){
    fetch('http://127.0.0.1:8000/fetch-server', {
        //请求方法
        method: 'POST',
        //请求头
        headers: {
            name: 'HHHHHHHHHHHHHH'
        },
        //请求体
        body: 'username=admin&password=admin'
    }).then(response => {
        //返回结果
        console.log(response);
        //获取响应体body,得使用返回结果里面的方法 __proto__ 里的 text() 或 json(),来获取请求体body的数据!
        //用return返回
        return response.text();
        //return response.json();  //如果服务端返回结果是 json,那就把方法换成 .json(),会解析成js对象。
    }).then(response => {
        console.log(response);
    })
}

服务端:

服务端 js代码

 // fetch() 发送AJAX请求
app.all('/fetch-server', (request,response) => {
    response.setHeader('Access-Control-Allow-Origin','*');
    response.setHeader('Access-Control-Allow-Headers','*');
    response.send("Hello AJAX fetch()");
});

预览:

 

URL也可以修改,参数可以直接在后面添加

fetch('http://127.0.0.1:8000/fetch-server?userid=333', {....,参数是 userid=333

 

如果服务端返回的数据是 json 对象,只需要把 return response.text();text() 方法换成 json() 方法:response.json(); 

 

13- 同源策略

字面意思,其实说白了,就是来自同一个服务。同一个树根

比如:这个页面是从127.0.0.1:9000来的,等会请求的数据也是127.0.0.1:9000里的,那么,这两个资源是同源的。这个请求也是满足同源策略的。

 

服务端 js代码

const express = require('express');

const app = express();

app.get('/home', (request,response) => {
    //响应一个页面
    response.sendFile(__dirname + '/index.html');
    //相当于 127.0.0.1:9000 这个本地文件所属目录
    // __dirname :用来动态获取当前文件模块所属目录的绝对路径
    // __filename:用来动态获取当前文件的绝对路径
});

app.get('/data', (request,response) => {
    //响应用户数据
    response.send("这个是用户数据");
});

app.listen(9000, () => {
    console.log("服务已启动...9000");
})

客户端:

index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>同源策略 AJAX</title>
</head>
<body>
    <h1>AJAX 同源策略</h1>
    <h1>其实说白了,就是来自同一个服务。</h1>
    <h2>这个页面是从127.0.0.1:9000来的,等会请求的数据也是127.0.0.1:9000里的</h2>
    <h2>所以说,这两个资源是同源的。这个请求也是满足同源策略的</h2>
    <button>点击获取</button>
    <script>
        const btn = document.querySelector('button');
        btn.onclick = function(){
            const xhr = new XMLHttpRequest();
            //这里因为是满足同源策略,所以 url 可以简写
            xhr.open("GET",'/data');
            //发送
            xhr.send();
            //绑定事件
            xhr.onreadystatechange = function(){
                if(xhr.readyState === 4){
                    if(xhr.status >= 200 && xhr.status < 300){
                        console.log(xhr.response);
                    }
                }
            }
        }
    </script>
</body>
</html>

然后在浏览输入 http://127.0.0.1:9000/home,会直接打开这个 index.html 文件,如图:

点击获取:

 

14- jsonp 跨域解决方案 实现原理

jsonp是一个非官方的跨域解决方案,只支持get请求。
如何工作?网页有一些标签具有跨域能力,比如img link iframe script,JSONP就是利用 script 标签的跨域能力来发送请求的。
如何使用?1.动态创建一个script标签。2.设置script的src,设置回调函数。

举例:之前讲 jQuery发送AJAX请求 时,我引用了网络上的jQuery文件:

<script crossorigin="anonymous" src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.3/jquery.js"></script>   

现在来说,我本地网页的url 与 引用的网页url 完全不一样,协议不一样,域名不一样,端口不一样。所以说这是一个跨域请求,但是!这个跨域请求丝毫没有影响到代码的运行!还可以正常使用里面的方法等等。所以说,script标签本身就具备跨域特性。

之前在服务端里写的 response.setHeader('Access-Control-Allow-Headers','*');,就是允许跨域的。     

 

原理演示:

index.html:

!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>jsonp的实现原理</title>
</head>
<body>
    <script src="./app.js"></script>
</body>
</html>

app.js:

const data = {
    name: '一段文字'
};

console.log(data.name);

首先index.html引入外部资源 js 文件(本地的),右键以 open with Live Server 来打开 index.html(http 协议) 后,发现可以输出js里的结果。

现在查看引用的js文件的url,变成了http协议的:http://127.0.0.1:5501/1st/AJAX/8-jsonp-AJAX/app.js

——————————————————————

然后把 index 文件里的 script 的 src 换成完整的书写方式:http://127.0.0.1:5501/1st/AJAX/8-jsonp-AJAX/app.js

然后右键 Open In Default Browser,以本地协议的方式打开 index.html网页,file:///E:/Code/A2/1st/AJAX/8-jsonp-AJAX/jsonp%E7%9A%84%E5%AE%9E%E7%8E%B0%E5%8E%9F%E7%90%86.html

就是以本地协议file方式打开网页,里面引用http协议的资源,发现还是可以正常运行,没有跨域问题。

 

在此基础上,修改 js 文件代码:

// 1 数据
const data = {
    name: '你的名字'
};

//console.log(data.name);

// 2 函数的调用
My_name_is(data);

html文件代码:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>jsonp的实现原理</title>
    <style>
        #result{
            height: 200px;
            width: 300px;
            font-size: 50px;
            font-weight: bold;
            color: rgb(241, 73, 43);
            border: 5px solid aqua;
        }
    </style>
</head>
<body>
    <div id="result"></div>
    <script>
        //处理数据 3.函数
        function My_name_is(data){
            //获取 result 元素
            const result = document.getElementById("result");
            result.innerHTML = data.name;
        }
        //小贴士:预编译变量声明和函数声明会提到最前面
    </script>

    <!-- <script src="./app.js"></script> -->
    <script src="http://127.0.0.1:5501/1st/AJAX/8-jsonp-AJAX/app.js"></script>
</body>
</html>

 可以正常运行,可以看到这个 js文件返回的结果是一个数据和一个函数的调用:

这里注意,这个函数前端必须要提前声明,要不然回来之后没有这个函数就会报错,没函数就不能调用。

 

<script></script>标签发送请求,返回的结果,必须可以让 js 引擎做解析,如果是字符串,那就解析不了了,举例:

前端:

<script src="http://127.0.0.1:8000/jsonp-server"></script>

后端:

// jsonp 服务
app.all('/jsonp-server', (request,response) => {
    response.send('hello jsonp-server');
});

 

 运行后,Console 栏报错提示:Uncaught SyntaxError: Unexpected identifier (未捕获的语法错误:意外的标识符)。也就是解析不了,因为这是非 js代码,返回 js代码就可以解析了。

 可以看到 Network 栏的 Response 有返回的数据:hello jsonp-server。

返回 js 语句代码:

app.all('/jsonp-server', (request,response) => {
    //response.send('hello jsonp-server'); // 返回非 js代码,script标签就无法解析
    response.send(' console.log("hello jsonp-server"); ');
});

运行后,可以看到 Console 栏显示 hello jsonp-server,无报错。Response 栏显示: console.log("helo jsonp-server"); 

所以说,<script>标签请求获取后,要的是函数调用的内容,也就是一段 js代码,js语句。 这样的话,前端的浏览器才可以解析并执行里面的内容。

——————

 

修改后端服务端代码: 

 

app.all('/jsonp-server', (request,response) => {
    //response.send('hello jsonp-server'); // 返回非 js代码,script标签就无法解析
    //response.send(' console.log("helo jsonp-server"); ');

    //1.数据
    const data = {
        name: '玻璃柠檬'
    };
    //将数据转化为 字符串
    let str = JSON.stringify(data);
    //返回结果  end()不会返回特殊响应头
    response.end(`My_name_is(${str})`)

});

 

打开页面,页面接收到了数据并执行了js代码:My_name_is(${str})  ,返回结果:My_name_is({"name":"玻璃柠檬"})

    前端网页预览:

 

 

返回的结果的形式,是一个函数的调用,而函数的实参,就是要返回客户端结果的数据。

这个函数必须要在前端声明,要不然回来之后没有这个函数,就会报错。

 

小贴士: ` ` 是 模板字符串,而里面的 ${ } 是与字符串区分开来的,里面是 变量名 或 JavaScript表达式。

`${}` 更多详情 ES6

 

jsonp 实践 - 返回 js 代码

在输入框input输入用户名,输入完后,失去焦点(点击别处),然后向服务端发送请求,对用户名进行是否存在的检测,然后返回结果(用户名已经存在),再让input输入框的颜色变红,

 

前端 客户端:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>原生jsonp实践</title>
</head>
<body>
    用户名:<input type="text" id="username">
    <p></p>

    <script>
        //获取 input 元素
        const input = document.querySelector('input');
        const p = document.querySelector('p');

        //声明 Check_username 函数
        function Check_username(data){
            //输入用户名的边框变红色
            input.style.border = "solid 1px #f00";
            //修改 p 标签的提示文本
            p.innerHTML = data.msg;
        };

        //绑定事件  onblur失去焦点
        input.onblur = function(){
            //获取用户的输入值
            let username = this.value;
            //向服务器端发送请求 检测用户名是否存在
            // 1. 创建 script 标签
            const script = document.createElement('script');
            // 2. 设置标签的 src 属性
            script.src = 'http://127.0.0.1:8000/jsonp-check-username';
            // 3. 将 script 插入到文档中
            document.body.appendChild(script);
        }
    </script>
</body>
</html>

 后端 服务端:

// jsonp 服务 检测用户名字是否存在
app.all('/jsonp-check-username', (request,response) => {
    const data = {
        exist: 1,
        msg: '用户名已经存在,请换一个别的名字'
    };
    let str = JSON.stringify(data);            //转换字符串
    response.end(`Check_username( ${str} )`);  //返回 js代码 Check(str); 
});

用户输入完用户名,失去焦点后,获取用户输入的值,准备向服务端发送请求,创建 <script> 标签,设置 script 标签的 src 属性,将 script 标签插入到页面中。这时,页面已经有了 script 标签并发送了请求,服务端返回一个 js语句代码:Check_username(str);     ,str 是一个对象,就是服务端的 data 对象,但是它以json格式的字符串形式发给客户端,到客户端后 script 代码自动解析为json对象,然后客户端 Check_username(data) 函数被调用,实参str 传给形参data,然后 data.msg,得到 '用户名已经存在,请换一个别的名字'

 

jQuery 发送 jsonp 请求

语法:$.getJSON('url', function(data){  });    

 注意:在使用 jQuery 发送 jsonp 请求时,url 后面要补一个参数 callback ,它的值是 ? ,写法:callback=?          

完整的就是 http://127.0.0.1:8000/jquery-jsonp-server?callback=?         

 

发送请求,查看已发送请求里面的 callback 参数:

$.getJSON('http://127.0.0.1:8000/jquery-jsonp-server?callback=?', function(data){

    console.log(data);

});

 

 可以看到,原本的 callback=? 并没有值,但发送请求后,callback是有值的,变成了 jQuery36307196822213934448_1676633833786 

服务端可以接收到这个参数,然后把这个 callback 的值,作为函数调用的一个函数名,然后去拼接字符串,

也就是说,现在 jQuery 已经注册了一个名字叫 jQuery36307196822213934448_1676633833786 的函数,

只要回来之后,返回的是 jQuery36307196822213934448_1676633833786 函数的调用,前端就可以对这个数据做一个处理。

说白了,这一串数字,就是一个函数名,好比我在后端写一个 jQuery36307196822213934448_1676633833786( str ),就是调用了这个函数,里面实参是 str,但这个函数是 jQuery 发送请求而声明的,可以简单理解为:

$.getJSON(url, function() {} );,里的回调函数 function(){} 就是 callback 的 值,就是 jQuery36307196822213934448_1676633833786。只需要在服务端调用并返回就可以。

 

然后后端获取 callback 的值,也就是获取到这个函数名。就可以作为回调函数,来做处理。

app.all('/jquery-jsonp-server',(request,response) => {
    let cb = request.query.callback;
    response.end(cb);
});

 

 获取到 callback 的值,然后拼接字符串,调用函数:

模板字符串 `${cb}( ${str} )`,相当于 getMax(data) ,把俩字符串拼成函数和实参,调用函数。 

 

app.all('/jquery-jsonp-server',(request,response) => {
    const data = {
        name: '语言等级考场',
        city: ['北京','大连','沈阳']
    };
    let str = JSON.stringify(data);     //转换为字符串
    
    let cb = request.query.callback;    //获取 callback 参数的值
    //返回结果
    response.end(`${cb}( ${str} )`);     
    
    //resp...end(`getMax( ${str} )`);   //上一句的意思类似于这样:  函数名(实参)
});

客户端:

<body>
    <button>jQuery 点击发送 jsonp 请求</button>
    <div id="result">
    </div>
    <script>
        $('button').eq(0).click(function(){
            $.getJSON('http://127.0.0.1:8000/jquery-jsonp-server?callback=?', function(data){
                //callback 的值,是jQuery随机生成的回调函数名字
                //callback 作为一个函数的调用
                console.log(data);
                
                $('#result').html(`
                    名称:${data.name}, <br/>
                    地区:${data.city}
                `);
            });
        });
    </script>
</body>

 

完整客户端:

完整客户端

 <!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>jQuery 发送 jsonp 请求</title>
    <script crossorigin="anonymous" src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.3/jquery.js"></script>
</head>
<body>
    <button>jQuery 点击发送 jsonp 请求</button>
    <h2>$.getJSON(url, function() {} );,里的回调函数 function(){} 就是 callback 的 值,就是 jQuery36307196822213934448_1676633833786。只需要在服务端调用并返回就可以。</h2>
    <div id="result">
    </div>
    <script>
        $('button').eq(0).click(function(){
            $.getJSON('http://127.0.0.1:8000/jquery-jsonp-server?callback=?', function(data){
                //$.getJSON(url, function() {} );,里的回调函数 function(){} 就是 callback 的 值,就是 jQuery36307196822213934448_1676633833786。只需要在服务端调用并返回就可以。
                //callback 的值,是jQuery随机生成的回调函数名字
                //callback 作为一个函数的调用
                console.log(data);
                
                $('#result').html(`
                    名称:${data.name}, <br/>
                    地区:${data.city}
                `);
            });
        });
    </script>
</body>
</html>

主要服务端部分:

服务端核心

 // (9).jQuery发送jsonp请求
app.all('/jquery-jsonp-server',(request,response) => {
    const data = {
        name: '语言等级考场',
        city: ['北京','大连','沈阳']
    };
    let str = JSON.stringify(data);
    //接收 callback 参数
    //请求url参数中的callback参数的值 赋值给 cb,callback的值是随机的,如jQuery363008071787967251609_1676631158208。
    //callback的值也就是回调函数的名字,不过这个值就是jQuery随机生成的。

    let cb = request.query.callback;
    //返回结果
    //resp...end(`getMax( ${str} )`);
    response.end(`${cb}( ${str} )`);
    //response.end(`${cb}( )`);
});

 

 15- 设置CORS响应头实现跨域

 CORS 跨域资源共享。  跨源资源共享(CORS) - HTTP | MDN (mozilla.org)

CORS 是官方的跨域解决方案。不需要客户端做任何操作,完全在服务端进行。支持 GET POST。

如何工作?

只需要服务端设置响应头就可以了。

 

服务端:

// (10).设置CORS响应头实现跨域
app.all('/cors-server',(request,response) => {
  //设置响应头
    response.setHeader("Access-Control-Allow-Origin","*"); //允许跨域
    response.setHeader("Access-Control-Allow-Origin","http://127.0.0.1:6666");  //只有这个网页才可以向服务端发送请求。
    response.setHeader("Access-Control-Allow-Headers","*"); //允许头信息自定义
    response.setHeader("Access-Control-Allow-Method","*");  //请求方法随意
    response.send("hello cors");
});

客户端:

<button>点击发送请求</button>
    <h1 id="result"></h1>
    <script>
        const btn = document.querySelector('button');
        btn.onclick = function(){
            // 1.创建对象
            const xhr = new XMLHttpRequest();
            // 2.初始化设置
            xhr.open("GET","http://127.0.0.1:8000/cors-server");
            // 3.发送
            xhr.send();
            // 4.绑定事件
            xhr.onreadystatechange = function(){
                if(xhr.readyState === 4){
                    if(xhr.status >= 200 && xhr.status < 300){
                        // 输出响应
                        console.log(xhr.response);
                    }
                }
            }
        }
    </script>

 

 

学习资源:https://www.bilibili.com/video/BV1WC4y1b78y

 
posted @ 2023-02-11 18:57  nekmea  阅读(92)  评论(0编辑  收藏  举报