目录
一、AJAX原理 —— XMLHttpRequest
1.1 使用XMLHttpRequest
二、 XMLHttpRequest - 查询参数 (就是往服务器后面拼接要查询的字符串)
三、 地区查询
四、 XMLHttpRequest - 数据提交
五、 认识Promise
5.1 为什么 JavaScript 需要异步?
5.2 Promiss - 三种状态
5.3 使用Promiss + XHM 获取省份列表
六、 封装简易的axios——获取省份列表
七、 注册账号——支持传递请求体数据 data选项
总结不易~ 本章节对我有很大的收获,希望对你也是!!!
本节素材已上传至Gitee:https://gitee.com/liu-yihao-hhh/ajax_studyhttps://gitee.com/liu-yihao-hhh/ajax_study
一、AJAX原理 —— XMLHttpRequest
定义
XMLHttpRequest(XHR) 对象用于与服务器交互。通过XMLHttpRequst可以再不刷新页面的情况下请求特定URL,获取数据。这允许网页在不影响用户操作的情况下,更新页面的局部内容。XMLHttpRequest在AJAX编程中被大量使用。
与axios的关系: axios内部采用XMLHttpRequest 与服务器交互
1.1 使用XMLHttpRequest
- 创建XMLHttpRequest对象
- 配置请求方法和请求url地址
- 监听loadend事件,接受响应结果
- 发起请求
const xhr = new XMLHttpRequest()xhr.open('请求方法', '请求url网址')xhr.addEventListener('loadend', () => {// 接受 - 响应结果console.log(xhr.response)})// 发送 - 请求xhr.send()
XMLHttpRequest基础使用,获取服务器的数据并展示
// 1. 创建一个XMR对象const xhr = new XMLHttpRequest()// 2. 配置请求方法和请求url地址xhr.open('GET', 'http://hmajax.itheima.net/api/province')// 3. 监听 loadend事件 接收响应结果xhr.addEventListener('loadend', () => {console.log(xhr.response) // 这里返回的是json字符串// json字符串转对象const data = JSON.parse(xhr.response)console.log(data.list.join('<br>')) // 数组转字符串进行拼接document.querySelector('.my-p').innerHTML = data.list.join('<br>')})// 4. 发起请求xhr.send()
二、 XMLHttpRequest - 查询参数 (就是往服务器后面拼接要查询的字符串)
定义: 浏览器提供给服务器的额外信息, 让服务器返回浏览器想要的数据
/*** 目标:使用XHR携带查询参数,展示某个省下属的城市列表*/const xhr = new XMLHttpRequest()// 进行查询 往服务器后面进行拼接查询的参数名 ?pname=xhr.open('GET', 'http://hmajax.itheima.net/api/city?pname=辽宁省')// loadend 加载结束事件xhr.addEventListener('loadend', () => {console.log(xhr.response)const data = JSON.parse(xhr.response)console.log(data.list)document.querySelector('.city-p').innerHTML = data.list.join('<br>')})xhr.send()
三、 地区查询
这一个案例就是要我们同时传入两个参数,但是我们不方便自己获取两个参数后拼接到url后面,这个时候,我们就可以采用浏览器内置的构造函数URLSearchParams 来创建一个对象,里面放入我们需要传入URL的对象参数,他就会自动给我们返回一个url编码的字符串,我们就可以直接在url后面进行拼接 ?+编码即可
/*** 目标: 根据省份和城市名字, 查询对应的地区列表*/// 1. 给查询按钮绑定一个点击事件document.querySelector('.sel-btn').addEventListener('click', () => {// 2. 收集省份和城市名字const pname = document.querySelector('.province').valueconst cname = document.querySelector('.city').value// 3. 组织查询参数字符串const qObj = {pname,cname}// 查询参数对象 -》 查询参数字符串// 浏览器内置的构造函数// 1. 创建 URLSearchParams 对象 自动将我们需要的多个查询对象转换成一个字符串 好方便后续的拼接const paramsObj = new URLSearchParams(qObj)const queryString = paramsObj.toString()console.log(queryString) // 拿到一个url编码 pname=%E6%B9%96%E5%8C%97&cname=%E6%AD%A6%E6%B1%89%E5%B8%82 // 4. 使用XHR对象, 查询地区列表const xhr = new XMLHttpRequest()xhr.open('GET', `http://hmajax.itheima.net/api/area?${queryString}`)// loadend 加载结束事件xhr.addEventListener('loadend', () => {console.log(xhr.response)const data = JSON.parse(xhr.response)console.log(data)const htmlStr = data.list.map(item => {return `<li class="list-group-item">${item}</li>`})console.log(htmlStr)const html = htmlStr.join('<br>')document.querySelector('.list-group').innerHTML = html})xhr.send()})
四、 XMLHttpRequest - 数据提交
需求: 通过XHR提交用户名和密码,完成注册功能
核心: 请求头设置Content-Type:application/json
请求体携带JSON字符串
提交数据就是在xhr.send()中进行提交,但是要提前设置请求头,告诉服务器我们提交的数据是JSON数据
xhr.setRequestHeader('Content-Type', 'application/json')
准备好要进行提交的数据之后,就将该数据转换成JSON字符串直接进行提交即可!
/*** 目标:使用xhr进行数据提交-完成注册功能*/// 后端的数据提交 http://hmajax.itheima.net/api/register// 请求参数 body application/jsondocument.querySelector('.reg-btn').addEventListener('click', () => {const xhr = new XMLHttpRequest()xhr.open('POST', 'http://hmajax.itheima.net/api/register')xhr.addEventListener('loadend', () => {console.log(xhr.response)})// 设置请求头 - 告诉服务器内容类型(JSON字符串)xhr.setRequestHeader('Content-Type', 'application/json')// 准备提交的数据const userObj = {username: '我是hhhhha',password: '11111111'}const userStr = JSON.stringify(userObj)console.log(userStr)// 设置请求体 发送请求xhr.send(userStr)})
五、 认识Promise
定义: Promise对象用于表示一个异步操作的最终完成(或失败)及其结果值
-
同步方式:你站在门口等外卖员送来,什么事都不干,一直等着。
-
异步方式:你点完外卖,继续干别的事(比如学习/打游戏),等外卖来了,电话通知你,然后你去取。
5.1 为什么 JavaScript 需要异步?
因为 JavaScript 是 单线程的(同一时间只能做一件事),如果你在执行一个耗时操作(比如网络请求、读取大文件),同步写法会卡住整个页面,用户无法点击、无法操作,非常糟糕。异步写法可以把“耗时的事”交给浏览器处理,不阻塞主线程,页面流畅运行
Promise语法
// 1. 创建Promise对象const p = new Promise((resolve, reject) => {// 2. 执行异步任务 并传递结果// 成功调用: resolve(值) 触发then()执行// 失败调用: reject(值) 触发catch()执行})// 3. 接收结果p.then(result => {// 成功}).catch(error => {// 失败})
学了Promise后, 会更好的理解axios , 能够解决回调函数地狱的问题
创建Promise对象后传入的两个参数都是函数,但是二者分别是表示成功时调用的函数 和 失败时调用的函数,等待定时器异步操作完成后,屏幕会输出函数传入的字符
// 1. 创建Promise对象// 参数名 类型 作用// resolve 函数 表示成功时调用,用来传递“成功的结果”// reject 函数 表示失败时调用,用来传递“失败的原因”const p = new Promise((resolve, reject) => {// 2. 执行异步操作setTimeout(() => {resolve('模拟AJAX请求-成功的结果')// reject(new Error('模拟AJAX请求-失败的结果'))}, 2000)})// 3. 获取结果p.then(result => {console.log(result)}).catch(error => {console.log(error)})
5.2 Promiss - 三种状态
一个Promiss对象,必然处于一下的一种状态:
待定(pending) new Promiss(): 初始状态, 既没有被兑现,也没有被拒绝
已兑现(fulfilled) .then(回调函数):意味着,操作成功完成
已拒绝(rejected) .catch(回调函数): 意味着操作失败
请求成功时,是fulfilled状态,触发.then()
请求失败时,是rejected状态, 触发.catch()
5.3 使用Promiss + XHM 获取省份列表
当发送请求错误的时候 将error.message错误信息渲染到屏幕上
六、 封装简易的axios——获取省份列表
通过myAxios传入的对象参数进行接收,默认是GET选项
- 创建Promise对象
- 发起XHR请求,默认是GET
- 进行xhr.open设置请求方法和地址,来为发送请求做准备
- loadend 是在请求完成后触发,来进行发挥成功或者失败的函数
- xhr.status 是服务器响应状态,xhr.response 是服务器响应内容
- xhr.send()正式发起请求到服务器
<p class="my-p"></p><script>/*** 目标:封装_简易axios函数_获取省份列表* 1. 定义myAxios函数,接收配置对象,返回Promise对象* 2. 发起XHR请求,默认请求方法为GET* 3. 调用成功/失败的处理程序* 4. 使用myAxios函数,获取省份列表展示*/// 1. 定义myAxios函数,接收配置对象,返回Promise对象function myAxios(config) {return new Promise((resolve, reject) => {// 2. 发起XHR请求 默认是getconst xhr = new XMLHttpRequest()// open 设置请求的方法和地址,为发送请求做准备xhr.open(config.method || 'GET', config.url)// loadend 是在 请求完成 (不管成功还是失败)时触发的事件,用来统一处理响应结果。xhr.addEventListener('loadend', () => {// 3. 调用成功 / 失败 \// xhr.response 是服务器响应的内容。if (xhr.status >= 200 && xhr.status < 300) resolve(JSON.parse(xhr.response))else reject(new Error(xhr.response))})// 正式发起请求,把配置好的 XHR 请求发送到服务器。xhr.send()})}// 4. 使用myAxios函数 获取省份列表myAxios({url: 'http://hmajax.itheima.net/api/province'}).then(result => {console.log(result)document.querySelector('.my-p').innerHTML = result.list.join('<br>')}).catch(error => {console.log(error)document.querySelector('.my-p').innerHTML = error.message})</script>
修改myAxios函数,支持传递查询参数,就是需要传入params选项
// 判断 有 params 选项, 携带查询参数if (config.params) {// 使用URLSearchParmas转换 并携带到url上const paramsObj = new URLSearchParams(config.params)const queryString = paramsObj.toString()console.log(paramsObj)console.log(queryString)// 把查询数字字符串 拼接到url?后面config.url += `?${queryString}`console.log(config.url)}
起到查询作用就是要根据上面学习的URLSearchParams浏览器内置函数来将用户传入的params查询参数对象转换成字符串来坪街道url?后面
- 判断是否携带params对象参数
- 使用 URLSearchParams转换 并携带到url上 此时还是一个对象关系
- 将paramsObj转换成字符串queryString
- 进行与url拼接
<p class="my-p"></p><script>/*** 目标:封装_简易axios函数_获取省份列表* 1. 定义myAxios函数,接收配置对象,返回Promise对象* 2. 发起XHR请求,默认请求方法为GET* 3. 调用成功/失败的处理程序* 4. 使用myAxios函数,获取省份列表展示*/// 1. 定义myAxios函数,接收配置对象,返回Promise对象function myAxios(config) {return new Promise((resolve, reject) => {// 2. 发起XHR请求 默认是getconst xhr = new XMLHttpRequest()// 判断 有 params 选项, 携带查询参数if (config.params) {// 使用URLSearchParmas转换 并携带到url上const paramsObj = new URLSearchParams(config.params)const queryString = paramsObj.toString()console.log(paramsObj)console.log(queryString)// 把查询数字字符串 拼接到url?后面config.url += `?${queryString}`}console.log(config.url)// open 设置请求的方法和地址,为发送请求做准备xhr.open(config.method || 'GET', config.url)// loadend 是在 请求完成 (不管成功还是失败)时触发的事件,用来统一处理响应结果。xhr.addEventListener('loadend', () => {// 3. 调用成功 / 失败 \// xhr.response 是服务器响应的内容。if (xhr.status >= 200 && xhr.status < 300) resolve(JSON.parse(xhr.response))else reject(new Error(xhr.response))})// 正式发起请求,把配置好的 XHR 请求发送到服务器。xhr.send()})}// 4. 使用myAxios函数 获取省份列表myAxios({url: 'http://hmajax.itheima.net/api/area',params: {pname: '湖北省',cname: '武汉市'}}).then(result => {console.log(result)document.querySelector('.my-p').innerHTML = result.list.join('<br>')}).catch(error => {console.log(error)document.querySelector('.my-p').innerHTML = error.message})</script>
七、 注册账号——支持传递请求体数据 data选项
// 判断是否有data选项 携带请求体if (config.data) {// 数据转换类型 在send中发送const jsonStr = JSON.stringify(config.data)// 请求体数据类型标记xhr.setRequestHeader('Content-Type', 'application/json')xhr.send(jsonStr)}// 正式发起请求,把配置好的 XHR 请求发送到服务器。else xhr.send()})
- 提交数据到服务器,用POST方法进行提交,然后携带data对象数据
- 要将data对象数据转换成JSON字符串
- 标记数据传输到服务器的数据类型是json格式
- 然后正式发起请求 xhr.send(josnStr)
<button class="reg-btn">注册用户</button><script>/*** 目标:封装_简易axios函数_获取省份列表* 1. 定义myAxios函数,接收配置对象,返回Promise对象* 2. 发起XHR请求,默认请求方法为GET* 3. 调用成功/失败的处理程序* 4. 使用myAxios函数,获取省份列表展示*/// 1. 定义myAxios函数,接收配置对象,返回Promise对象function myAxios(config) {return new Promise((resolve, reject) => {// 2. 发起XHR请求 默认是getconst xhr = new XMLHttpRequest()// 判断 有 params 选项, 携带查询参数if (config.params) {// 使用URLSearchParmas转换 并携带到url上const paramsObj = new URLSearchParams(config.params)const queryString = paramsObj.toString()console.log(paramsObj)console.log(queryString)// 把查询数字字符串 拼接到url?后面config.url += `?${queryString}`}console.log(config.url)// open 设置请求的方法和地址,为发送请求做准备xhr.open(config.method || 'GET', config.url)// loadend 是在 请求完成 (不管成功还是失败)时触发的事件,用来统一处理响应结果。xhr.addEventListener('loadend', () => {// 3. 调用成功 / 失败 \// xhr.response 是服务器响应的内容。if (xhr.status >= 200 && xhr.status < 300) resolve(JSON.parse(xhr.response))else reject(new Error(xhr.response))})// 判断是否有data选项 携带请求体if (config.data) {// 数据转换类型 在send中发送const jsonStr = JSON.stringify(config.data)// 请求体数据类型标记xhr.setRequestHeader('Content-Type', 'application/json')xhr.send(jsonStr)}// 正式发起请求,把配置好的 XHR 请求发送到服务器。else xhr.send()})}document.querySelector('.reg-btn').addEventListener('click', () => {// 4. 使用myAxios函数 获取省份列表myAxios({url: 'http://hmajax.itheima.net/api/register',method: 'POST',data: {username: 'wshhaaaaa12w~',password: '1234567'}}).then(result => {console.log(result)}).catch(error => {console.log(error)})})</script>
八、 天气预报案例
一些渲染操作都是非常简单的,就最后一步原生js 响应式输入,只要得到了当前的输入框,然后add~input就能做到响应式!!!
document.querySelector('.search-city').addEventListener('input', e => {console.log(e.target.value)
})
/*** 目标1:默认显示-北京市天气* 1.1 获取北京市天气数据* 1.2 数据展示到页面*/// 获取并渲染城市天气函数
const getWeather = async (cityCode) => {// 获取天气数据const response = await axios('http://hmajax.itheima.net/api/weather', { params: { city: cityCode } })console.log(response.data)const data = response.data.data// 展示数据document.querySelector('.dateShort').innerHTML = data.datedocument.querySelector('.dateLunar').innerHTML = data.dateLunardocument.querySelector('.area').innerHTML = data.area// 当天的气温const nowWStr = `<div class="tem-box"><span class="temp"><span class="temperature">${data.temperature}</span><span>°</span></span></div><div class="climate-box"><div class="air"><span class="psPm25">${data.psPm25}</span><span class="psPm25Level">${data.psPm25Level}</span></div><ul class="weather-list"><li><img src="${data.weatherImg}" class="weatherImg" alt=""><span class="weather">${data.weather}</span></li><li class="windDirection">${data.windDirection}</li><li class="windPower">${data.windPower}</li></ul></div>`document.querySelector('.weather-box').innerHTML = nowWStr// 当天的天气const twObj = data.todayWeatherconst todayWStr = `<div class="range-box"><span>今天:</span><span class="range"><span class="weather">${twObj.weather}</span><span class="temNight">${twObj.temNight}</span><span>-</span><span class="temDay">${twObj.temDay}</span><span>℃</span></span></div><ul class="sun-list"><li><span>紫外线</span><span class="ultraviolet">${twObj.ultraviolet}</span></li><li><span>湿度</span><span class="humidity">${twObj.humidity}</span>%</li><li><span>日出</span><span class="sunriseTime">${twObj.sunriseTime}</span></li><li><span>日落</span><span class="sunsetTime">${twObj.sunsetTime}</span></li></ul></div>`document.querySelector('.today-weather').innerHTML = todayWStr// 七日天气预报数据展示const dayForecast = data.dayForecastconst dayForecastStr = dayForecast.map(item => {return `<li class="item"><div class="date-box"><span class="dateFormat">${item.dateFormat}</span><span class="date">${item.date}</span></div><img src="${item.weatherImg}" alt="" class="weatherImg"><span class="weather">${item.weather}</span><div class="temp"><span class="temNight">${item.temNight}</span>-<span class="temDay">${item.temDay}</span><span>℃</span></div><div class="wind"><span class="windDirection">${item.windDirection}</span><span class="windPower"><${item.windPower}</span></div></li>`}).join('')document.querySelector('.week-wrap').innerHTML = dayForecastStr
}// m默认进入网页 - 就要获取天气数据(北京城市编码就是 ‘110100’)
getWeather('110100')// 搜索城市列表
document.querySelector('.search-city').addEventListener('input', async e => {console.log(e.target.value)const response = await axios('http://hmajax.itheima.net/api/weather/city', { params: { city: e.target.value } })const cityStr = response.data.data.map(item => {return `<li class="city-item">${item.name}</li>`}).join('')document.querySelector('.search-list').innerHTML = cityStrdocument.querySelector('.search-list').addEventListener('click', ee => {const num = response.data.data.filter(item => {return item.name === ee.target.innerHTML})console.log(num)if (num.length > 0) getWeather(num[0].code)})
})
最后获得城市的code值优化,只需要在渲染的同时加上自定义data-code属性就好!
const cityStr = response.data.data.map(item => {return `<li class="city-item" data-code="${item.code}">${item.name}</li>`}).join('')document.querySelector('.search-list').innerHTML = cityStrdocument.querySelector('.search-list').addEventListener('click', e => {console.log(e.target.dataset.code)getWeather(e.target.dataset.code)})