原生AJAX

原生AJAX

AJAX:
全称为Asynchronous Javascript And XML,就是异步的 JS 和 XML,是一种用于创建动态 Web 应用程序的技术。
通过 Ajax 技术,可以在不刷新整个页面的情况下,向服务器发送请求,获取数据并进行处理,从而实现更加流畅、响应更快的 Web 应用程序。
Ajax 的核心是 XMLHttpRequest 对象,它提供了在客户端浏览器和服务器之间发送 HTTP 请求和接收响应的功能。使用 XMLHttpRequest 对象,可以在不刷新整个页面的情况下更新部分页面内容,从而实现异步数据传输。除此之外,还可以使用 jQuery 等 JavaScript 库中提供的 Ajax 方法、Fetch API 等技术来实现 Ajax 功能。

作用:
向服务器发送请求,服务器响应给我们浏览器内部一个对象,它并不是一门新技术,而是我们之前学过的多种技术的混合体。

都学过哪些向服务器发送请求(request)的方式:

  • get———————–
  1. 浏览器地址栏
  2. a标签的href
  3. img的src
  4. link的href
  5. 表单
  6. apifox/postman
  7. ajax
  • post——————————
  1. apifox/postman
  2. 表单
  3. jax

客户端和服务器

  • 客户端是向服务器发请求的。
    • 浏览器
    • postman
    • a标签 img标签 link标签 script标签 form标签….
    • ajax(异步局部刷新)
    • App
    • 小程序
  • 服务器主要接收客户端的请求,给出响应。通过node的框架叫express,提供服务器功能。

http请求(请求报文)

  • 请求行

    • 请求方法 get post put ….
    • url 不管是什么样的请求方式,都可以通过url传递参数
    • http协议版本
  • 请求头

    • Content-Type: text/plain, application/x-www-form-urlencoded, application/json, mutipul/form-data
  • 请求体(请求正文),就是传给服务器的数据,post请求和put请求才有请求体,get请求没有请求体

原生AJAX

http响应(响应报文):

  • 响应行
  • 响应头
  • 响应体(响应正文)

原生AJAX

经典四步

(前端)

第一步:创建网络请求的AJAX对象(使用XMLHttpRequest

 let xhr = new XMLHttpRequest()

第二步:监听XMLHttpRequest对象状态的变化,或者监听onload事件(请求完成时触发)

    xhr.onreadystatechange = function () {
        if (xhr.readyState == 4) {
            // response就是响应
            console.log(xhr.response);
        }
    }

绑定load的方法

当服务器把数据响应给ajax的时候 会触发load事件

        xhr.onload = function () {
            console.log(xhr.response);
        }

第三步:配置网络请求(通过open方法)

xhr.open('get', 'http://127.0.0.1:3001/getData')

第四步:发送send网络请求

xhr.send()

(后端)

安装以下依赖

"dependencies": {
    "body-parser": "^1.20.0",
    "express": "^4.18.1",
    "multer": "^1.4.5-lts.1"
}

创建一个服务器

原生AJAX

前端代码

注:此时必须要在浏览器上直接输入http://127.0.0.1:3000/demo01, 才能打开,不能用vs的open with live server

原生AJAX
网络面板的分析

原生AJAX

原生AJAX

通过url把数据扔给服务器

  • 任何的请求方式(get,post,put…)都可以通过url传参
  • 一旦修改了服务器代码,一定要重启服务器,修改的效果才会生效

前端向后端传输字符串

<body>
    <h1>通过url把数据扔给服务器</h1>
    <hr>
    <table>
        <tr>
            <td>用户名:</td>
            <td><input type="text" name="username" id="username"></td>
        </tr>
        <tr>
            <td>密码:</td>
            <td><input type="password" name="pwd" id="pwd"></td>
        </tr>
        <tr>
            <td></td>
            <td>
                <button id="btn">GET方式提交</button>
            </td>
        </tr>
    </table>
    <script>
        const btn = document.querySelector('#btn');
        const username = document.querySelector('#username');
        const pwd = document.querySelector('#pwd');

        const xhr = new XMLHttpRequest();
        xhr.onload = () => {
            alert(xhr.responseText);
        };

        btn.onclick = () => {
            // 拼接查询字符串
            const qs = `uername=${username.value}&pwd=${pwd.value}`
            xhr.open("get","/getData?"+qs)
            xhr.send();
        }
    </script>
</body>

后端

const express = require("express")
const path = require("path")

// 创建 express 实例
const app = express();


app.get("/",(req,res)=>{
    res.sendFile(path.join(__dirname,"../client","index.html"))
})

app.get('/getData', (req, res) => {
    console.log('接收到 GET 请求:')
    console.log('url:', req.url);
    console.log('从url中提取数据:', req.query);
    console.log('');
    res.send('GET 方式提交成功!');
});

app.listen(3000,()=>{
    console.log('服务器启动了,端口是3000');
})

前端传输字符串,后端可以将其转为对象
由xxx=xxx&yyy=yyy
变为{xxx:xxx,yyy:yyy}

原生AJAX

通过请求体把数据扔给服务器

get请求只能通过url把数据扔给服务器,post,put请求可以不仅可以通过url把数据扔给服务器,也可以通过请求体把数据扔给服务器。
数据通过请求体传输,有以下几种格式:

  • text/plain

    • 默认的请求体类型,如果不设置请求头字段 Content-type,默认就是该种类型
    • 求体只要是字符串就可以,后端不会做任何处理
  • application/x-www-form-urlencoded

    • 需要设置请求头 xhr.setRequestHeader(‘Content-type’, ‘application/x-www-form-urlencoded’);
    • 要求请求体是查询字符串,如 a=100&b=200
    • 点击提交按钮提交表单(非Ajax),如果 Method 是 Post,默认请求体内容就是 x-www-form-urlencoded
  • application/json

    • 设置设置请求头 xhr.setRequestHeader(‘Content-type’, ‘application/json’);
    • 要求请求体是 json 格式的字符串
  • multipart/form-data

1. 演示text/plain,后端代码:

1.req.body 就可以获取请求体中的数据
2.typeof req.body 查看接收到的数据是什么类型
3.res.send('POST 方式提交成功!'); 响应数据
4.req.query 得到url传递的参数的
5.req.headers['content-type'] 得到请求头

后端代码

// 导入模块
const path = require('path');
const express = require('express');
const bodyParser = require('body-parser');  // body-parser 专门用于接收请求体数据
const multer = require('multer');


// 创建 express 实例
const app = express();


// 挂载中间件处理请求体
app.use(bodyParser.text());
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({extended: false}));


