Ajax-1

同步与异步

同步代码

  • 逐行执行,需要原地等待结果后,才继续向下执行

异步代码

  • 调用后耗时,不阻塞代码继续执行(不必原地等待),在将来完成后触发一个回调函数

认识 AJAX

什么是 AJAX

  • AJAX 是 异步的JavaScript和XML [Asynchronous JavaScript And XML]
  • 简单点说,就是使用XMLHttpRequest对象与服务器通信
  • 它可以使用 JSON, XML, HTML 和 text 文本等格式发送和接收数据
  • AJAX 最吸引人的是它"异步"的特性,也就是说它能在不刷新页面的情况下与服务器进行,交互数据,或更新页面

如何使用 AJAX

  • 先学会使用axios库,与服务器进行数据交互
  • 再学习XMLHttpRequest对象的使用,了解 AJAX的底层原理

使用 axios

基本使用

语法:
	1.引入 axios.js:https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js
	2.使用 axios 函数:
		2.1 传入配置对象
		2.2 使用 .then 回调函数接收结果,并做后续处理
<body>
    
    <script src="https://unpkg.com/axios/dist/axios.min.js">script>
    <script>
        axios({
            url: 'http://datavmap-public.oss-cn-hangzhou.aliyuncs.com/areas/csv/100000_province.json',
        }).then((result) => { console.log(result.data.rows) })
    script>
body>

核心配置

请求方法


    
    

查询参数

