Ajax使用

Ajax(Asynchronous JavaScript And XML)是浏览器提供的一套方法,可以实现页面无刷新更新数据,提高网站浏览体验

Ajax相关

Ajax不是新的编程语言,而是将现有的标准组合使用的方式
通过Ajax可以在浏览器中向服务器发送异步请求,无需刷新页面与服务端通信,获取数据,允许根据用户事件来更新部分页面内容
但是没有浏览历史,不能回退,且存在跨域问题,对SEO优化不好

XML 可扩展标记语言,用于传输和存储数据

与html格式类似,但html中都是预定义标签,而XML全是自定义标签,用于表示一些数据
现在已经被JSON取代

// XML格式
<Student>
    <name></name>
    <age>18</age>
    <gender></gender>
</Student>

// JSON格式
{"name":"人","age":18,"gender":"男"}

http请求报文、响应报文的格式与参数

  • 请求报文
请求行  1.请求方式  /GET  2.URL  3.http协议版本 HTTP/1.1
请求头   Host: www.baidu.com
        Cookie: ...
        Content-Type: ...
        User-Agent: ...
        ....
空行
请求体   username=admin&password=123456
  • 响应报文
响应行  1.http协议版本 HTTP/1.1   2.响应状态码  200   3.响应状态字符串
响应头   Content-Type: ... // 网页描述
        Content-length: ...
        Content-encodeing: ...
        ....
空行
响应体   <html>    // 网页内容被放在响应体中
            <head>
            </head>
            <body>
            </body>
        </html>

Chrome控制台查看通信报文

F12 -> NetWork/网络 加载网页所有的请求都会列在这里
点击请求弹出详情标签页,主要关注 Headers/标头 与 Response/响应

node.js基本使用,通过node简单搭建服务端

  • 1.初始化环境:调出控制台集成终端(shift+command+Y),或在项目最外层根目录中右键 输入npm init --yes
  • 2.安装express框架npm i express
  • 3.新建并编写一个js文件作为服务端
  • 4.集成终端输入node js文件名启动服务,按control+C关闭服务
  • 当服务端代码修改时,需要重启服务
// 引入express
const express = require('express');

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

// 创建路由规则
// 参数1 端口服务
// 回调函数参数 request 对请求报文的封装
// 回调函数参数 response 对响应报文的封装

// 对get请求的服务
app.get('/server', (request, response) => {
    // 设置允许跨域
    response.setHeader('Access-Control-Allow-Origin','*');
    // 设置响应体
    response.send('hello forGET');
});
// "http://127.0.0.1:8000/server"
// 当访问到此URL或接收到针对此服务的请求时,就会执行回调函数中的代码,并由response作出响应

// 监听端口启动服务
app.listen(8000, () => {
    console.log('服务已在8000端口启动');
});
// 此时在"http://127.0.0.1:8000" + "/server"(get或post的第一个参数) 就可以访问到
// 或者"localhost:8000" + "/server" 访问
  • nodemon安装
    node服务端安装自动重启工具nodemon 当服务端发生改动时自动重启
    集成终端输入npm install -g nodemon
    mac系统需输入sudo npm install -g nodemon
    安装完成后,集成终端输入nodemon js文件名启动服务

Ajax使用

  • 发送GET请求
<button>点击发送请求</button>
<div id="getResult"></div>
<script>
    // 获取元素
    const btn = document.getElementsByTagName('button')[0];
    const getResult = document.getElementById('getResult');
    // 点击事件
    btn.onclick = function() {
        // 使用Ajax 4个步骤
        // 1.创建Ajax对象
        const xhr = new XMLHttpRequest();
        // 2.初始化 设置 请求方式 和 url
        xhr.open('GET', 'http://127.0.0.1:8000/server');
        // 3.发送请求
        xhr.send();
        // 4.事件绑定 处理服务端返回的结果 xhr.onreadystatechange
        // readystate xhr对象属性,状态码 0,1,2,3,4
        // 每当 readyState 发生改变时,都会执行函数 
        xhr.onreadystatechange = function() {
            // 4代表服务端已返回所有的响应结果
            if (xhr.readyState === 4) {
                // 响应状态码中 以2开头的都是响应成功
                if (xhr.status >= 200 && xhr.status < 300) {
                    console.log(xhr.status); // 响应状态码
                    console.log(xhr.statusText); // 响应状态字符串
                    console.log(xhr.getAllResponseHeaders()); // 所有响应头
                    console.log(xhr.response); // 响应体
                    getResult.innerHTML = xhr.response;
                }
            }
        }
    }