app.get("/",(req,res)=>{
    res.sendFile(path.join(__dirname,"../client","index.html"))
})
app.post('/getData', (req, res) => {
    console.log('接收到 POST 请求:')
    console.log('URL中获取的信息:', req.query);
    console.log('请求体内容类型:', req.headers['content-type']);
    console.log('请求体中获取的信息:', req.body, typeof req.body);
    console.log('');
    res.send('POST 方式提交成功!');
});
app.listen(3000,()=>{
    console.log('服务器启动了,端口是3000');
})

前端代码

<body>
    <h1>通过url把数据扔给服务器</h1>
    <hr>
    <table>
        <tr>
            <td>用户名:</td>
            <td><input type="text" name="username" id="username"></td>
        </tr>
        <tr>
            <td>密码:</td>
            <td><input type="password" name="pwd" id="pwd"></td>
        </tr>
        <tr>
            <td></td>
            <td>
                <button id="btn">POST方式提交</button>
            </td>
        </tr>
    </table>
    <script>
        const btn = document.querySelector('#btn');
        const username = document.querySelector('#username');
        const pwd = document.querySelector('#pwd');

        const xhr = new XMLHttpRequest();
        xhr.onload = () => {
            alert(xhr.responseText);
        };

        btn.onclick = () => {
            // ?a=1&b=2 url 传参
            xhr.open("post","/getData?a=1&b=2")

            const qs = `uername=${username.value}&pwd=${pwd.value}`
            // send中写请求体  send(qs); 叫请求体传参
            xhr.send(qs);
        }
    </script>
</body>

原生AJAX
此时请求头有一个默认的content-type为 text/plain

2. 演示 application/x-www-form-urlencoded,后端同上

注:请求头一定要在 open() 之后,send() 之前设置

前端代码

<body>
    <h1>通过url把数据扔给服务器</h1>
    <hr>
    <table>
        <tr>
            <td>用户名:</td>
            <td><input type="text" name="username" id="username"></td>
        </tr>
        <tr>
            <td>密码:</td>
            <td><input type="password" name="pwd" id="pwd"></td>
        </tr>
        <tr>
            <td></td>
            <td>
                <button id="btn">POST方式提交</button>
            </td>
        </tr>
    </table>
    <script>
        const btn = document.querySelector('#btn');
        const username = document.querySelector('#username');
        const pwd = document.querySelector('#pwd');

        const xhr = new XMLHttpRequest();
        xhr.onload = () => {
            alert(xhr.responseText);
        };

        btn.onclick = () => {
            // ?a=1&b=2 url 传参
            xhr.open("post","/getData?a=1&b=2")

            const qs = `uername=${username.value}&pwd=${pwd.value}`

            // 在发请求之前,设置请求头,设置的头,叫Content-Type
            // 请求头一定要在 open() 之后,send() 之前设置
            xhr.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');

            // send中写请求体  send(qs); 叫请求体传参
            xhr.send(qs);
        }
    </script>
</body>

原生AJAX

原生AJAX
此时请求头的content-type为 application/x-www-form-urlencoded

3. 演示 application/json,后端同上。

前端代码:

<body>
    <h1>通过url把数据扔给服务器</h1>
    <hr>
    <table>
        <tr>
            <td>用户名:</td>
            <td><input type="text" name="username" id="username"></td>
        </tr>
        <tr>
            <td>密码:</td>
            <td><input type="password" name="pwd" id="pwd"></td>
        </tr>
        <tr>
            <td></td>
            <td>
                <button id="btn">POST方式提交</button>
            </td>
        </tr>
    </table>
    <script>
        const btn = document.querySelector('#btn');
        const username = document.querySelector('#username');
        const pwd = document.querySelector('#pwd');

        const xhr = new XMLHttpRequest();
        xhr.onload = () => {
            alert(xhr.responseText);
        };

        btn.onclick = () => {
            // ?a=1&b=2 url 传参
            xhr.open("post","/getData?a=1&b=2")

            // 如果传递的格式是application/json 那么就不再是x=y&z=s
            const data = { username:username.value, pwd:pwd.value }

            // 请求头一定要在 open() 之后,send() 之前设置
            xhr.setRequestHeader('Content-type', 'application/json');

            // send中写请求体  send(qs); 叫请求体传参
            // 传递时,需要把JSON对象,手动转化成JSON字符串
            xhr.send(JSON.stringify(data));
        }
    </script>
</body>

此时请求头的content-type为 application/josn
原生AJAX

  1. text/plain
    username=wc&pwd=111 字符串
    后端不会解析成对象,一般不用
  2. application/x-www-form-urlencoded
    username= admin&pwd=123 字符串
    后端会解析成对象
  3. application/json
    JSON.stringfy()转化成字符串
    后端也解析成对象了
    注:不论哪种格式,传递时,都是字符串

表单默认的也可以发送请求

前端代码:

<body>
    <h1>通过url把数据扔给服务器</h1>
    <hr>
    <form action="/getData?a=1&b=2" method="post">
        <table>
            <tr>
                <td>用户名:</td>
                <td><input type="text" name="username" id="username"></td>
            </tr>
            <tr>
                <td>密码:</td>
                <td><input type="password" name="pwd" id="pwd"></td>
            </tr>
            <tr>
                <td></td>
                <td>
                    <input type="submit" value="非ajax请求"></input>
                </td>
            </tr>
        </table>
    </form>
</body>

原生AJAX
直接表单请求,整个页面都会刷新
ajax请求,异步局部刷新

通过FormData传递数据

请求体(send()的方法的参数)除了是字符串,也可以是 formData 对象。如果请求体是 FormData 对象,浏览器会自动设置请求头字段 Content-type 为 multipart/form-data。

FormData不仅可以传递普通数据,还可以上传文件。

后端代码:

const path = require('path');
const express = require('express');
const bodyParser = require('body-parser');  // body-parser 专门用于接收请求体数据
const multer = require('multer');
// 创建 express 实例
const app = express();

// 挂载中间件处理请求体
app.use(bodyParser.text());
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({extended: false}));

app.get("/",(req,res)=>{
    res.sendFile(path.join(__dirname,"../client","index.html"))
})

// req.body 接收前端传递过来的普通数据
// req.file  接收前端上传过来的文件
const upload = multer({ dest: 'uploads/' })
app.post('/upload',upload.single('avator'),(req, res) => {
    console.log('文件上传成功:');
    console.log('文件信息:', req.file);
    console.log('表单数据:', req.body);
    console.log('');
    res.send('文件上传成功!');
});

app.listen(3000,()=>{
    console.log('服务器启动了,端口是3000');
})

前端代码

(浏览器自己添加的请求头,我们没有设置):

<body>
    <h1>通过formdata把数据扔给服务器</h1>
    <hr>
    <table>
        <tr>
            <td>用户名:</td>
            <td><input type="text" name="username" id="username"></td>
        </tr>
        <tr>
            <td>密码:</td>
            <td><input type="password" name="pwd" id="pwd"></td>
        </tr>
        <tr>
            <td></td>
            <td>
                <button id="btn1">提交1</button>
            </td>
        </tr>
    </table>

    <script>
        const username = document.querySelector("#username")
        const pwd = document.querySelector("#pwd")
        const btn1 = document.querySelector("#btn1")
        const xhr = new XMLHttpRequest();
        xhr.onload = () => {
            alert(xhr.responseText);
        };
        btn1.onclick = () => {
            // 创建FormData  fd是一个对象  容器
            const fd = new FormData();
            fd.append("username",username.value)
            fd.append("pwd",pwd.value)
            fd.append("aaaa",1111)
            fd.append("bbbb",2222)
            xhr.open('POST', '/upload');
            // send中不只可以放字符串,还可以放formdata对象
            xhr.send(fd);
        }
    </script>

</html>

另一种写法:

<body>
    <h1>通过formdata把数据扔给服务器</h1>
    <hr>
    <!-- form表单有默认的提交事件 -->
    <!-- onsubmit="return false" 阻止默认事件的 -->
    <form onsubmit="return false">
        <table>
            <tr>
                <td>用户名:</td>
                <td><input type="text" name="username" id="username"></td>
            </tr>
            <tr>
                <td>密码:</td>
                <td><input type="password" name="pwd" id="pwd"></td>
            </tr>
            <tr>
                <td></td>
                <td>
                    <button id="btn1">提交1</button>
                </td>
            </tr>
        </table>
    </form>

    <script>
        const username = document.querySelector("#username")
        const pwd = document.querySelector("#pwd")
        const btn1 = document.querySelector("#btn1")

        const xhr = new XMLHttpRequest();

        xhr.onload = () => {
            alert(xhr.responseText);
        };


        btn1.onclick = () => {
            const formBox = document.querySelector("form");
            // 创建formData容器时,传递一个formDOM元素
            // 自动将form元素中的表单控制添加到formData中
            const fd = new FormData(formBox);

            xhr.open('POST', '/upload');
            xhr.send(fd);
        }
    </script>
</body>

原生AJAX

此时请求头的content-type为 multipart/form-data类型

原生AJAX

通过FormData实现文件上传

后端代码:

const path = require('path');
const express = require('express');
const bodyParser = require('body-parser');  // body-parser 专门用于接收请求体数据
const multer = require('multer');

// 创建 express 实例
const app = express();

// 挂载中间件处理请求体
app.use(bodyParser.text());
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({extended: false}));


app.get("/",(req,res)=>{
    res.sendFile(path.join(__dirname,"../client","index.html"))
})

// req.body 接收前端传递过来的普通数据
// req.file  接收前端上传过来的文件
const upload = multer({ dest: 'uploads/' })
app.post('/upload',upload.single('avator'), (req, res) => {
    console.log('文件上传成功:');
    console.log('文件信息:', req.file);
    console.log('表单数据:', req.body);
    console.log('');
    res.send('文件上传成功!');
});


app.listen(3000,()=>{
    console.log('服务器启动了,端口是3000');
})

前端代码

