Ajax原理-XMLHttpRequest

1. XMLHttpRequest

是什么?

和axios的关系:

axios 内部采用 XMLHttpRequest 与服务器交互

学习XMLHttpRequest的目的:

掌握使用 XHR 与服务器进行数据交互,了解 axios 内部原理,加强对知识的理解,提升技术认知。

1.1. 基本语法

步骤:

  1. 创建 XMLHttpRequest 对象
  2. 配置请求方法和请求 url 地址
  3. 监听 loadend 事件接收响应结果
  4. 发起请求
javascript 复制代码
// 1. 创建 XMLHttpRequest 对象
const xhr = new XMLHttpRequest()
// 2. 配置请求方法和请求 url 地址
xhr.open('请求方法','请求url网址')
// 3. 监听 loadend 事件接收响应结果
xhr.addEventListener('loadend', () => {
//响应结果
console.log(xhr.response)
})

// 4. 发起请求
xhr.send()

1.1.1. 获取省份案例

需求:获取并展示所有省份名字

接口地址

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>
</head>
<body>

    <div class="data"></div>
    
    <script>
        // 1. 实例化XHR对象
        const xhr = new XMLHttpRequest()

        // 2. open方法设置请求方法和url路径
        xhr.open('GET','https://hmajax.itheima.net/api/province')

        // 3. 设定接收服务器的响应事件  loadend
        // loadend是XMLHttpRequest的一个事件,当服务器响应了不论是否成功都触发这个事件
        xhr.addEventListener('loadend',function(){
            // 获取服务器响应的结果(响应体的数据 xhr.response是一个字符串(JSON) )
            console.log(xhr.response)
            // 如何判断是成功还是失败的响应: xhr.status >=200 && xhr.status<300
            // console.log(xhr.status)
            if(xhr.status >=200 && xhr.status<300){
                // 成功
               let jsObj = JSON.parse(xhr.response)
               console.log(jsObj);

            //  其他的逻辑写在这里。。。。
            document.querySelector('.data').innerHTML = jsObj.list.join('<br>')
            }else{
                // 失败
            }            
        })

        // 4. send()发送请求
        xhr.send()

    </script>
</body>
</html>

1.2. 查询参数

**定义:**浏览器提供给服务器的额外信息,让服务器返回浏览器想要的数据

语法: XXXX参数名1=值1 & 参数名2=值2

多个参数之间 用 & 符号分隔的键/值对列表

如何使用XMLHttpRequest发送携带参数的请求?

javascript 复制代码
// 1. 创建 XMLHttpRequest 对象
const xhr = new XMLHttpRequest()
// 2. 配置请求方法和请求 url 地址
xhr.open('请求方法','请求url网址?key1=value1&key2=value2&.....')
// 3. 监听 loadend 事件接收响应结果
xhr.addEventListener('loadend', () => {
//响应结果
console.log(xhr.response)
})

// 4. 发起请求
xhr.send()

1.2.1. 查询地区

需求:输入【省份】和【城市】名字点击查询按钮,获得地区列表

接口地址

开发步骤:

html 复制代码
<!DOCTYPE html>
<html lang="zh-CN">

<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>案例_地区查询</title>
    <link href="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/5.1.3/css/bootstrap.min.css" rel="stylesheet">
    <style>
        :root {
            font-size: 15px;
        }

        body {
            padding-top: 15px;
        }
    </style>
</head>