</script>
  • 设置GET请求参数
// 在设置请求方式和url时,在url后添加"?"+参数,参数间以&连接
xhr.open('GET', 'http://127.0.0.1:8000/server?a=1&b=2&c=3');
  • 发送POST请求
<div id="postResult"></div>
<script>
    // 获取元素
    const btn = document.getElementsByTagName('button')[0];
    const postResult = document.getElementById('postResult');
    // 鼠标事件
    postResult.onmouseenter = function() {
        // 1.创建Ajax对象
        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) {
                    postResult.innerHTML = xhr.response;
                }
            }
        }
    }
</script>
// 同时,在服务端需要有与之匹配的服务规则
// 对post请求的服务
app.post('/server', (request, response) => {
    // 设置允许跨域
    response.setHeader('Access-Control-Allow-Origin','*');
    // 设置响应体
    response.send('hello forPOST');
});
  • 设置POST请求参数
// 发送请求时设置post请求参数
xhr.send('a=1&b=2&c=3');
xhr.send('a=1:b=2:c=3');
xhr.send('1234567');
// 参数格式灵活,但服务端一定要有与之相应的处理方式
  • 设置请求头信息
// xhr.setRequestHeader() 方法  在发送请求前设置
xhr.setRequestHeader('Content','application/x-www-form-urlencoded'); // 预定义的请求头
xhr.setRequestHeader('name','abc'); // 自定义的请求头 一般会包含身份校验的信息,传递给服务器
// 自定义的请求头会报错,需要在服务端设置
// all可以接收任意类型的请求
app.all('/server', (request, response) => {
    // 设置允许跨域
    response.setHeader('Access-Control-Allow-Origin', '*');
    // 设置响应头
    response.setHeader('Access-Control-Allow-Headers', '*');
    // 设置响应体
    response.send('hello forPOST');
});

服务端响应JSON数据

// 配置服务端
app.all('/json-server', (request, response) => {
    // 设置允许跨域
    response.setHeader('Access-Control-Allow-Origin', '*');
    // 设置响应头
    response.setHeader('Access-Control-Allow-Headers', '*');
    // 设置响应数据
    const data = {
        name: 'abc'
    };
    let str = JSON.stringify(data); // 传输的是字符串数据
    // 设置响应体
    response.send(str);
});
<div id="postResult"></div>
<script>
    // 获取元素
    const btn = document.getElementsByTagName('button')[0];
    const postResult = document.getElementById('postResult');
    // 鼠标事件
    postResult.onmouseenter = function() {
        const xhr = new XMLHttpRequest();
        xhr.open('POST', 'http://127.0.0.1:8000/json-server');
        xhr.send();
        xhr.onreadystatechange = function() {
            if (xhr.readyState === 4) {
                if (xhr.status >= 200 && xhr.status < 300) {
                    // postResult.innerHTML = xhr.response;
                    console.log(xhr.response);
                    // 处理数据 手动转换JSON数据
                    let data = JSON.parse(xhr.response);
                    console.log(data);
                    postResult.innerHTML = data.name;
                }
            }
        }
    }
</script>
// 自动转换JSON数据的方式
// 在发送请求前就设置响应体数据的类型
xhr.responseType = 'json';

异常问题解决

IE浏览器缓存问题

IE浏览器会对Ajax请求的数据做缓存,当下一次发送同样的请求时,会从缓存中取数据,而不是从服务器返回最新数据