<body>
    <h1>通过formdata把数据扔给服务器</h1>
    <hr>
    <!-- form表单有默认的提交事件 -->
    <!-- onsubmit="return false" 阻止默认事件的 -->
    <form onsubmit="return false">
        <table>
            <tr>
                <td>用户名:</td>
                <td><input type="text" name="username" id="username"></td>
            </tr>
            <tr>
                <td>密码:</td>
                <td><input type="password" name="pwd" id="pwd"></td>
            </tr>
            <tr>
                <td>头像: </td>
                <td><input type="file" name="avator" id="avator"></td>
            </tr>
            <tr>
                <td></td>
                <td>
                    <button id="btn1">提交1</button>
                </td>
            </tr>
        </table>
    </form>

    <script>
        const username = document.querySelector("#username")
        const pwd = document.querySelector("#pwd")
        const avator = document.querySelector("#avator")
        const btn1 = document.querySelector("#btn1")

        const xhr = new XMLHttpRequest();

        xhr.onload = () => {
            alert(xhr.responseText);
        };


        btn1.onclick = () => {
            // const formBox = document.querySelector("form");
            // 创建formData容器时,传递一个formDOM元素
            // 自动将form元素中的表单控制添加到formData中
            // const fd = new FormData(formBox);

            const fd = new FormData();
            fd.append("uname",username.value);
            fd.append("upwd",pwd.value);
            fd.append("avator",avator.files[0]);

            xhr.open('POST', '/upload');
            xhr.send(fd);
        }
    </script>
</body>

原生AJAX

此时请求头的content-type为 multipart/form-data类型

图片被下载下来了

原生AJAX

响应报文

后端:

const path = require('path');
const express = require('express');

// 创建 express 实例
const app = express();

app.get("/",(req,res)=>{
    res.sendFile(path.join(__dirname,"../client","index.html"))
})

app.get('/getData', (req, res) => {
    console.log('接收到请求');
    res.send('hello ajax ' + Math.random());
});

app.listen(3000,()=>{
    console.log('服务器启动了,端口是3000');
})

前端:

<body>
   <button id="btn">点我发请求</button>
    <script>
        const xhr = new XMLHttpRequest();  

        xhr.onload = function(){
            // 响应分三部分:
            //   响应行
            console.log("响应行:")
            console.log('响应状态码:', xhr.status);
            console.log('响应状态描述:', xhr.statusText);
            console.log('');
            //   响应头
            console.log('响应头:');
            console.log('Content-type:', xhr.getResponseHeader('Content-type'));
            console.log('Date:', xhr.getResponseHeader('Date'));
            console.log(xhr.getAllResponseHeaders());
            console.log('');
            //   响应体
            console.log('响应体:')
            console.log(xhr.response);
            console.log(xhr.responseText);
        }
        const btn = document.querySelector("#btn");
        btn.onclick = ()=>{
            xhr.open("get","/getData")
            // get请求没有请求体,null是空的意思
            xhr.send(null);
        }

    </script>
</body>

获取到的结果(响应报文)
原生AJAX

响应JSON

如果想让后端响应一个JSON,也需要设置一个响应头,告诉客户端,响应的数据类型。

后端:

const path = require('path');
const express = require('express');

// 创建 express 实例
const app = express();
app.get("/",(req,res)=>{
    res.sendFile(path.join(__dirname,"../client","index.html"))
})
app.get('/getData', (req, res) => {
    console.log('接收到请求');
    const stus = [
        {id:"01",name:"malu",age:18,score:88},
        {id:"02",name:"wc",age:13,score:23},
        {id:"03",name:"xq",age:16,score:44},
        {id:"04",name:"xx",age:10,score:55},
    ]
    const stusStr = JSON.stringify(stus)
    // 告诉客户端,响应的是JSON数据
    res.setHeader("Content-Type", "application/json;charset=utf-8");
    res.send(stusStr);
});

app.listen(3000,()=>{
    console.log('服务器启动了,端口是3000');
})

前端:

1. 请求同一个url,第一次不走缓存
2. 从第二次开始,就可能走缓存,304表示走的缓存
注:切记要把禁用缓存关掉
3. 不想走缓冲,需要让每次请求的url不一样

<body>
    <button id="btn">点我发请求</button>
    <script>
        const xhr = new XMLHttpRequest();

        // 设置期望的响应数据的数据类型,期望服务器响应json
        // xhr.responseText  这个API就不能使用了
        xhr.responseType = "json";

        xhr.onload = function () {
            // console.log(JSON.parse(xhr.responseText));

            // xhr.response得到服务器响应的JSON数据  不需要通过JSON.parse
            console.log(xhr.response);
        }

        const btn = document.querySelector("#btn");
        btn.onclick = () => {
            
            // xhr.open("get", "/getData?random="+Math.random())
            xhr.open("get", "/getData?xx="+Date.now())
            xhr.send(null);
        }

    </script>
</body>

浏览器测试

原生AJAX

超时处理

后端:

const path = require('path');
const express = require('express');

// 创建 express 实例
const app = express();

app.get("/",(req,res)=>{
    res.sendFile(path.join(__dirname,"../client","index.html"))
})

app.get('/getData', (req, res) => {
    console.log('接收到请求');
    const timeout = Math.floor(Math.random()*10) * 1000

    const stus = [
        {id:"01",name:"malu",age:18,score:88},
        {id:"02",name:"wc",age:13,score:23},
        {id:"03",name:"xq",age:16,score:44},
        {id:"04",name:"xx",age:10,score:55},
    ]
    const stusStr = JSON.stringify(stus)
    // 告诉客户端,响应的是JSON数据
    res.setHeader("Content-Type", "application/json;charset=utf-8");
    setTimeout(()=>{
        res.send(stusStr);
    },timeout)
});

app.listen(3000,()=>{
    console.log('服务器启动了,端口是3000');
})

前端:

<body>
    <button id="btn">点我发请求</button>
    <button id="btn2">手动取消请求</button>
    <script>
        const xhr = new XMLHttpRequest();

        xhr.responseType = "json";

        xhr.onload = function () {
            console.log(xhr.response);
        }

        // 设置超时时间  5s
        // 如果时间到了,数据还没有回来,会自动取消请求
        xhr.timeout = 5000; 

        const btn = document.querySelector("#btn");
        btn.onclick = () => {
            xhr.open("get", "/getData?xx="+Date.now())
            xhr.send(null);
        }

        const btn2 = document.querySelector("#btn2");
        btn2.onclick = () => {
          xhr.abort(); // 手动取消请求~
        }

    </script>