<body>
    <ul>ul>

    <script src="https://unpkg.com/axios/dist/axios.min.js">script>
    <script>
        axios({
            url: 'https://hmajax.itheima.net/api/city',
            method: 'get',
            params: {
                pname: prompt('请输入省份:')
            },
        }).then((result) => {
            // 筛选有效的数据
            cityArr = result.data.list
            // 将数据展示到页面上
            const str = cityArr.map(ele => `
  • ${ele}
  • `
    ).join('') document.querySelector('ul').innerHTML = str })
    script> body>

    提交数据

    <body>
        <script src="https://unpkg.com/axios/dist/axios.min.js">script>
        <script>
            axios({
                url: 'https://hmajax.itheima.net/api/register',
                method: 'post',
                // 要提交的数据
                data: {
                    username: prompt('请输入注册账号'),
                    password: prompt('请输入密码')
                }
            }).then((result) => {
                alert(result.data.message)
            })
        script>
    body>
    

    错误处理

    处理格式

        <script>
            axios({
                //请求选项
            }).then(() => {
                // 处理数据
            }).catch(error => {
                // 处理错误
            })
        script>
    

    具体演示

    <body>
        <div>
            <label>注册账号:<input type="text">label><br>
            <label>填写密码:<input type="password">label>
        div>
        <button>注册button>
    
        <script src="https://unpkg.com/axios/dist/axios.min.js">script>
        <script>
            const input = document.querySelector('input[type=text]')
            const password = document.querySelector('input[type=password]')
            const button = document.querySelector('button')
    
            button.addEventListener('click', () => {
                
                axios({
                    url: 'https://hmajax.itheima.net/api/register',
                    method: 'post',
                    // 要提交的数据
                    data: {
                        username: input.value,
                        password: password.value
                    }
                }).then((result) => {
                    alert(result.data.message)
                }).catch(error => {
                    alert(error.response.data.message)
                })
    
            })
        script>
    body>
    

    XMLHttpRequest

    认识 XHR

    • XHRXMLHttpRequest
    • XMLHttpRequest 对象用于与服务器交互
    • 通过 XMLHttpRequest 可以在不刷新页面的情况下,请求特定URL,获取数据
    • 这允许网页在不影响用户操作的情况下,更新页面的局部内容
    • XMLHttpRequest 在 AJAX 编程中被大量使用
    • 我们使用的 axios 是一个封装好的库,它的底层使用 XMLHttpRequest 与服务器交互

    基本使用

    <body>
        <ul>ul>
    
        <script>
            // 1. 创建XMLHttpRequset对象
            const xhr = new XMLHttpRequest()
            // 2. 配置请求方法和URL地址
            xhr.open('GET', 'https://hmajax.itheima.net/api/province')
            // 3. 提前准备好一个监听loadend事件,用来接收响应结果
            xhr.addEventListener('loadend', () => {
                // 数据处理与筛选
                const result = JSON.parse(xhr.response)
                const arr = result.list
                // 将数据渲染到页面
                document.querySelector('ul').innerHTML =
                    arr.map(ele => `
  • ${ele}
  • `
    ).join('') }) // 4.向服务端发送请求 xhr.send()
    script> body>

    查询参数

    <body>
        <ul>ul>
    
        <script>
            // 目标:使用XHR携带查询参数
            const xhr = new XMLHttpRequest()
            const goal = prompt('您要搜索的城市:')
            xhr.open('GET', `https://hmajax.itheima.net/api/city?pname=${goal}`)
            xhr.addEventListener('loadend', () => {
                const result = JSON.parse(xhr.response
                const arr = result.list
                // 将数据渲染到页面
                document.querySelector('ul').innerHTML =
                    arr.map(ele => `
  • ${ele}
  • `
    ).join('') }) xhr.send()
    script> body>

    数据提交

    <body>
        <button class="reg-btn">注册用户button>
    
        <script>
            // 目标:使用XHR实现数据提交,完成注册功能
            document.querySelector('.reg-btn').addEventListener('click', () => {
                const xhr = new XMLHttpRequest()
                xhr.open('POST', 'https://hmajax.itheima.net/api/register')
                xhr.addEventListener('loadend', () => {
                    const result = JSON.parse(xhr.response)
                    console.log(result)
                })
                
                // xhr设置请求头:告诉服务器内容类型(JSON字符串)
                xhr.setRequestHeader('Content-Type', 'application/json')
                // 准备要提交的数据
                const dataObj = {
                    username: 'jackJack',
                    password: '123456'
                }
                const dataStr = JSON.stringify(dataObj)     // 将数据对象转为JSON字符串
                // 设置好"请求体",并发送请求
                xhr.send(dataStr)
            })
        script>
    body>
    

    Promise

    基本认识

    Promise 对象用于表示一个异步操作的最终完成(或失败)的结果

        <script>
            // 1.创建Promise对象
            const p = new Promise((resolve, reject) => {
                
                // 2.执行异步任务
                // 任务执行成功,就调用:resolve(...) 从而触发 then() 执行
                // 任务执行失败,就调用:reject(...) 从而触发 catch() 执行
            })
    
            // 3.接收结果
            p.then(result => {
                // 成功
            }).catch(error => {
                // 失败
            })
        script>
    

    Promise 对象有三种状态

    • 待定状态------pending
    • 已兑现状态—fulfilled
    • 已拒绝状态—rejected
        <script>
            // 1.创建Promise对象(此时Promise对象处于pending状态,即:待定状态)
            const p = new Promise((resolve, reject) => {
    
                // Promise对象创建时,这里的代码都会执行了
                console.log('是这里先执行吗?');
    
                setTimeout(() => {
                    
                    resolve('模拟AJAX请求:结果成功!')   // 当执行完resolve(),此时Promise对象处于fulfilled状态,即:已兑现状态
    
    
                    // reject(new Error('模拟AJAX请求:结果失败!'))    // 当执行完reject(),此时Promise对象处于rejected状态,即:已拒绝状态
    
                }, 2000)
            })
            console.log('还是说,这里先执行呢?')
    
            // 3.获取结果
            p.then(result => {
                console.log(result);
            }).catch(error => {
                console.log(error);
            })
        script>
    

    具体使用

    需求:使用 Promise + XHR 获取省份列表

    <body>
        <ul>ul>
    
        <script>
            // 1. 创建Promise对象
            const p = new Promise((resolve, reject) => {
                // 2. 执行XHR异步代码,获取省份列表
                const xhr = new XMLHttpRequest()
                xhr.open('GET', 'https://hmajax.itheima.net/api/province')
                xhr.addEventListener('loadend', () => {
                    // console.log(xhr.status)  // 响应状态码
                    if (xhr.status >= 200 && xhr.status < 300) {
                        resolve(JSON.parse(xhr.response))
                    } else {
                        reject(new Error(xhr.response))
                    }
                })
                xhr.send()
            })
    
            // 正常要执行的代码(非异步的)
            console.log('假设这里')
            console.log('有着很多很多的')
            console.log('代码需要执行')
            console.log('-------------')
    
            // 3. 返回结果
            p.then(result => {
                // 先给用户提前告知一声结果
                alert('省份数据获取成功!')
                // 然后将数据渲染到页面上
                arr = result.list
                document.querySelector('ul').innerHTML =
                    arr.map(ele => `
  • ${ele}
  • `
    ).join('') }).catch(error => { // 先给用户提前告知一声结果 alert('省份数据获取失败!') // 错误对象要用console.dir详细打印 console.dir(error) // 并且将关键的错误信息,显示到页面给用户看 document.querySelector('ul').innerHTML = `${error.message}` }) // 正常要执行的代码(非异步的) console.log('假设这里') console.log('有着很多很多的') console.log('代码需要执行') console.log('-------------')
    script> body>

    回调函数地狱

    概念:在回调函数中嵌套回调函数,一直嵌套下去形成了回调函数地狱

    缺点:

    • 代码可读性差
    • 异常无法捕获
    • 耦合性严重,牵一发动全身

    例子:如下

    <body>
        <div>
            <div class="province">div>
            <div class="city">div>
            <div class="area">div>
        div>
        
        <script src="https://unpkg.com/axios/dist/axios.min.js">script>
        <script>
            // 获取省份
            axios({ url: 'http://hmajax.itheima.net/api/province' }).then(result => {
                const pname = result.data.list[0]
                document.querySelector('.province').innerHTML = pname
                // 获取城市
                axios({ url: 'http://hmajax.itheima.net/api/city', params: { pname: pname } }).then(result => {
                    const cname = result.data.list[0]
                    document.querySelector('.city').innerHTML = cname
                    // 获取地区
                    axios({ url: 'http://hmajax.itheima.net/api/area', params: { pname: pname, cname: cname } }).then(result => {
                        const area = result.data.list[0]
                        document.querySelector('.area').innerHTML = area
                    })
                })
            })
        script>
    body>
    

    链式调用

    基本认识

    • 一个 Promise对象 在它的 then() 方法 里可以 return 一个 新的 Promise对象

    • 利用好这个特性,就能串联下一个任务,实现链式调用

    <body>
        <script src="https://unpkg.com/axios/dist/axios.min.js">script>
        <script>
            const p1 = new Promise((resolve, reject) => {
                // 模拟请求
                setTimeout(() => {
                    resolve('湖北省')   // 2秒之后服务器成功返回了数据:"湖北省",通过resolve()传递给then()
                }, 2000)
            })
    
            const p2 = p1.then(result => {
                console.log(result)     // 输出传递过来的结果
                return new Promise((resolve, reject) => {
                    // 模拟请求
                    setTimeout(() => {
                        resolve('武汉市')   // 2秒之后服务器成功返回了数据:"武汉市",通过resolve()传递给then()
                    }, 2000)
                })
            })
    
            const p3 = p2.then(result => {
                console.log(result) // 输出传递过来的结果
                return new Promise((resolve, reject) => {
                    // 模拟请求
                    setTimeout(() => {
                        resolve('武昌区')   // 2秒之后服务器成功返回了数据:"武昌区",通过resolve()传递给then()
                    }, 2000)
                })
            })
    
            p3.then(result => {
                console.log(result) // 输出传递过来的结果
            })
        script>
    body>
    

    解决回调函数地狱

    <body>
        <ul>ul>
    
        <script src="https://unpkg.com/axios/dist/axios.min.js">script>
        <script>
            // 防止变量污染
            (function () {
    
                // 定义变量,用来存储数据
                let province
                let city
                let area
    
                // 利用"链式调用"解决"回调函数地狱"
                let obj = axios({ url: 'http://hmajax.itheima.net/api/province' })  // 请求获取省份数据
    
                obj = obj.then(result => {
                    console.log(result)     // 输出省份数据
                    province = result.data.list[18]
                    return axios({ url: 'http://hmajax.itheima.net/api/city', params: { pname: province } })  // 请求获取城市数据
                })
    
                obj = obj.then(result => {
                    console.log(result)     // 输出城市数据
                    city = result.data.list[0]
                    return axios({ url: 'http://hmajax.itheima.net/api/area', params: { pname: province, cname: city } })  // 请求获取地区数据
                })
    
                obj.then(result => {
                    console.log(result)     // 输出地区数据
                    area = result.data.list[0]
    
                    // 将数据显示到页面上
                    document.querySelector('ul').innerHTML = `
  • ${province}
  • ${city}
  • ${area}
  • `
    }) })()
    script> body>

    Promise.all 静态方法

    基本认识

    概念:合并多个Promise对象,等待所有的Promise对象完成,才进行后续逻辑

    • 所有的Promise对象都成功,全体才算成功
    • 任意一个Promise对象失败了,全体都失败了

    语法:如下

        <script>
            const p = Promise.all([Promise对象, Promise对象, ...])
            p.then(result => {
                // result结果: [Promise对象成功的结果, Promise对象成功的结果, ...]
            }).catch(error => {
                // 最先失败的Promise对象,抛出的异常
            })
        script>
    

    具体演示

    <body>
        <ul>ul>
    
        <script src="https://unpkg.com/axios/dist/axios.min.js">script>
        <script>
            // 目标:实现将北京、上海、广州、深圳的天气同时展示到页面上
    
            // 1. 发起请求,得到Promise对象
            const bjPromise = axios({ url: 'http://hmajax.itheima.net/api/weather', params: { city: '110100' } })   // 北京Promise对象
            const shPromise = axios({ url: 'http://hmajax.itheima.net/api/weather', params: { city: '310100' } })   // 上海Promise对象
            const gzPromise = axios({ url: 'http://hmajax.itheima.net/api/weather', params: { city: '440100' } })   // 广州Promise对象
            const szPromise = axios({ url: 'http://hmajax.itheima.net/api/weather', params: { city: '440300' } })   // 深圳Promise对象
    
            // 2. 使用Promise.all,合并多个Promise对象
            const p = Promise.all([bjPromise, shPromise, gzPromise, szPromise])
            p.then(result => {
                console.log(result)
                // 将数据渲染到页面上
                document.querySelector('ul').innerHTML =
                    result.map(ele => {
                        const info = ele.data.data
                        return `
  • [地区: ${info.area}]-[时间: ${info.date}]-[天气: ${info.weather}]
  • `
    }).join('') }).catch(error => { console.dir(error) })
    script> body>

    简易版 axios (过程)

    需求:基于 Promise + XHR 封装 myAxios 函数

    • 回顾 axios 的功能
    <body>
        <ul>ul>
        
        <script src="https://unpkg.com/axios/dist/axios.min.js">script>
        <script>
            axios({
    
                url: 'https://hmajax.itheima.net/api/province',
                method: 'get'
    
            }).then(result => {
    
                // console.log(result)             // 返回数据对象
                // console.log(result.data.list)   // 筛选有效信息
                
                // 将数据渲染到页面
                const arr = result.list
                document.querySelector('ul').innerHTML =
                    arr.map(ele => `
  • ${ele}
  • `
    ).join('') }).catch(error => { console.log(error) })
    script> body>
    • 制作简易版的 axios
    <body>
        <script>
            function myAxios(configObj) {
                // ------------函数体--------------
    
                // 函数第一步:创建Promise对象
                const p = new Promise((resolve, reject) => {
    
                    // ----------------异步代码---------------------
    
                    // 1.1 判断configObj是否有params属性
                    if (configObj.params) {
                        // 1.2 使用URLSearchParams将"查询参数的对象"转换成"符合查询参数格式的字符串"
                        const paramsObj = new URLSearchParams(configObj.params)
                        const queryString = paramsObj.toString()
                        // 1.3 把查询参数字符串,拼接到URL后面
                        configObj.url += `?${queryString}`
                    }
    
                    // 2.1 准备好一个用于与服务器连接的xhr对象
                    const xhr = new XMLHttpRequest()
                    // 2.2 给xhr配置相关信息
                    xhr.open(configObj.method, configObj.url)
                    xhr.addEventListener('loadend', () => {
                        if (xhr.status >= 200 && xhr.status < 300) {
                            resolve(JSON.parse(xhr.response))
                        } else {
                            reject(new Error(xhr))
                        }
                    })
    
                    // 3.1 判断是否有data选项
                    if (configObj.data) {
                        // 3.2 转换数据类型,准备好"数据体"
                        const jsonStr = JSON.stringify(configObj.data)
                        // 3.3 配置好"数据头"
                        xhr.setRequestHeader('Content-Type', 'application/json')
                        // 3.4 携带数据,向服务器发送"请求"
                        xhr.send(jsonStr)
                    } else {
                        // 3.5 如果没有data选项,直接发送"请求"即可
                        xhr.send()
                    }
    
                    // ----------------异步代码---------------------
                })
    
                // 函数第二步:返回Promise对象
                return p
    
                // ------------函数体--------------
            }
        script>
    body>
    

    简易版 axios (模板)

    为了方便日后使用简易版的 axios,现将它简化压缩成下面代码(用法不变)

        function simpleAxios(config) {
            return new Promise((resolve, reject) => {
                config.params && (config.url += `?${new URLSearchParams(config.params).toString()}`)
                const xhr = new XMLHttpRequest()
                xhr.open(config.method, config.url)
                xhr.addEventListener('loadend', () => {
                    if (xhr.status >= 200 && xhr.status < 300) { resolve(JSON.parse(xhr.response)) }
                    else { reject(new Error(xhr.response)) }
                })
                if (config.data) {
                    xhr.setRequestHeader('Content-Type', 'application/json')
                    xhr.send(JSON.stringify(config.data))
                } else { xhr.send() }
            })
        }
    

    新方法实现异步

    async 与 await

    <body>
        <div>
            <div class="province">div>
            <div class="city">div>
            <div class="area">div>
        div>
    
        <script src="https://unpkg.com/axios/dist/axios.min.js">script>
        <script>
            // 1. 定义 async 修饰函数
            async function getData() {
                // 1.1. await 等待 Promise 对象成功的结果
                // await 必须用在 async 修饰的函数内(await会阻止"异步函数内"代码继续执行,原地等待结果)
    
                const pObj = await axios({ url: 'http://hmajax.itheima.net/api/province' })
                const province = pObj.data.list[0]
    
                const cObj = await axios({ url: 'http://hmajax.itheima.net/api/city', params: { pname: province } })
                const city = cObj.data.list[0]
    
                const aObj = await axios({ url: 'http://hmajax.itheima.net/api/area', params: { pname: province, cname: city } })
                const area = aObj.data.list[0]
    
                // 1.2 将数据渲染到页面上
                document.querySelector('.province').innerHTML = province
                document.querySelector('.city').innerHTML = city
                document.querySelector('.area').innerHTML = area
            }
    
            // 2. 调用函数
            getData()
        script>
    body>
    

    错误捕获

    <body>
        <div>
            <div class="province">div>
            <div class="city">div>
            <div class="area">div>
        div>
    
        <script src="https://unpkg.com/axios/dist/axios.min.js">script>
        <script>
            async function getData() {
                try {
                    const pObj = await axios({ url: 'http://hmajax.itheima.net/api/province' })
                    const province = pObj.data.list[0]
    
                    const cObj = await axios({ url: 'http://hmajax.itheima.net/api/city', params: { pname: province } })
                    const city = cObj.data.list[0]
    
                    const aObj = await axios({ url: 'http://hmajax.itheima.net/api/area', params: { pname: province, cname: city } })
                    const area = aObj.data.list[0]
    
                    document.querySelector('.province').innerHTML = province
                    document.querySelector('.city').innerHTML = city
                    document.querySelector('.area').innerHTML = area
    
                } catch (error) {
                    console.dir(error)
                }
            }
    
            // 调用函数
            getData()
        script>
    body>
    

    你可能感兴趣的:(#,Ajax,ajax)