// 在请求url后加上当前时间戳,IE浏览器会认为这是两次不同的请求
xhr.open('POST', 'http://127.0.0.1:8000/server?t=' +Date.now());

请求超时控制与网络异常处理

// 服务端配置延时3s响应
app.get('/delay', (request, response) => {
    // 设置允许跨域
    response.setHeader('Access-Control-Allow-Origin', '*');
    // 延时3s
    setTimeout(() => {
        // 设置响应体
        response.send('hello forGET');
    }, 3000);
});
<button>点击发送请求</button>
<div id="getResult"></div>
<script>
    // 获取元素
    const btn = document.getElementsByTagName('button')[0];
    const getResult = document.getElementById('getResult');
    // 点击事件
    btn.onclick = function() {
        const xhr = new XMLHttpRequest();

        // 超时设置,2s后未收到响应,则取消请求
        xhr.timeout = 2000;
        // 超时回调
        xhr.ontimeout = function() {
            alert('网络异常,请稍后重试');
        };

        // 网络异常回调 // 模拟网络断开 F12控制台 No throttline改为Offline
        xhr.onerror = function() {
            alert('网络中断,请检查网络是否连接');
        };

        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) {
                    getResult.innerHTML = xhr.response;
                }
            }
        }
    }
</script>

取消请求

// 服务端使用延时配置
// xhr.abort() 取消请求
<button>点击发送请求</button>
<button>点击取消请求</button>
<script>
    // 获取元素
    const btn = document.querySelectorAll('button');
    // 放外面让两个元素都能访问到Ajax对象
    let xhr = new XMLHttpRequest();
    btn[0].onclick = function() {
        xhr = new XMLHttpRequest();
        xhr.open('GET', 'http://127.0.0.1:8000/delay');
        xhr.send();
        // 仅演示取消请求,数据不做处理
    }
    btn[1].onclick = function() {
        xhr.abort();
    }
</script>

请求重复发送问题

防止频繁发送相同请求,增加服务器负担

// 服务端使用延时配置
<button>点击发送请求</button>
<script>
    // 获取元素
    const btn = document.querySelectorAll('button');
    // 放外面让两个元素都能访问到Ajax对象
    let xhr = new XMLHttpRequest();
    // 标识变量 记录发送状态
    let isSending = false;
    btn[0].onclick = function() {
        if (isSending) xhr.abort(); // 如果正在发送,取消该请求,创建一个新的请求
        xhr = new XMLHttpRequest();
        // 修改标识变量 为正在发送
        isSending = true;
        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) {
                    console.log(xhr.response);
                    // 接收数据后,状态修改为未发送
                    isSending = false;
                }
            }
        }
    }
</script>

jQuery发送Ajax请求

  • jQuery文件 CDN链接
    搜索bootcdn -> 查询jquery -> 复制粘贴 <script> 标签链接

  • get请求

$.get(url,[data],[callback],[type]);

  • post请求

$.post(url,[data],[callback],[type]);

  • get与post请求 参数
  • url:请求地址
  • data:参数
  • callback:载入成功的回调函数
  • type:返回内容格式,XML、html、script、json、text、_default
  • 通用请求方法ajax

$.ajax({ })
个性化参数更多 查看文档

// 服务端配置
app.all('/jquery-server', (request, response) => {
    // 设置允许跨域
    response.setHeader('Access-Control-Allow-Origin', '*');
    // 设置响应头
    response.setHeader('Access-Control-Allow-Headers', '*');
    
    const data = { name: 'abc' };
    // 设置响应体
    response.send(JSON.stringify(data));
});
<button>get</button>
<button>post</button>
<button>ajax</button>
<script>
    $('button').eq(0).click(function() {
        $.get('http://127.0.0.1:8000/jquery-server', {a: 100,b: 200}, function(data) {
            console.log(data);
        });
    });
    $('button').eq(1).click(function() {
        $.post('http://127.0.0.1:8000/jquery-server', {a: 100,b: 200}, function(data) {
            console.log(data);
        }, 'json'); // 返回内容自动转换格式
    });
    $('button').eq(2).click(function() {
        $.ajax({
            url: 'http://127.0.0.1:8000/jquery-server', // 请求url
            data: {a: 100,b: 200}, // 参数
            type: 'GET', // 请求类型
            dataType: 'json', // 响应体数据类型
            // 请求成功回调函数
            success: function(data) {
                console.log(data);
            },
            timeout: 2000, // 超时时间
            // 请求失败回调函数  超时或网络异常都会调用
            error: function() {
                console.log('请求失败');
            },
            // 自定义 头信息
            headers: {c: 3,d: 4}
        });
    });