</body>

原生AJAX

Ajax中的事件

  • readystatechange 当ajax状态发生变化,就会触发,触发至少 4 次
    • 0 UNSET — XHR对象已创建或已被 abort() 方法重置。
    • 1 OPENDED — open() 方法已经被调用。
    • 2 HEADERS_RECEIVED — send() 方法已经被调用,并且响应头和响应状态已经可获得。
    • 3 LOADING — 下载中, responseText 属性已经包含部分数据。
    • 4 DONE — 所有响应数据接收完毕。
  • load 响应结束的时候触发,此时 readyState 的值是 4
  • loadstart 开始请求的时候触发,此时 readyState 的值是 1
  • loadend 响应结束之后触发,不论请求是否成功都会触发
  • error 请求失败触发,应用层面的错误也算是请求成功(如 404错误),只有网络错误才算请求失败,指请求无法发出的错误
  • progress 当开始接收响应内容,被触发多次,该事件的回调函数可以获取一个 progressEvent 对象

e.loaded表示已下载数据 e.total表示全部数据
由此写一个案例,显示出正在加载xx%

前端代码:

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

<head>
    <meta charset="UTF-8">
    <title>Document</title>
    <style>
        #box {
            margin-top: 20px;
            width: 600px;
            padding: 20px;
            min-height: 200px;
            border: 1px solid #999;
        }
    </style>
</head>

<body>
    <button id="btn">点我发请求</button>
    <div id="box"></div>
    <script>
        const xhr = new XMLHttpRequest();
        const divBox = document.querySelector("#box")
        // xhr有好多状态,只要状态发生变化,就会触发readystatechange事件
        xhr.onreadystatechange = ()=>{
            console.log("xhr readystate:", xhr.readyState)
        }
        // 开始发请求  xhr.readyState是1
        xhr.onloadstart = ()=>{
            console.log('loadstart');
        }
        // 不管成功还是失败,loadend都要走
        xhr.onloadend = ()=>{
            console.log('onloadend');
        }
        // 在接收响应数据时,接收一点,就会触发progress事件,直到接入完毕
        xhr.onprogress = (e)=>{
            console.log('e:',e.loaded, e.total);
            divBox.innerHTML = "已下载:"+(e.loaded/e.total * 100).toFixed(2)+"%"
            if(e.loaded == e.total){
                divBox.innerHTML = "下载成功~"
            }
        }
        const btn = document.querySelector("#btn");
        btn.onclick = () => {
            xhr.open("get", "/getData?xx="+Date.now())
            xhr.send(null);
        }

    </script>
</body>

</html>

后端:

// 导入模块
const path = require('path');
const express = require('express');
// 创建 express 实例
const app = express();

app.get("/",(req,res)=>{
    res.sendFile(path.join(__dirname,"../client","index.html"))
})

app.get('/getData', (req, res) => {
    console.log('接收到请求');
    // 告诉客户端,响应的是JSON数据
    res.setHeader("Content-Type", "application/json;charset=utf-8");
        res.send('hello malu'.repeat(10000000))
});

app.listen(3000,()=>{
    console.log('服务器启动了,端口是3000');
})

得到的结果
原生AJAX

总结

ajax对象中的属性如下:

属性名 含义
readyState 返回一个数字,表示请求的状态: 0 -- UNSET -- XHR对象已创建或已被 abort() 方法重置。 1 -- OPENDED -- open() 方法已经被调用。 2 -- HEADERS_RECEIVED -- send() 方法已经被调用,并且响应头和响应状态已经可获得。 3 -- LOADING -- 下载中, responseText 属性已经包含部分数据。 4 -- DONE -- 所有响应数据接收完毕。
status 响应状态码,如 404、200 等。
statusText 响应状态码的文本描述,如 200 对应的是 “OK”。
responseXML 接收格式为 XML 的响应数据,返回一个 document 对象。
responseText 获取响应文本,返回一个字符串。
responseType 用于设置响应内容的类型 
response 返回的类型取决于 responseType 的设置。 
timeout 设置超时时间。

ajax对象中的方法如下:

方法名 含义
open() 初始化 HTTP 请求,用来指定请求方式和 URL。 xhr.open(method, url, [async], [user], [password])
send() 发送 HTTP 请求,参数可以设置请求体,没有请求体无需设置参数。
setRequestHeader() 设置 HTTP 请求头的值。必须在 open() 之后、send() 之前调用。
abort() 如果请求已被发出,则立刻中止请求。
getAllResponseHeaders() 以字符串形式返回所有的响应头。
getResponseHeader() 返回指定的响应头。

ajax对象中的事件如下:

事件名 含义
onreadystatechange readyState 属性值发生变化触发该事件。
onabort 请求中止时触发。
onerror 请求遇到错误时触发。
onloadstart 接收到响应数据时触发。
onload 请求成功完成时触发。
onloadend 当请求结束时触发, 无论请求成功 ( load) 还是失败 (abor 或 error)。
onprogress 当请求接收到更多数据时,周期性地触发。
ontimeout 在预设时间内没有接收到响应时触发。

默认情况下,ajax发的请求,都是异步的,通过open方法中的第3个参数可以设置同步

  • true 表示异步请求 , 不写也是true
  • false 表示同步请求,基本上不用
    注:
    异步请求 —在发请求之后,其它的同步任务都已经执行完毕了。
    同步请求 —在发请求之后,需要等到响应完全结束后才会执行剩下同步任务。

跨域 cors