<body>
    <div class="container">
        <form id="editForm" class="row">
            <!-- 输入省份名字 -->
            <div class="mb-3 col">
                <label class="form-label">省份名字</label>
                <input type="text" value="广东省" name="province" class="form-control province" placeholder="请输入省份名称" />
            </div>
            <!-- 输入城市名字 -->
            <div class="mb-3 col">
                <label class="form-label">城市名字</label>
                <input type="text" value="深圳市" name="city" class="form-control city" placeholder="请输入城市名称" />
            </div>
        </form>
        <button type="button" class="btn btn-primary sel-btn">查询</button>
        <br><br>
        <p>地区列表: </p>
        <ul class="list-group area-group">
            <!-- 示例地区 -->
            <li class="list-group-item">东城区</li>
        </ul>
    </div>
    <script>

        document.querySelector('button').addEventListener('click', function () {
            let pname = document.querySelector('.form-control.province').value
            let cname = document.querySelector('.form-control.city').value

            const xhr = new XMLHttpRequest()
            // ✨✨✨带参数请求的核心代码就是在url后面使用?key=value&key1=value1传参
            xhr.open('GET', `https://hmajax.itheima.net/api/area?pname=${pname}&cname=${cname}`)
            xhr.addEventListener('loadend', function () {
                if (xhr.status >= 200 && xhr.status < 300) {

                    // ✨✨✨将xhr.response 这个json字符串转成对象
                    let jsObj = JSON.parse(xhr.response)
                    console.log(jsObj);

                    let str = ''
                    jsObj.list.forEach(item => {
                        str += `<li class="list-group-item">${item}</li>`
                    })

                    document.querySelector('.list-group').innerHTML = str

                }
            })

            xhr.send()
        })

    </script>
</body>

</html>

1.3. 提交数据(POST)

**场景:**将页面表单数据提交给服务器,例如:登录,注册,密码修改,新增,编辑等业务

XMLHttpRequest数据提交核心步骤:

  1. 请求头设置Content-Type
  2. 请求体携带符合要求的数据

举例:如下注册账号接口

  1. 请求头设置Content-Type: application/json
  2. 请求体携带 JSON 字符串

核心代码:

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>
</head>
<body>
    
    <script src="./lib/axios.js"></script>
    <script>
        // axios({
        //     url:'https://hmajax.itheima.net/api/register',
        //     method:'POST',
        //     data:{
        //         username:'itheima524',
        //         password:'12345678'
        //     }
        // })
        // .then(res=>{
        //     console.log(res.data)            
        // })

        const xhr = new XMLHttpRequest()

        xhr.open('POST','https://hmajax.itheima.net/api/register')

        xhr.addEventListener('loadend',function(){
            if(xhr.status>=200 && xhr.status <300){
                console.log(xhr.response)                
            }
        })

        // 在发送之前增加请求报文头Content-Type
        xhr.setRequestHeader('Content-Type','application/json')
        // xhr.setRequestHeader('Content-Type','application/x-www-form-urlencoded')
        
        // 准备请求体数据
        let userInfo = {username:'itheima536',password:'12345678'}
        //✨✨ 注意点:由于Contet-type是一个application/json,所以必须将对象转换成json字符串发送
        let userInfoBody = JSON.stringify(userInfo)

        // ✨✨ send()里面如果带了参数,表示向请求报文体中增加数据发送给服务器
        // get请求是无需带参数的,只有POST,PUT,PATCH这些方法才带参数
        xhr.send(userInfoBody)
        // xhr.send('username=itheima536&password=12345678')


    </script>
</body>
</html>

1.4. 文件上传

  1. 实例化FormData
  2. 使用append方法根据接口文档添加参数
  3. send方法发送formdata对象
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>
</head>
<body>
    <input type="file" class="img">
    
    <script>

        document.querySelector('.img').addEventListener('change',function(){

            // 1. 获取选择的图片
            let file = this.files[0]

            // 2. 实例化FormData对象
            const fd = new FormData()
            fd.append('img',file)

            const xhr = new XMLHttpRequest()
            xhr.open('POST','https://hmajax.itheima.net/api/uploadimg')
            xhr.addEventListener('loadend',function(){
                if(xhr.status >=200 && xhr.status<300){
                    console.log(xhr.response)                    
                }else{
                    alert('失败')
                }
            })

            // 3. 将formData对象提交给服务器
            xhr.send(fd)
        })


    </script>
</body>
</html>

1.5. XHR总结

XHR是什么? 浏览器提供的一个和服务器通讯的对象(XMLHttpReqeust)

和axios的关系是什么?axios库里面针对XHR来进行封装的

面试官:请手写一个和服务器通讯的ajax代码,不允许使用axios