</script>

Ajax工具库 axios

使用npm下载或CDN链接 github
或者搜索bootcdn -> 查询axios -> 复制粘贴 <script> 标签链接

axios发送请求

// 服务端配置
app.all('/axios-server', (request, response) => {
    // 设置允许跨域
    response.setHeader('Access-Control-Allow-Origin', '*');
    // 设置响应头
    response.setHeader('Access-Control-Allow-Headers', '*');
    const data = { name: 'abc' };
    // 设置响应体
    response.send(JSON.stringify(data));
});
<button>get</button>
<button>post</button>
<button>axios函数</button>
<script>
    const btn = document.querySelectorAll('button');
    // 配置 baseURL
    axios.defaults.baseURL = 'http://127.0.0.1:8000';
    btn[0].onclick = function() {
        axios.get('/axios-server', {
            // 请求参数
            params: {
                ID: 12345
            },
            // 自定义请求头参数
            headers: {
                name: 'abc'
            }
        }).then(value => { // 数据返回与处理
            console.log(value);
        })
    };
    btn[1].onclick = function() {
        axios.post('/axios-server', {
            username: 'admin', // 请求体
            password: 'admin'
        }, {
            // 请求参数
            params: {
                ID: 123
            },
            // 自定义请求头参数
            headers: {
                age: 1
            }
        }).then(value => { // 数据返回与处理
            console.log(value);
        })
    };
    btn[2].onclick = function() {
        // axios函数发送请求
        axios({
            method: 'POST',
            url: '/axios-server',
            // 请求参数
            params: {
                ID: 123
            },
            // 自定义请求头参数
            headers: {
                age: 1
            },
            // 请求体参数
            data: {
                username: 'admin',
                password: 'admin'
            }
        }).then(response => { // 数据返回与处理
            console.log(response);
            console.log(response.status);
            console.log(response.statusText);
            console.log(response.headers);
            console.log(response.data);
        })
    };
</script>

fetch函数

// 服务端
app.all('/fetch-server', (request, response) => {
    // 设置允许跨域
    response.setHeader('Access-Control-Allow-Origin', '*');
    // 设置响应头
    response.setHeader('Access-Control-Allow-Headers', '*');
    const data = { name: 'abc' };
    // 设置响应体
    response.send(JSON.stringify(data));
});
<button>发送请求</button>
<script>
    const btn = document.querySelector('button');
    btn.onclick = function() {
        fetch('http://127.0.0.1:8000/fetch-server', {
            // 请求方式
            method: 'POST',
            // 请求头
            headers: {
                name: 'a'
            },
            // 请求体
            body: 'username=admin&password=admin'
        }).then(response => { // 响应数据处理
            // return response.text();
            return response.json();
        }).then(response => {
            console.log(response);
        })
    }
</script>

跨域

同源策略
是浏览器的一种安全策略,确保一个应用中的资源只能被本应用的资源访问
同源:网页请求的url与目标资源的url,协议、域名、端口,必须完全相同

违背同源策略就是跨域

  • 同源示例
// 服务端配置
// 引入express
const express = require('express');

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

app.get('/page', (request, response) => {
    // 响应一个页面
    response.sendFile(__dirname + '/JSTest.html');
    // 通过 http://127.0.0.1:8000/page 打开页面
});
app.get('/data', (request, response) => {
    // 响应信息
    response.send('userData');
    // 只有通过http://127.0.0.1:8000/page打开的页面,与请求才是同源(相同url端口的服务)
    // 使用live server插件打开的页面端口为5500,而5500端口没有此get服务
});