同源策略是浏览器的一种安全策略,要求ajax代码所在的页面url中的协议,域名,端口与ajax请求中url中的协议,域名,端口要完全一样。

  • 源:协议 + 域名 + 端口
  • 同源:相同的协议 && 相同域名 && 相同的端口
  • 不同源:不同的协议 || 不同的域名 || 不同的端口

前端代码:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Document</title>
    <style>
        #box {
            margin-top: 20px;
            width: 600px;
            padding: 20px;
            min-height: 200px;
            border: 1px solid #999;
        }
    </style>
</head>

<body>
    <button id="btn">点我发请求</button>
    <div id="box"></div>
    <script>
        const xhr = new XMLHttpRequest();
        const divBox = document.querySelector("#box")
        const btn = document.querySelector("#btn")

        xhr.onload = ()=>{
            divBox.innerHTML += xhr.response + '<br>';
        }
        xhr.onerror = () => {
            console.log('请求失败!');
        }

        btn.onclick = () => {
            xhr.open("get","http://127.0.0.1:3000/getData")
            xhr.send();
        }
    </script>
</body>

</html>

后端:

const path = require('path');
const express = require('express');

// 创建 express 实例
const app = express();

app.get("/", (req, res) => {
    res.sendFile(path.join(__dirname, "../client", "index.html"))
})

app.get('/getData', (req, res) => {
    console.log("来到服务器了~~")
    res.send('hello malu')
});

app.listen(3000, () => {
    console.log('服务器启动了,端口是3000');
})

当我们访问不同源时,会有报错出现,但代码是成功走到服务器的,即同源策略

原生AJAX
原生AJAX

要抵抗同源策略,即跨域,常见解决跨越的方案

  • cors
  • jsonp
  • 前端配置代理

cors解决跨域

是后端去解决跨域问题的  重点代码Access-Control-Allow-Origin
在上面的后端代码中添加以下代码

app.get('/getData', (req, res) => {
    console.log("来到服务器了~~")
    // 通过请求头,抵抗同源策略
    // res.set('Access-Control-Allow-Origin', 'http://localhost:3000');
    // res.set('Access-Control-Allow-Origin', 'http://192.168.217.1:3000');

    // 请允许多个域名来请求
    // const allowOrigins = ['http://localhost:3000','http://192.168.217.1:3000']
    // if (allowOrigins.includes(req.get('Origin'))) {
    //     res.set('Access-Control-Allow-Origin', req.get('Origin'));
    // }

    // 允许所有的域名
    res.set('Access-Control-Allow-Origin', '*');

    res.send('hello malu')
});

此时即使不同源也可以获取数据

还有一个模块,叫做cros模块,访问www.npmjs.com/package/cor…, 自行探索

JSONP

不受同源策略的限制

  • 资源的引入 如:img标签的src link标签的href script标签的src
  • 页面中的超连接 a标签中的href
  • 表单的提交
  • 重定向页面

发送http请求的方式:

  • 浏览器的地址栏,只能发送get请求
  • postman软件,可以各种请求,类似postman这样的软件有很多
  • html中有些标签也会发请求,如img, link, script, a, form….
  • ajax可以发各位请求,最大的特点:局部刷新,提升用户体验

JSONP不是ajax,JSONP发请求本质是利用script标签的src发的请求

Fetch

Fetch API 被设计用来取代 XMLHttpRequest,它提供了许多与 XMLHttpRequest 相同的功能,但被设计成更具可扩展性和高效性。

Fetch API 的主要特点包括:

  1. Promise 风格的 API:Fetch API 提供了 Promise 风格的 API,可以更加方便地处理异步请求和响应。
  2. 更加灵活的请求和响应:Fetch API 可以发送任何类型的请求,包括 GET、POST、PUT、DELETE 等,也可以接收任何类型的响应,包括文本、JSON、二进制数据等。
  3. 更加强大的响应处理:Fetch API 提供了一系列的响应处理方法,包括 json()、text()、blob()、arrayBuffer() 等,可以更加方便地处理响应数据

前端代码:

<body>
<!-- CRUD   增删改查 -->
<button id="btn1">get请求</button>
<button id="btn2">post请求</button>
<button id="btn3">put请求</button>
<button id="btn4">delete请求</button>
<script>
let btn1 = document.querySelector("#btn1");
let btn2 = document.querySelector("#btn2");
let btn3 = document.querySelector("#btn3");
let btn4 = document.querySelector("#btn4");
// get请求
btn1.onclick = async function () {
// fetch方法返回 pormise对象
// let p = fetch("http://httpbin.org/get?a=1");
// console.log(p)  // Promise padding
// fetch("http://httpbin.org/get?a=1").then(res=>{
//     return res.json(); // 把可读流处理成json数据
// }).then(data=>{
//     console.log("data:",data)
// }).catch(reason=>{
//     console.log('请求失败!', reason);
// })
// fetch("http://httpbin.org/get?a=1").then(res=>res.json()).then(data=>{
//     console.log("data:",data)
// }).catch(reason=>{
//     console.log('请求失败!', reason);
// })
try {
const res = await fetch("http://httpbin.org/get?a=1")
const data = await res.json();
console.log(data);
} catch (reason) {
console.log('请求失败!', reason);
}
}
//post请求
btn2.onclick = function () {
let user = {name:"malu",age:10,sex:"man",score:{chinese:110,maths:90,english:88}}
const res = await fetch("http://httpbin.org/post",{
method:"post",
headers:{
'Content-type': 'application/json',
},
body:JSON.stringify(user)
})
const data = await res.json()
console.log("data:",data)
}
//put请求
btn3.onclick = function () { 
let user = {id:1, name:"malu",age:10,sex:"woman",score:{chinese:66,maths:44,english:99}}
const res = await fetch("http://httpbin.org/put",{
method:"put",  // 修改数据
headers:{
'Content-type': 'application/json',
},
body:JSON.stringify(user)
})
const data = await res.json()
console.log("data:",data)
}
//delete请求
btn4.onclick = function () { 
const res = await fetch("http://httpbin.org/delete/1",{
method:"delete", 
headers:{
'Content-type': 'application/json',
}
})
const data = await res.json()
console.log("data:",data)
}
</script>
</body>