核心步骤:

  1. 创建对象var xhr = new XMLHttpRequest();
  2. 配置请求 xhr.open('GET', 'URL');
  3. 处理响应
    • 设置状态改变时的回调函数:xhr.addEventListener('loadend',function(){})
    • 检查 xhr.status >= 200 && xhr.status < 300 确认成功。
  1. 发送请求xhr.send();
  • GET请求核心要素
    • 修改open方法第一个参数为GET,第二个参数表示请求的url地址
      • URL地址如果需要带参数-> ?key=value&key1=value1
  • POST请求的核心要素
    • 在发送请求之前使用xhr.setReqeustHeader()设置Contnet-Type请求头
      • applicaton/json -> json字符串
      • application/x-www-form-urlencoded -> key=value&key1=value1
    • 在send方法中传入符合Content-type格式要求的数据
  • 文件上传的核心要素
    • 配合<input type="file">这个元素来实现的
    • 实例化FormData 以及使用append方法追加数据
    • send()方法将formdata对象发生给服务器
    • 注意点:文件上传不需要手动设置Contnet-Type

2. Promise + XHR 实现myAxios获取省份列表

目标:通过 Promise+XHR模拟axios函数的封装,体验axios内部的实现过程

需求:

  1. 使用 Promise 管理 XHR ,模拟实现一个简单版的myAxios
  2. 使用myAxios访问:http://hmajax.itheima.net/api/province 获取省份数据

实现步骤:

  1. 创建myAxios函数,内部使用返回一个 new Promise((resolve,reject)=>{ })
  2. 执行 XHR 异步代码,获取省份列表 接口文档
  3. 请求成功(xhr.status >= 200 && xhr.status < 300)或失败函数,做后续处理
    1. resolve(成功的数据)
    2. reject(失败数据)
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>
</head>

<body>

    <!-- <script src="./lib/axios.js"></script> -->
    <script>
        /* myAxios()
        .then()
        .catch()

        拆解:
        1. 构造出 能 .then().catch()
        2. 在Promise中通过XHR去和服务器通信
        3. 服务器响应的成功或者失败结果,由Promise中的resolve和reject交出去
        */
        function myAxios() {
            return new Promise((resolve, reject) => {
                // resolve(100)
                // // reject('错误:100')
                // 借助XHR来发送请求和响应
                const xhr = new XMLHttpRequest()
                xhr.open('GET','https://hmajax.itheima.net/api/city?pname=广东省')
                xhr.addEventListener('loadend',function(){
                    if(xhr.status >=200 && xhr.status <300){
                        // 成功
                        resolve(xhr.response)
                    }else{
                        reject('失败')
                    }
                })
                xhr.send()
            })
        }

        myAxios()
        .then(res=>{
            console.log(res);            
        })
        .catch(err=>{
            console.log(err);
        })

        // async function call() {
        //     try {
        //         let res = await myAxios()
        //         console.log(res);

        //     } catch (err) {
        //         console.log(err);
        //     }
        // }

        // call()

    </script>
</body>

</html>
相关推荐
午后书香8 分钟前
一天三场面试,口干舌燥要晕倒(二)
前端·javascript·面试
Book_熬夜!24 分钟前
CSS—补充:CSS计数器、单位、@media媒体查询
前端·css·html·媒体
程序员大澈25 分钟前
1个基于 Three.js 的 Vue3 组件库
javascript·vue.js
程序员大澈31 分钟前
3个 Vue Scoped 的核心原理
javascript·vue.js
hyyyyy!34 分钟前
《原型链的故事:JavaScript 对象模型的秘密》
javascript·原型模式
程序员大澈43 分钟前
3个好玩且免费的api接口
javascript·vue.js
程序员大澈1 小时前
4个 Vue 路由实现的过程
javascript·vue.js·uni-app
几度泥的菜花1 小时前
如何禁用移动端页面的多点触控和手势缩放
前端·javascript
狼性书生1 小时前
electron + vue3 + vite 渲染进程到主进程的双向通信
前端·javascript·electron
肥肠可耐的西西公主2 小时前
前端(AJAX)学习笔记(CLASS 4):进阶
前端·笔记·学习