// 监听端口启动服务
app.listen(8000, () => {
    console.log('服务已在8000端口启动');
}); 
<button>发送请求</button>
<script>
    const btn = document.querySelector('button');
    btn.onclick = function() {
        const xhr = new XMLHttpRequest();
        // 满足同源策略 url可以简写
        // xhr.open('GET', 'http://127.0.0.1:8000/data');
        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>

JSONP跨域解决方案

JSONP (JSON with Padding),是非官方的跨域解决方案,只支持get请求
在网页有一些标签本身具有跨域能力,比如img、link、iframe、script
JSONP是利用script标签的跨域能力来发送请求

  • 简单跨域示例
<div id="result"></div>
<script>
    function handle(data) {
        const result = document.getElementById('result');
        result.innerHTML = data.name;
    }
</script>
<!-- <script src="data.js"></script> -->
<!-- 使用live server插件开启5500端口服务,再通过文件打开页面,此时js文件引入路径与页面完全不同,但引入仍然有效-->
<script src="http://127.0.0.1:5500/data.js"></script>
// data.js文件
const data = {
    name: 'nbname'
}

handle(data);
  • 使用script跨域
// 服务端
// 引入express
const express = require('express');

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

app.get('/check-username', (request, response) => {
    const data = {
        exist: 1,
        msg: '用户名已存在'
    }
    let str = JSON.stringify(data);
    // 返回一段js代码
    response.end(`handle(${str})`);
});

// 监听端口启动服务
app.listen(8000, () => {
    console.log('服务已在8000端口启动');
});
用户名:<input type="text" id="username">
<p></p>
<script>
    const input = document.getElementById('username');
    const p = document.querySelector('p');
    // 修改表单元素样式
    function handle(data) {
        input.style.border = 'solid 1px #f00';
        p.innerHTML = data.msg;
    }
    input.onblur = function() {
        // 获取用户输入
        let username = this.value;
        // 向服务端发送请求,检测用户名是否存在
        // 创建script标签
        const script = document.createElement('script');
        // 设置src属性
        script.src = 'http://127.0.0.1:8000/check-username';
        // 添加script标签
        document.body.appendChild(script);
    }
</script>
  • jQuery发送JSONP跨域请求
// 引入express
const express = require('express');

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

app.get('/jquery-jsonp-server', (request, response) => {
    const data = {
        name: 'qwer'
    }
    let str = JSON.stringify(data);
    // 接收callback参数 jQuery36003954591679019166_1648313886731
    let cb = request.query.callback;
    // 返回一段js代码
    response.end(`${cb}(${str})`);
});

// 监听端口启动服务
app.listen(8000, () => {
    console.log('服务已在8000端口启动');
});
<button>点击发送请求</button>
<div id="result"></div>
<script>
    $('button').eq(0).click(function() {
        // url后添加参数 ?callback=? 固定写法,虽然是空参数,但在运行时是有值的 jQuery36003954591679019166_1648313886731
        $.getJSON('http://127.0.0.1:8000/jquery-jsonp-server?callback=?', function(data) {
            $('#result').html(`
                名称:${data.name}
            `)
        })
    })
</script>

CORS跨域解决方案

CORS是一个W3C标准,全称是"跨域资源共享"(Cross-origin resource sharing)
不需要在客户端做任何特殊操作,完全在服务器中处理,支持 get 和 post 请求
通过设置一个响应头告知浏览器,请求允许跨域

// 设置允许跨域   * 通配,允许跨域访问所有站点,也可以设置指定url
response.setHeader('Access-Control-Allow-Origin', '*');
response.setHeader('Access-Control-Allow-Headers', '*');
response.setHeader('Access-Control-Allow-Method', '*');

查看更多响应头信息 MDN文档

posted @   ChiseledPumpkin  阅读(34)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 25岁的心里话
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示