原生AJAX

Axios介绍

Axios 是前端最流行的 ajax 请求库 ,没有之一,react、vue官方都推荐使用 的 Ajax 请求库。

axios特点

  • 基于 XMLHttpRequest + Promise 的异步的 Ajax 请求库
  • 浏览器端、Node端都可以使用
  • 支持请求和响应拦截器
  • 支持请求取消
  • 批量发送多个请求
  • 支持请求与响应的数据转换(二次封装)

Axios的基本使用

  • axios(config): 通用/最本质的发送意类型请求的方式。
  • axios(url[,config]): 第一个参数是地址,第二个参数是配置项。
  • axios.request(config): 等同于axios(config) (了解)
  • axios.get(url[, config]): 发get请求
  • axios.delete(url[, config]): 发delete请求
  • axios.post(url[, data, config]): 发post请求
  • axios.put(url[, data, config]): 发put请求
  • axios.patch(url[, data[, config]]) 发送patch请求

前端演示:

介绍了五种方式,调用axios

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<!--将源码直接拉下来,就不需要每次访问网页-->
<script src="./js/axios.js"></script>
</head>
<body>
<button id="btn">使用axios发请求</button>
<script>
const btn = document.querySelector("#btn");
btn.onclick = async function(){
// 使用方式一  axios函数使用一个对象作为参数
// axios({
//     method:"GET",
//     url:'https://tenapi.cn/v2/toutiaohot'
// }).then(res => {
//     console.log(res);
// }).catch(reason => {
//     console.log('请求失败!', reason);
// });
// 使用方式二  第一个参数是url,第二个参数是请求配置对象
// axios("/toutiaohot",{
//     method:"GET",
//     baseURL:"https://tenapi.cn/v2",
//     timemout:5000
// }).then(res => {
//     console.log(res);
// }).catch(reason => {
//     console.log('请求失败!', reason);
// });
// 使用方式三  axios.request() 方法同 axios 本身
// axios.request("/toutiaohot",{
//     method:"GET",
//     baseURL:"https://tenapi.cn/v2",
//     timemout:5000
// }).then(res => {
//     console.log(res);
// }).catch(reason => {
//     console.log('请求失败!', reason);
// });
// 使用方式四  axios.get()  axios.post()
// axios.get("/toutiaohot",{
//     baseURL:"https://tenapi.cn/v2",
//     timemout:5000
// }).then(res => {
//     console.log(res);
// }).catch(reason => {
//     console.log('请求失败!', reason);
// });
// 使用方式五   async await 方式  //最简单
// const res = await axios.get("/toutiaohot",{
//     baseURL:"https://tenapi.cn/v2",
//     timemout:5000
// })
const res = await axios.get("https://tenapi.cn/v2/toutiaohot")
console.log(res);
}
</script>
</body>
</html>

Axios请求配置项

托管静态页面

原生AJAX

常用的请求配置项:

{
// `url` 是用于请求的服务器 URL
url: '/user',
// `method` 是创建请求时使用的方法
method: 'get', // default
// `baseURL` 将自动加在 `url` 前面,除非 `url` 是一个绝对 URL。
// 它可以通过设置一个 `baseURL` 便于为 axios 实例的方法传递相对 URL
baseURL: 'https://some-domain.com/api/',
// `headers` 是即将被发送的请求头
headers: {
'Content-type': 'appliation/json'
},
// `data` 是作为请求主体被发送的数据
// 只适用于这些请求方法 'PUT', 'POST', 和 'PATCH'
// 在没有设置 `transformRequest` 时,必须是以下类型之一:
// - string, plain object, ArrayBuffer, ArrayBufferView, URLSearchParams
// - 浏览器专属:FormData, File, Blob
// - Node 专属: Stream
data: {
firstName: 'Fred'
},
// `timeout` 指定请求超时的毫秒数(0 表示无超时时间)
// 如果请求话费了超过 `timeout` 的时间,请求将被中断
timeout: 1000,
// `responseType` 表示服务器响应的数据类型,可以是 'arraybuffer', 'blob', 'document', 'json', 'text', 'stream'
responseType: 'json', // default
}

发送get,post,put,delete请求

<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
</head>
<body>
<button id="btn1">get</button>
<button id="btn2">post</button>
<button id="btn3">put</button>
<button id="btn4">delete</button>
<script>
const btn1 = document.querySelector("#btn1");
const btn2 = document.querySelector("#btn2");
const btn3 = document.querySelector("#btn3");
const btn4 = document.querySelector("#btn4");
// 做全局配置
axios.defaults.baseURL = "http://localhost:3000"
axios.defaults.timeout = 5000;
axios.defaults.headers.token = "xxxx";  // 不用管
// get
btn1.onclick = async function () {
// axios.get("/users?a=1&b=2").then(res => {
axios.get("/users", {
params: { name: "malu" }  // 也是配置问号传参
}).then(res => {
console.log(res.data);
}).catch(reason => {
console.log('请求失败:', reason);
})
}
// post
btn2.onclick = async function () {
// Content-Type: application/json
let user = {
id: 3,
name: "xq"
};
// axios.post中第二个参数是请求体, 第三个参数是配置对象
// 传递的参数的类型是:Content-Type: application/json
// 不需要通过JSON.stringify包了
axios.post("/users", user, {}).then(res => {
console.log(res.data);
}).catch(reason => {
console.log('请求失败:', reason);
})
}
// put
btn3.onclick = async function () {
let user = {
name: "malu666"
};
axios.put("/users/1", user, {}).then(res => {
console.log(res.data);
}).catch(reason => {
console.log('请求失败:', reason);
})
}
// delete
btn4.onclick = async function () {
axios.delete("/users/3").then(res => {
console.log(res.data);
}).catch(reason => {
console.log('请求失败:', reason);
})
}
</script>
</body>
</html>

