目錄
一、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)})