设置全局配置项,将被应用到每一个要求。

axios.defaults.baseURL = "http://api.example.com";
axios.defaults.timeout = 2000;
axios.defaults.headers = {
token:"abc",
a:1,
b:2
}

Axios创建实例

前面使用的axios,是默认的实例(axios函数),引入axios.js,就有默认的实例。前面我们讲了,可以给这个默认实例做一些配置。当给该实例设置一些默认配置时, 这些配置就被固定下来了,但是后续开发中, 某些配置可能会不太一样,比如,我们要向两台服务器发出请求:

原生AJAX

创建两个实例,前端代码:

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
</head>
<body>
<button id="btn1">获取壁纸</button>
<button id="btn2">每日一言</button>
<script>
const btn1 = document.querySelector("#btn1");
const btn2 = document.querySelector("#btn2");
// 创建axios实例,参数是请求配置对象
const bizhiInstance = axios.create({
baseURL:"https://v2.api-m.com",
timeout: 5000
})
const yiyanInstance = axios.create({
baseURL:"https://tenapi.cn",
timeout: 5000
})
btn1.onclick = async function () {
bizhiInstance.get("/api/wallpaper",{params:{a:1}}).then(res => {
console.log('获取壁纸成功~',res.data);
}) .catch(reason => {
console.log('获取壁纸失败~',reason);
})
}
btn2.onclick = async function () {
yiyanInstance.get("/v2/yiyan",{params:{a:1}}).then(res => {
console.log('获取一言成功~',res);
}) .catch(reason => {
console.log('获取一言失败~',reason);
})
}
</script>
</body>
</html>

Axios取消请求

流程:

  • 请求配置项中配置 cancelToken 对象
  • 用变量保存用于取消请求的 cancel 函数
  • 在后面特定时机调用 cancel 函数实现取消请求
  • 在错误回调中判断如果 error 是 cancel, 做相应处理

取消请求,前端代码:

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
</head>
<body>
<button id="btn1">发送请求</button>
<button id="btn2">取消请求</button>
<script>
const btn1 = document.querySelector("#btn1");
const btn2 = document.querySelector("#btn2");
// 定义定义变量,保存取消请求的函数,当需要取消请求的时候调用该函数
let cancelFn = null;
// 发送请求
btn1.onclick = async function () {
axios.get("https://tenapi.cn/v2/yiyan",{
cancelToken: new axios.CancelToken((cb)=>{
console.log("xxxxxxx")
console.log(cb)  // 取消请求的 cancel 函数
cancelFn = cb;
})
}).then(res => {
console.log('获取一言成功~',res);
}) .catch(reason => {
console.log('获取一言失败~',reason);
})
}
btn2.onclick = async function () {
cancelFn();
}
</script>
</body>
</html>

批量处理响应

前端代码:

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
</head>
<body>
<button id="btn1">发送请求</button>
<script>
const btn1 = document.querySelector("#btn1");
// 全局配置
axios.defaults.baseURL = "https://v2.api-m.com";
btn1.onclick = async function () {
// 写了axios.get,表示请求已经发出去了
const res1 = axios.get("https://v2.api-m.com/api/wallpaper")
const res2 = axios.get("https://v2.api-m.com/api/weather?city=%E5%8C%97%E4%BA%AC")
const res3 = axios.get("https://v2.api-m.com/api/music163hot")
// all是用于处理多个响应的
axios.all([res1,res2,res3]).then(res=>{
// console.log('成功获取数据!',res);
let [bzData,tqData,yyData] = res;
console.log(bzData.data)
console.log(tqData.data)
console.log(yyData.data)
}).catch(reason => {
console.log('失败!', reason);
});
}
</script>
</body>
</html>

拦截器

axios的也可以设置拦截器:拦截每次请求和响应

  • axios.interceptors.request.use(请求成功拦截, 请求失败拦截)
  • axios.interceptors.response.use(响应成功拦截, 响应失败拦截)

原生AJAX

前端代码:

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
<script src="https://cdn.bootcdn.net/ajax/libs/nprogress/0.2.0/nprogress.min.js"></script>
<link href="https://cdn.bootcdn.net/ajax/libs/nprogress/0.2.0/nprogress.min.css" rel="stylesheet">
</head>
<body>
<button id="btn1">发送请求</button>
<script>
const btn1 = document.querySelector("#btn1");
// 请求拦截器  config是配置对象
axios.interceptors.request.use(config=>{
// 表示拦截后,给请求头上添加一个a
config.headers.a = 1;
NProgress.start(); // 显示进度条
// 放行
return config;
})
// 响应拦截器
axios.interceptors.response.use(response=>{
NProgress.done(); // 关闭进度条
return response;
},err=>{
// 失败的回调,直接响应失败的promsie
return Promise.reject(err)
})
btn1.onclick = async function () {
axios.get("http://localhost:3000/users").then(res=>{
console.log('成功获取数据!',res.data);
}).catch(reason => {
console.log('失败!', reason);
});
}
</script>
</body>
</html>

原文链接:https://juejin.cn/post/7358274599883554816 作者:yaelyuki

(0)
上一篇 2024年4月17日 上午10:16
下一篇 2024年4月17日 上午10:27

相关推荐

发表回复

登录后才能评论