頭條創作挑戰賽#
關注我,帶你了解java
一、AJAX簡介
1.什么是AJAX?
AJAX = 異步 JavaScript 和 XML。
AJAX 是一種用于創建快速動態網頁的技術。通過在后臺與服務器進行少量數據交換,AJAX 可以使網頁實現異步更新。這意味著可以在不重新加載整個網頁的情況下,對網頁的某部分進行更新。ajax 中使用的技術有JavaScript, html , dom , xml ,css 等。主要是 JavaScript , XML.
JavaScript::使用腳本對象 XMLHttpRequest 發送請求, 接收響應數據
XML:發送和接收的數據格式,現在使用 json
AJAX 不單需要前端的技術,同時需要后端(服務器)的配合。服務器需要提供數據,數據是 AJAX 請求的響應結果。
2.原生JS實現AJAX
代碼實現:
網頁代碼:
注釋:
1. open(method, url, async) 方法需要三個參數:
method:發送請求所使用的方法(GET或POST);與POST相比,GET更簡單也更快,并且在大部分情況下都能用;然而,在以下情況中,請使用POST請求:
無法使用緩存文件(更新服務器上的文件或數據庫)
向服務器發送大量數據(POST 沒有數據量限制)
發送包含未知字符的用戶輸入時,POST 比 GET 更穩定也更可靠
url:規定服務器端腳本的 URL(該文件可以是任何類型的文件,比如 .txt 和 .xml,或者服務器腳本文件,比如 .asp 和 .php (在傳回響應之前,能夠在服務器上執行任務));
async:規定應當對請求進行異步(true)或同步(false)處理;true是在等待服務器響應時執行其他腳本,當響應就緒后對響應進行處理;false是等待服務器響應再執行。
2. send() 方法可將請求送往服務器。
3. onreadystatechange:存有處理服務器響應的函數,每當 readyState 改變時,onreadystatechange 函數就會被執行。
4. readyState:存有服務器響應的狀態信息。
0: 請求未初始化(代理被創建,但尚未調用 open() 方法)
1: 服務器連接已建立(open方法已經被調用)
2: 請求已接收(send方法已經被調用,并且頭部和狀態已經可獲得)
3: 請求處理中(下載中,responseText 屬性已經包含部分數據)
4: 請求已完成,且響應已就緒(下載操作已完成)
5. responseText:獲得字符串形式的響應數據。
6. setRequestHeader():POST傳數據時,用來添加 HTTP 頭,然后send(data),注意data格式;GET發送信息時直接加參數到url上就可以,比如url?a=a1&b=b1。
PS:Fetch polyfill 的基本原理是探測是否存在window.fetch方法,如果沒有則用 XHR 實現。
3.JQuery的三種方式實現AJAX:
3.1 $.ajax()
該方法用于執行Ajax請求,常用于其他jQuery Ajax方法不能完成的請求,也許我們可以把它稱為"jQuery中Ajax系列方法之母"。
1.url:鏈接地址,字符串表示
2.data:需發送到服務器的數據,GET與POST都可以,格式為{A: ‘…’, B: ‘…’}
3.type:“POST” 或 “GET”,請求類型
4.timeout:請求超時時間,單位為毫秒,數值表示
5.cache:是否緩存請求結果,bool表示
6.contentType:內容類型,默認為"application/x-www-form-urlencoded"
7.dataType:服務器響應的數據類型,字符串表示;當填寫為json時,回調函數中無需再對數據反序列化為json
8.success:請求成功后,服務器回調的函數
9.error:請求失敗后,服務器回調的函數
10.complete:請求完成后調用的函數,無論請求是成功還是失敗,都會調用該函數;如果設置了success與error函數,則該函數在它們之后被調用
11.async:是否異步處理,bool表示,默認為true;設置該值為false后,JS不會向下執行,而是原地等待服務器返回數據,并完成相應的回調函數后,再向下執行
3.2 $.get()
$.get() 函數的功能單一,專門用來發起 get 請求,從而將服務器上的資源請求到客戶端來進行使用。
語法:
$.get(url, [data], [callback])
參數介紹:
發起不帶參數的請求時,直接提供請求的 URL 地址和請求成功之后的回調函數即即可
發起帶參數的請求時,以參數對象的形式添加第二個參數
3.3 $.post()
jQuery 中 $.post() 函數的功能單一,專門用來發起 post 請求,從而向服務器提交數據。
語法:
$.post(url, [data], [callback])
參數介紹:
使用 $post() 向服務器提交數據
星辰大海,永不止步
END
Ajax 即“Asynchronous Javascript And XML”(異步 JavaScript 和 XML),是指一種創建交互式、快速動態網頁應用的網頁開發技術,無需重新加載整個網頁的情況下,能夠更新部分網頁的技術。
通過在后臺與服務器進行少量數據交換,Ajax 可以使網頁實現異步更新。這意味著可以在不重新加載整個網頁的情況下,對網頁的某部分進行更新。
傳統開發的缺點,是對于瀏覽器的頁面,全部都是全局刷新的體驗。如果我們只是想取得或是更新頁面中的部分信息那么就必須要應用到局部刷新的技術。局部刷新也是有效提升用戶體驗的一種非常重要的方式。
ajax技術是基于js語言的擴展,能夠通過將請求發送給后臺,并從后臺取得相關數據,然后將數據在頁面做局部刷新的重要技術。
動力節點的Ajax教程會通過對ajax的傳統使用方式,結合json操作的方式,結合跨域等高級技術的方式,對ajax做一個全面的講解。
ajax學習資料下載:http://www.bjpowernode.com/?toutiaoajax
這個術語源自描述從基于 Web 的應用到基于數據的應用。
Ajax 不是一種新的編程語言,而是一種用于創建更好更快以及交互性更強的Web應用程序的技術。
使用 JavaScript 向服務器提出請求并處理響應而不阻塞用戶核心對象XMLHttpRequest。通過這個對象,您的 JavaScript 可在不重載頁面的情況與 Web 服務器交換數據,即在不需要刷新頁面的情況下,就可以產生局部刷新的效果。
Ajax 在瀏覽器與 Web 服務器之間使用異步數據傳輸(HTTP 請求),這樣就可使網頁從服務器請求少量的信息,而不是整個頁面。
Ajax可使因特網應用程序更小、更快,更友好。
Ajax 是一種獨立于 Web 服務器軟件的瀏覽器技術。Ajax 基于下列 Web 標準:
JavaScript、XML、HTML與 CSS 在 Ajax 中使用的 Web 標準已被良好定義,并被所有的主流瀏覽器支持。Ajax 應用程序獨立于瀏覽器和平臺。
Web 應用程序較桌面應用程序有諸多優勢;它們能夠涉及廣大的用戶,它們更易安裝及維護,也更易開發。
不過,因特網應用程序并不像傳統的桌面應用程序那樣完善且友好。通過 Ajax,因特網應用程序可以變得更完善,更友好。
使用AJAX構建下列web標準
應用案例
該技術在 1998 年前后得到了應用。允許客戶端腳本發送HTTP請求(XMLHTTP)的第一個組件由Outlook Web Access小組寫成。該組件原屬于微軟 Exchange Server,并且迅速地成為了 Internet Explorer 4.0 的一部分。部分觀察家認為,Outlook Web Access 是第一個應用了 Ajax 技術的成功的商業應用程序,并成為包括Oddpost 的網絡郵件產品在內的許多產品的領頭羊。但是,2005 年初,許多事件使得 Ajax 被大眾所接受。Google 在它著名的交互應用程序中使用了異步通訊,如Google、Google 地圖、Google 搜索建議、Gmail等。Ajax 這個詞由《Ajax: A New Approach to Web Applications》一文所創,該文的迅速傳播加強了人們使用該項技術的意識。另外,對Mozilla/Gecko 的支持使得該技術走向成熟,變得更為易用。
Ajax 前景非常樂觀,可以提高系統性能,優化用戶界面。Ajax 現有直接框架 AjaxPro,可以引入 AjaxPro.2.dll 文件,可以直接在前臺頁面 JavaScript 調用后臺頁面的方法。但此框架與表單驗證有沖突。另外微軟也引入了 Ajax 組件,需要添加AjaxControlToolkit.dll 文件,可以在控件列表中出現相關控件。
構架應用及對AJAX框架的思考
重要的技術和 Ajax 開發模式可以從現有的知識中獲取。例如,在一個發送請求到服務端的應用中,必須包含請求順序、優先級、超時響應、錯誤處理及回調,其中許多元素已經在Web 服務中包含了。同時,隨著技術的成熟還會有許多地方需要改進,特別是UI部分的易用性。
Ajax 開發與傳統的 B/S開發有很大的不同。這些不同引入了新的編程問題,最大的問題在于易用性。由于 Ajax 依賴瀏覽器的 JavaScript 和XML,瀏覽器的兼容性和支持的標準也變得和 JavaScript 的運行時性能一樣重要了。這些問題中的大部分來源于瀏覽器、服務器和技術的組合,因此必須理解如何才能最好的使用這些技術。
綜合各種變化的技術和強耦合的客戶服務端環境,Ajax 提出了一種新的開發方式。Ajax 開發人員必須理解傳統的 MVC 架構,這限制了應用層次之間的邊界。同時,開發人員還需要考慮 B/S 環境的外部和使用 Ajax 技術來重定型 MVC 邊界。最重要的是,Ajax 開發人員必須禁止以頁面集合的方式來考慮 Web 應用而需要將其認為是單個頁面。一旦 UI 設計與服務架構之間的范圍被嚴格區分開來后,開發人員就需要更新和變化的技術集合了。
利用AJAX J2ee開發組織機構
? 不需要插件支持
? 用戶體驗極佳
? 提升Web程序性能
? 減輕服務器和寬帶的負擔
? 前進后退按鈕被破壞
? 搜索引擎的支持不夠
? 開發調試工具缺乏
? 運用XHTML+CSS來表達資訊;
? 運用JavaScript操作DOM(Document Object Model)來執行動態效果;
? 運用XML和XSLT操作資料;
? 運用XMLHttpRequest或新的Fetch API與網頁服務器進行異步資料交換;
注意:AJAX與Flash、Silverlight和Java Applet等RIA技術是有區分的。
在現代的Web開發中,AJAX(Asynchronous JavaScript and XML)已經成為不可或缺的一部分。它使我們能夠在不刷新整個頁面的情況下與服務器進行通信,為用戶提供更流暢的體驗。然而,要真正掌握AJAX,我們需要了解JavaScript中的同步與異步編程、Promise對象以及如何使用強大的axios庫來簡化AJAX請求。在這篇推文中,我們將深入探討這些概念,幫助你更好地理解和利用AJAX的潛力。讓我們開始這段AJAX之旅吧!
定義:AJAX是異步的JavaScript與XML,使用XMLHttpRequest對象與服務器通信;
Axios是一個基于promise構建的一個網絡請求庫,可以用于瀏覽器和node.js
<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
(1)傳入配置對象
(2)使用.then回調函數接收結果,并做后續處理
axios({
url:'目標資源地址'
}).then(result=>{
// 對服務器返回的數據做后續處理
})
<!doctype html>
<html lang="zh-cmn-Hans">
<head>
<meta charset="utf-8">
<link rel="icon" href="./img/favicon.ico">
<title>axios使用</title>
<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
</head>
<body>
<p class="my-p"></p>
</body>
<script>
axios({
url:'http://hmajax.itheima.net/api/province'
}).then(result=>{
// 對服務器返回的數據做后續處理
console.log(result.data.list.join('<br>'));
// 把準備好的省份列表插入到頁面
document.querySelector('.my-p').innerHTML = result.data.list.join('<br>');
})
</script>
</html>
url是什么?URL是統一資源定位符,用于訪問服務器上的資源;結構由協議、域名、資源路徑組成;
url參數查詢語法:
http://xxx.com/xxx/xxx?參數名1=值1&參數名2=值2
使用axios提供的params選項:
axios({
url:'目標資源地址',
params:{
參數名:值
}
}).then(result=>{
// 對服務器返回的數據做后續處理
})
實際上axios在運行時會把params中的內容自動拼接到url?參數名=值上面;
在ES6中如果屬性名和變量同名,可以只寫一個;
通過省份和城市名查詢該城市的所有區縣;
document.querySelector('.sel-btn').addEventListener('click',() => {
let pname = document.querySelector('.province').value;
let cname = document.querySelector('.city').value;
// 基于axios獲取服務器數據
axios({
url: "http://hmajax.itheima.net/api/area",
params: {
pname,
cname
}
}).then(result => {
let list = result.data.list;
// 利用map函數將list映射到新的列表中theLi中
let theLi = list.map(areaName => `<li class="list-group-item">${areaName}</li>`).join('')
console.log(theLi);
//將結果插入到元素中顯示出來
document.querySelector('.list-group').innerHTML = theLi;
})
})
axios配置請求方法怎么寫?
GET方法是默認的可以省略,在參數列表中,如果是get方法則用params,如果是post方法則用data;
axios({
url:'目標資源地址',
method:'請求方法',
params:{
參數名:值
}
}).then(result=>{
// 對服務器返回的數據做后續處理
})
在axios請求錯誤時,通過調用catch方法傳入回調函數并定義形參;例如在用戶注冊失敗時,通過彈窗提示用戶錯誤原因。
axios({
// 請求選項
}).then(result=>{
// 對服務器返回的數據做后續處理
}).catch(error=>{
// 處理錯誤
})
Http協議:規定了瀏覽器發送以及服務器返回內容的格式;
請求報文:瀏覽器按照Http協議要求的格式,發送給服務器的內容
請求報文的組成部分有:
在瀏覽器中怎么查看請求報文:
在瀏覽器的瀏覽器開發者工具的網絡面板中,選擇Fetch/XHR中查看,
請求行和請求頭在標頭選項中,請求體在載荷選項中;
在故障排查時可以查看請求報文的請求體內容是否正確;
響應報文的組成部分有:
HTTP響應狀態碼:用來表明請求是否成功,主要含義如下:
根據后端的接口文檔,前端使用ajax進行調用;
示例API文檔:https://apifox.com/apidoc/project-1937884/doc-1695440
// 點擊登錄時,用戶名和密碼長度判斷,并提交數據和服務器通信
// 定義一個函數用于顯示提示框
function showAlert(msg, isSuccess) {
const alert = document.querySelector(".alert");
let alertStyle = isSuccess ? "alert-success" : "alert-danger";
alert.innerHTML = msg;
alert.classList.add("show");
alert.classList.add(alertStyle);
// 設置一個定時器3s后隱藏提示框
setTimeout(() => {
alert.classList.remove("show");
alert.classList.remove(alertStyle);
}, 3000);
}
// 登錄點擊事件
document
.querySelector(".btn-login")
.addEventListener("click", function () {
// 獲取用戶名和密碼
const username = document.querySelector(".username").value;
const password = document.querySelector(".password").value;
// 對長度做判斷
if (username.length < 8) {
console.log("用戶名長度不能低于8");
showAlert("用戶名長度不能低于8", false);
return;
}
if (password.length < 6) {
console.log("密碼長度不能低于6");
showAlert("密碼長度不能低于6", false);
return;
}
axios({
method: "post",
url: "http://hmajax.itheima.net/api/login",
data: {
username,
password,
},
})
.then((result) => {
console.log(result.data.message);
showAlert(result.data.message, true);
})
.catch((error) => {
console.log(error.response.data.message);
showAlert(error.response.data.message, false);
});
});
使用form-serialize插件快速收集表單元素的值,獲取結果的鍵值對,根據表單元素中的name屬性獲取鍵;
語法:
const data = serialize(form, {hash: true, empty:true})
/**
* 使用serialize函數,快速收集表單元素的值
* 參數1:要獲取的表單名稱;
* 參數2:配置對象
* hash:設置獲取數據結構
* - false:獲取到的是url查詢字符串
* - true:獲取到的是JS對象
* empty:設置是否獲取空值
* - false: 不獲取空值
* - true: 可以獲取到空值
*/
使用方法:
引入插件,調用serialize方法獲取到表單元素的值,表單元素的name屬性會成為返回對象的屬性名;
將上面用戶登錄的案例利用form-serialize插件獲取表單的值,下面是實際的寫法:
// 點擊登錄時,用戶名和密碼長度判斷,并提交數據和服務器通信
// 定義一個函數用于顯示提示框
function showAlert(msg, isSuccess) {
const alert = document.querySelector(".alert");
let alertStyle = isSuccess ? "alert-success" : "alert-danger";
alert.innerHTML = msg;
alert.classList.add("show");
alert.classList.add(alertStyle);
// 設置一個定時器3s后隱藏提示框
setTimeout(() => {
alert.classList.remove("show");
alert.classList.remove(alertStyle);
}, 3000);
}
// 登錄點擊事件
document
.querySelector(".btn-login")
.addEventListener("click", function () {
// 使用form-serialize插件
const form = document.querySelector(".form-example");
const data = serialize(form, { hash: true, empty: true });
// 使用解構賦值的方式獲取對象中用戶名和密碼
const { username, password } = data;
// 對長度做判斷
if (username.length < 8) {
console.log("用戶名長度不能低于8");
showAlert("用戶名長度不能低于8", false);
return;
}
if (password.length < 6) {
console.log("密碼長度不能低于6");
showAlert("密碼長度不能低于6", false);
return;
}
axios({
method: "post",
url: "http://hmajax.itheima.net/api/login",
data: {
username,
password,
},
})
.then((result) => {
console.log(result.data.message);
showAlert(result.data.message, true);
})
.catch((error) => {
console.log(error.response.data.message);
showAlert(error.response.data.message, false);
});
});
功能:不離開當前頁面,顯示單獨內容,供用戶操作;調用彈窗可以通過兩種方式:屬性控制和JS控制;
使用方法:
(1)引入bootstrap.css與bootstrap.js
(2)給啟動彈框的組件添加bootstrap屬性,顯示彈框需要兩個屬性,分別是data-bs-toggle和data-bs-target
<button class="btn btn-primary" data-bs-toggle="modal" data-bs-target=".my-box">顯示彈框</button>
(3)通過自定義屬性,控制彈框的顯示和隱藏,隱藏彈框需要設置屬性data-bs-dismiss="modal"
通過屬性控制彈窗的顯示和隱藏具體寫法如下:
<div class="container mt-3">
<button class="btn btn-primary" data-bs-toggle="modal" data-bs-target=".my-box">顯示彈框</button>
<!-- 彈框標簽 -->
<div class="modal my-box" tabindex="-1">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">請輸入姓名</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<span>姓名</span>
<input type="text" class="form-contorl" placeholder="默認姓名">
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">
取消
</button>
<button type="button" class="btn btn-primary">
保存
</button>
</div>
</div>
</div>
</div>
</div>
通過JS控制,顯示或隱藏彈框:
// 創建彈框對象
const modalDom = document.querySelector('.my-box');
const modal = new bootstrap.Modal(modalDom);
// 顯示彈框
modal.show();
// 隱藏彈框
modal.hide();
獲取圖書列表的的API地址http://hmajax.itheima.net/api/books
這里重點需要掌握的技巧是使用map將列表對象中的元素映射到html元素中,并進行字符串拼接的過程。map函數可以傳入三個參數(element、index、array),具體用法可見:https://www.freecodecamp.org/chinese/news/javascript-map-how-to-use-the-js-map-function-array-method/
window.addEventListener('load',()=>{
const creator = '小雨';
// 封裝一個函數,獲取并渲染圖書列表
function getBookList(){
axios({
url: 'http://hmajax.itheima.net/api/books',
params: {
creator,
}
}).then(result => {
const bookList = result.data.data;
console.log(bookList);
const bookItemHtml = bookList.map((item,index) => {
return `<tr>
<td>${index + 1}</td>
<td>${item.bookname}</td>
<td>${item.author}</td>
<td>${item.publisher}</td>
<td>
<span class="del">刪除</span>
<span class="edit">編輯</span>
</td>
</tr>`
}).join('');
document.querySelector('.list').innerHTML = bookItemHtml;
})
}
// 執行函數
getBookList()
})
// 目標2:添加圖書信息
// 獲取彈窗信息
const addModalDom = document.querySelector('.add-modal');
const addModal = new bootstrap.Modal(addModalDom);
document.querySelector('.add-btn').addEventListener('click',()=>{
// 獲取表單信息
const bookItemForm = document.querySelector('.add-form');
const bookItem = serialize(bookItemForm,{hash:true,empty:true});
// 提交表單信息
axios({
url: 'http://hmajax.itheima.net/api/books',
method: 'post',
data:{
// 這里的...是對象展開運算符
...bookItem,
// 這里的creator是一個全局變量
creator
}
}).then(result => {
// 添加成功后請求重新渲染頁面
getBookList();
// 重置表單,防止點擊添加按鈕后,上一次添加的內容還在
bookItemForm.reset();
// 隱藏彈框
addModal.hide();
})
});
思路:
a.綁定點擊事件獲取圖書id
b.調用刪除接口
c.刷新圖書列表
由于每一行圖書信息是動態生成的,如果給每一行圖書的刪除按鈕添加點擊事件,則需要委托其父級元素設置點擊事件。再定義一個判斷條件如果點擊的元素中含有del類,則獲取其父元素的id。
在渲染圖書列表時就給刪除按鈕的父級元素創建一個自定義屬性data-id,在點擊事件中,查詢父元素的id屬性即可通過AJAX刪除指定ID的元素列表;
在接口文檔中看到需要提供PATH參數,這是一種新的傳參方式(路徑傳參)
將URL改為模板字符串即可。
// 目標3:刪除圖書信息
document.querySelector('.list').addEventListener('click',function(e) {
if(e.target.classList.contains('del')){
// 獲取圖書id
const id = e.target.parentNode.dataset.id;
console.log(id);
// 調用刪除接口
axios({
method: 'delete',
url: `http://hmajax.itheima.net/api/books/${id}`,
}).then(result =>{
// 刷新圖書列表
getBookList();
});
}
})
實現該功能主要需要完成以下部分的內容:
編輯彈窗的顯示和隱藏;
獲取當前圖書的ID并通過查詢接口獲取圖書信息填充到彈窗中;
點擊提交按鈕時,提交表單中的內容到后臺,并重新渲染前端頁面內容;
// 目標4:編輯圖書信息
// 獲取編輯彈窗信息
const editModalDom = document.querySelector('.edit-modal');
const editModal = new bootstrap.Modal(editModalDom);
document.querySelector('.list').addEventListener('click',function(e){
if(e.target.classList.contains('edit')){
// 點擊編輯按鈕打開彈框
editModal.show();
// 獲取圖書id
const theId = e.target.parentNode.dataset.id;
// 獲取圖書詳情信息
axios({
url: `http://hmajax.itheima.net/api/books/${theId}`,
}).then(result => {
const bookObj = result.data.data;
const keys = Object.keys(bookObj);
console.log(keys);
keys.forEach(key => {
document.querySelector(`.edit-form .${key}`).value = bookObj[key];
});
});
}
})
// 點擊修改按鈕后保存數據提交到服務器
document.querySelector('.edit-btn').addEventListener('click',function(){
// 獲取編輯表單信息
const editForm = document.querySelector('.edit-form');
// 保存修改后的表單內容
const {id,bookname,author,publisher} = serialize(editForm,{hash:true,empty:true});
// 將表單中的內容提交到服務器
axios({
method: 'put',
url: `http://hmajax.itheima.net/api/books/${id}`,
data: {
id,
bookname,
author,
publisher,
creator
}
}).then(() => {
// 刷新頁面
getBookList();
// 隱藏彈窗
editModal.hide();
});
})
圖片上傳的思路:
(1)獲取圖片文件對象
給input標簽添加一個change事件;
(2)使用FormData攜帶圖片文件
const fd = new FormData()
fd.append(參數名,值)
(3)提交表單數據到服務器
通過ajax將圖片提交到服務器,并拿到后端返回的圖片URL;
document.querySelector('.upload').addEventListener('change',(e)=>{
// 獲取圖片文件
console.log(e.target.files[0]);
// 使用FormData攜帶圖片文件
const fd = new FormData();
fd.append('img',e.target.files[0])
// 提交到服務器
axios({
url: 'http://hmajax.itheima.net/api/uploadimg',
method: 'POST',
data: fd
}).then(result => {
console.log(result.data.data);
const imgurl = result.data.data.url;
document.querySelector('.my-img').src = imgurl;
})
})
實現一個案例,點擊按鈕上傳圖片,并替換網頁背景,頁面刷新后依然不變;
思路:
window.addEventListener('load',() => {
// 邏輯
document.querySelector('.bg-ipt').addEventListener('change',e => {
console.log(e.target.files[0]);
const fd = new FormData();
fd.append('img',e.target.files[0])
// 通過Ajax將圖片傳給后端服務器
axios({
method: 'POST',
url:'http://hmajax.itheima.net/api/uploadimg',
data: fd,
}).then(result => {
console.log(result);
const imgURL = result.data.data.url;
console.log(imgURL);
document.querySelector('body').style.backgroundImage = `url(${imgURL})`;
// 將URL存到本地
localStorage.setItem('backImg',imgURL);
})
})
// 網頁運行后獲取url地址
const bgUrl = localStorage.getItem('backImg');
bgUrl && (document.querySelector('body').style.backgroundImage = `url(${bgUrl})`);
})
實現效果:
該案例可以分為四個主要步驟:
const creator = "xiaoyu";
function getProfile() {
axios({
url: 'http://hmajax.itheima.net/api/settings',
params:{
creator
}
}).then(result => {
// 獲取個人信息
const profile = result.data.data;
// 遍歷個人信息屬性并顯示到頁面中
Object.keys(profile).forEach(key => {
// 對于性別和頭像需要特殊處理
if(key === 'avatar'){
document.querySelector('.prew').src = profile.avatar;
}else if(key === 'gender'){
const genderList = document.querySelectorAll('.gender');
genderList[profile.gender].checked = true;
}else{
document.querySelector(`.${key}`).value = profile[key];
}
});
});
}
在做的這一步的時候,總是提交出錯,原因是沒有看清api文檔中的參數要求
需要在整個FormData對象里append兩個對象avatar和creator;
document.querySelector('.upload').addEventListener('change',(e) => {
const fd = new FormData();
fd.append('avatar',e.target.files[0]);
fd.append('creator',creator);
axios({
url: "http://hmajax.itheima.net/api/avatar",
method: "PUT",
data:fd,
}).then(result => {
document.querySelector('.prew').src = result.data.data.avatar;
});
});
這里需要注意給要提交的的對象添加一個屬性,以及將字符串型的“0”轉為整型的“0”
document.querySelector('.submit').addEventListener('click',() => {
const form = document.querySelector('.user-form');
const newProfile = serialize(form,{hash:true,empty:true});
// 對象可以通過賦值的方式添加屬性
newProfile.creator = creator;
// 將字符串型的“0”轉為整型的“0”
newProfile.gender = +newProfile.gender;
axios({
url: 'http://hmajax.itheima.net/api/settings',
method: 'put',
data:{
...newProfile,
}
}).then(result => {
console.log(result);
});
});
這里需要將提交成功的toast顯示出來,如何使用Bootstrap顯示提示框呢?
<div class="toast" data-bs-delay="1500">
提示框內容
</div>
const toastDom = document.querySelector('css選擇器');
// 創建提示框對象
const toast = new bootstrap.Toast(toastDom);
// 顯示提示框
toast.show()
實際寫法:
<!-- toast提示框 -->
<div class="toast my-toast" data-bs-delay="1500">
<div class="toast-body">
<div class="alert alert-success info-box">
操作成功
</div>
</div>
</div>
const toastDom = this.document.querySelector('.my-toast');
const toast = new bootstrap.Toast(toastDom);
toast.show()
定義:XMLHttpRequest(XHR)對象用于與服務器交互,通過XMLHttpRequest可以在不刷新頁面的情況下請求特定URL,獲取數據,這允許網頁在不影響用戶操作的情況下,更新頁面的布局內容。在AJAX編程中被大量使用;
使用XMLHttpRequest的方法:
// 創建xhr對象
const xhr = new XMLHttpRequest()
xhr.open('請求方法','url網址')
xhr.addEventListener('loadend',() => {
// 輸出響應結果
console.log(xhr.response)
})
// 正式發送請求
xhr.send()
使用axios返回的結果是對象,而原生的XHR返回的是JSON字符串,如果需要將JSON字符串轉化為對象可以使用JSON.parse()的方式;
XHR如果需要攜帶查詢參數則直接在URL里設置;
document.querySelector('.my-btn').addEventListener('click',() => {
const xhr = new XMLHttpRequest();
xhr.open('get','http://hmajax.itheima.net/api/province');
xhr.addEventListener('loadend',() =>{
const data = JSON.parse(xhr.response);
console.log(xhr.response);
document.querySelector('.res').innerHTML = data.list.join('</br>');
});
xhr.send();
})
知識拓展:對于多參數查詢字符串,是可以根據對象名生成的。
// 創建URLSearchParams對象
const paramsObj = new URLSearchParams({
參數名1:值1,
參數名2:值2
})
// 生成指定格式的查詢字符串
const queryString = paramsObj.toString()
// 結果:參數名1=值1&參數名2=值2
document.querySelector('.my-btn').addEventListener('click',() => {
const formDom = document.querySelector('.user-form');
const form = serialize(formDom,{hash:true,empty:true});
const pname = form.province;
const cname = form.city;
// 獲取xhr查詢參數
const paramsObj = new URLSearchParams({
pname:pname,
cname:cname
}
);
const queryURL = paramsObj.toString();
const xhr = new XMLHttpRequest();
xhr.open('get',`http://hmajax.itheima.net/api/area/?${queryURL}`);
xhr.addEventListener('loadend',()=>{
// JSON.parse()將JSON字符串轉為JS對象
const resObj = JSON.parse(xhr.response);
const resItem = resObj.list.map(function (areaName) {
return `<li class="list-group-item">${areaName}</li>`
}).join('');
console.log(resItem);
document.querySelector('.list-group-item').innerHTML = resItem;
});
xhr.send();
})
使用方法:
// 告訴服務器需要傳遞的內容類型是JSON字符串
xhr.setRequestHeader('Content-Type','application/json')
// JSON.stringify將JS對象轉為JSON字符串
const user = {key1:value1,key2:value2};
const userStr = JSON.stringify(user);
// 發送請求體數據
xhr.send(userStr)
實際用法:
document.querySelector('.my-btn').addEventListener('click',() => {
const formDom = document.querySelector('.user-form');
const form = serialize(formDom,{hash:true,empty:true});
const pname = form.province;
const cname = form.city;
// 獲取xhr查詢參數
const paramsObj = new URLSearchParams({
pname:pname,
cname:cname
}
);
const queryURL = paramsObj.toString();
const xhr = new XMLHttpRequest();
xhr.open('get',`http://hmajax.itheima.net/api/area/?${queryURL}`);
xhr.addEventListener('loadend',()=>{
const resItem = JSON.parse(xhr.response).list.map(function (areaName) {
return `<li class="list-group-item">${areaName}</li>`
}).join('');
console.log(resItem);
document.querySelector('.list-group-item').innerHTML = resItem;
});
xhr.send();
})
定義:Promise對象用于表示一個異步操作的最終結果值;
使用Promise的好處:
可以鏈式調用
用法:
// 1.創建Promise對象
const p = new Promise((resove,reject) => {
// 2.執行異步任務,并傳遞結果
setTimeout(() => {
// resove('模擬成功結果');
// reject(new Error('模擬失敗結果'))
},2000);
})
p.then(result => {
// 成功
console.log(result);
}).catch(error => {
// 失敗
console.log(error);
})
需求:使用Promise管理XHR獲取省份列表,并展示到頁面上
實現:
// 1.創建Promise對象
const p = new Promise((resolve,reject) => {
// 2.執行XHR異步代碼,獲得省份列表
const xhr = new XMLHttpRequest();
xhr.open('get','http://hmajax.itheima.net/api/province');
xhr.addEventListener('loadend',() => {
// 根據XHR返回的結果,轉為對象分別給resolve和reject函數作為參數傳進去
if(xhr.status >= 200 && xhr.status < 300){
resolve(JSON.parse(xhr.response));
}else{
reject(new Error(xhr.response));
}
});
xhr.send();
});
// 3.關聯成功或失敗函數做后續處理,result和error為上面傳入的參數對象
p.then(result => {
document.querySelector('.my-p').innerHTML = result.list.join('</br>');
}).catch(error => {
console.dir(error);
document.querySelector('.my-p').innerHTML = error.message;
})
需求:通過Promise和XHR實現對axios的自定義封裝,完成返回省份列表的獲取;
思路:
Step1:封裝myAxios函數,傳入config配置對象,返回Promise對象;
Step2:發起xhr請求,定義默認方法為get,定義接收URL參數;
Step3:定義成功或失敗請求的處理程序,將傳入結果對象
Step4:實際調用自封裝的函數,檢查效果
function myAxios(config) {
// 1. 定義myAxios函數,接受配置對象,返回Promise對象
const p = new Promise((resolve,reject) => {
// 2.發起xhr請求
const xhr = new XMLHttpRequest();
xhr.open(config.method || 'get',config.url);
xhr.addEventListener('loadend',() => {
// 3.判斷響應結果
if(xhr.status >= 200 && xhr.status < 300){
return resolve(JSON.parse(xhr.response));
}else{
return reject(new Error(xhr.response));
}
});
xhr.send();
});
return p;
}
// 4.調用自封裝的my-axios
myAxios({
url: 'http://hmajax.itheima.net/api/province',
}).then(result => {
document.querySelector('.my-p').innerHTML = result.list.join('</br>');
}).catch(error => {
document.querySelector('.my-p').innerHTML = error.message;
})
需求:修改myAxios函數,支持傳遞params參數,完成地區列表的查詢
思路:需要考慮自定義的axios對傳入的params參數的支持,可以借助URLSearchParams實現將params參數轉為URL連接;
function myAxios(config) {
// 1. 定義myAxios函數,接受配置對象,返回Promise對象
return new Promise((resolve,reject) => {
// 2.對params進行處理,前提是有params參數
if(config.params){
const paramsObj = new URLSearchParams(config.params);
const queryString = paramsObj.toString();
config.url += `?${queryString}`;
}
// 3.發起xhr請求
const xhr = new XMLHttpRequest();
xhr.open(config.method || 'get',config.url);
xhr.addEventListener('loadend',() => {
// 4.判斷響應結果
if(xhr.status >= 200 && xhr.status < 300){
return resolve(JSON.parse(xhr.response));
}else{
return reject(new Error(xhr.response));
}
});
xhr.send();
});
}
需求:修改myAxios函數,支持傳遞請求體數據,完成用戶注冊功能;
思路:
Step1:myAxios函數調用后,判斷data選項
Step2:轉換數據類型,在send方法中發送
Step3:使用自己封裝的myAxios完成用戶注冊功能
實現:
function myAxios(config) {
// 1. 定義myAxios函數,接受配置對象,返回Promise對象
return new Promise((resolve,reject) => {
// 2.對params進行處理,前提是有params參數
if(config.params){
const paramsObj = new URLSearchParams(config.params);
const queryString = paramsObj.toString();
config.url += `?${queryString}`;
}
// 3.發起xhr請求
const xhr = new XMLHttpRequest();
xhr.open(config.method || 'get',config.url);
xhr.addEventListener('loadend',() => {
// 4.判斷響應結果
if(xhr.status >= 200 && xhr.status < 300){
return resolve(JSON.parse(xhr.response));
}else{
return reject(new Error(xhr.response));
}
});
// 對data進行處理
if(config.data){
const jsonString = JSON.stringify(config.data);
xhr.setRequestHeader('Content-Type','application/json')
xhr.send(jsonString);
}else{
xhr.send();
}
});
}
document.querySelector('.my-btn').addEventListener('click',() => {
// 5.調用自封裝的my-axios
myAxios({
url: 'http://hmajax.itheima.net/api/register',
method: 'post',
data: {
username: 'xiaoyu1927',
password: '7654321'
}
}).then(result => {
console.log(result);
alert(result.message);
}).catch(error => {
console.dir(error);
alert(error.message);
})
})
步驟:
Step1:默認獲取北京市天氣數據,并展示;
這一步其實就是通過axios獲取后端數據,并顯示到html頁面中,${}將網頁中的元素替換為JS對象屬性值,對于需要批量操作的內容,可以使用map進行映射操作;
Step2:搜索城市列表,展示;
同樣是調用axios,需要給搜索框設置對input事件的監聽
const searchInput = document.querySelector('.search-city');
searchInput.addEventListener('input',() => {
// 獲取輸入框中輸入的內容
const searchName = searchInput.value;
// 通過axios獲取城市列表
myAxios({
url: 'http://hmajax.itheima.net/api/weather/city',
params:{
city:searchName,
}
}).then(result => {
console.log(result);
const cityList = result.data;
const cityListStr = cityList.map(item => {
return `<li class="city-item">${item.name}</li>`;
}).join('');
document.querySelector('.search-list').innerHTML = cityListStr;
});
});
Step3:點擊城市,顯示對應天氣數據
對搜索到的城市列表添加一個點擊事件,獲取該城市的cityCode,調用前面的方法將結果渲染到頁面中;
由于城市列表是動態生成的,如果需要綁定點擊事件則需要通過事件委托(綁定父元素并通過e.target.classList.contains來獲取元素)
在Step2中添加的城市列表city-item也要添加自定義屬性data-code=${item.code}
return `<li class="city-item" data-code="${item.code}">${item.name}</li>`;
document.querySelector('.search-list').addEventListener('click',(e) => {
// 通過事件委托,判斷點擊的是否是城市item
if(e.target.classList.contains('city-item')){
const cityCode = e.target.dataset.code;
getWeather(cityCode)
}
})
最終效果:
同步:逐行執行,需要原地等待結果后,才繼續向下執行;
異步:不必等待任務完成,在將來完成后觸發一個回調函數;
概念:在回調函數中嵌套回調函數,一直嵌套下去就形成了回調函數地獄;
缺點:耦合性嚴重,異常無法捕獲,可讀性差;
通過一個案例,理解回調函數地獄問題:
//根據省名稱獲取城市名稱,再根據省名稱和城市名稱獲取地區名稱,axios模擬回調函數地獄問題
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}}).then(result => {
const cname = result.data.list[0];
document.querySelector('.city').innerHTML = cname;
axios({url:'http://hmajax.itheima.net/api/area',params:{pname,cname}}).then(result =>{
const areaname = result.data.list[0];
document.querySelector('.area').innerHTML = areaname;
})
})
})
這種嵌套問題,出現異常是無法捕獲的;
依靠then()方法會返回一個新生成的Promise對象的特性,繼續串聯下一環任務,直到結束;
// 將省份變量pname定義為全局變量
let pname = '';
axios({url:'http://hmajax.itheima.net/api/province'}).then(result => {
pname = result.data.list[0];
document.querySelector('.province').innerHTML = pname;
// 關鍵點是axios結尾返回一個新的Promise對象,這樣就可以鏈式調用了
return axios({url:'http://hmajax.itheima.net/api/city',params:{pname}})
}).then(result => {
const cname = result.data.list[0];
document.querySelector('.city').innerHTML = cname;
// 由于這里的pname是在兄弟作用域中,所以將pname定義為全局變量;
return axios({url:'http://hmajax.itheima.net/api/area',params:{pname,cname}})
}).then(result => {
const area = result.data.list[0];
document.querySelector('.area').innerHTML = area;
})
使用async和await關鍵字后,可以更簡潔的寫出基于Promise的異步行為,不需要刻意地鏈式調用Promise;
//獲取默認省市區
async function getDefaultArea(){
const pObj = await axios({url:'http://hmajax.itheima.net/api/province'});
const pname = pObj.data.list[0];
const cObj = await axios({url:'http://hmajax.itheima.net/api/city',params:{pname}});
const cname = cObj.data.list[0];
const aObj = await axios({url:'http://hmajax.itheima.net/api/area',params:{pname,cname}});
const aname = aObj.data.list[0];
// 賦予到頁面上
document.querySelector('.province').innerHTML = pname;
document.querySelector('.city').innerHTML = cname;
document.querySelector('.area').innerHTML = aname;
}
getDefaultArea();
async修飾函數名表明該函數為異步函數,await表明該任務為異步任務,會原地等待后續執行完畢后將結果返回,替代then回調函數的功能;
在使用async函數和await關鍵字時,如何捕獲錯誤?答案是使用try...catch關鍵字;
//獲取默認省市區
async function getDefaultArea() {
try {
const pObj = await axios({ url: 'http://hmajax.itheima.net/api/province' });
const pname = pObj.data.list[0];
const cObj = await axios({ url: 'http://hmajax.itheima.net/api/city', params: { pname } });
const cname = cObj.data.list[0];
const aObj = await axios({ url: 'http://hmajax.itheima.net/api/area', params: { pname, cname } });
const aname = aObj.data.list[0];
// 賦予到頁面上
document.querySelector('.province').innerHTML = pname;
document.querySelector('.city').innerHTML = cname;
document.querySelector('.area').innerHTML = aname;
} catch (error) {
console.dir(error);
}
}
getDefaultArea();
Javascript 單線程為了讓耗時代碼不阻塞其他代碼運行,設計了事件循環模型;
執行代碼和收集異步任務,在調用棧空閑時,反復調用任務隊列里回調函數執行機制;
為什么要有事件循環?
因為JavaScript是單線程的,為了不阻塞JavaScript引擎,而設計執行的代碼模型
事件循環的過程:
a.執行同步代碼,遇到異步代碼交給宿主瀏覽器環境執行;
b.異步有了結果后,把回調函數放入任務隊列排隊;
c.當調用棧空閑后,返回調用任務隊列里的回調函數;
ES6之后引入了promise對象,讓JS引擎也能發起異步任務:
宏任務:由瀏覽器環境執行的異步代碼,例如JS腳本事件、定時器、AJAX、用戶交互事件
微任務:由JS引擎環境執行的異步代碼,例如Promise對象.then()回調;
Promise.then()屬于微任務隊列,優先級高于宏任務隊列;
// 回答代碼執行順序
console.log(1);
setTimeout(() => {
console.log(2);
const p = new Promise(resolve => resolve(3));
p.then(result => console.log(result))
},0);
const p = new Promise(resolve => {
setTimeout(() => {
console.log(4);
},0);
resolve(5);
});
p.then(result => console.log(result));
const p2 = new Promise(resolve => resolve(6));
p2.then(result => console.log(result));
console.log(7);
答案:1 7 5 6 2 3 4
語法:
const p = Promise.all([Promise對象,Promise對象,...])
p.then(result => {
// result結果:[Promise對象成功結果,Promise對象成功結果,...]
}).catch(error => {
// 第一個失敗的Promise對象,拋出的異常
})
const bjPromise = axios({url:'http://hmajax.itheima.net/api/weather',params:{city:'110100'}})
const shPromise = axios({url:'http://hmajax.itheima.net/api/weather',params:{city:'310100'}})
const gzPromise = axios({url:'http://hmajax.itheima.net/api/weather',params:{city:'440100'}})
const szPromise = axios({url:'http://hmajax.itheima.net/api/weather',params:{city:'440300'}})
const p = Promise.all([bjPromise,shPromise,gzPromise,szPromise]);
p.then(result => {
console.log(result);
const weathers = result.map(item => {
return `<li>${item.data.data.area}---${item.data.data.weather}</li>`;
}).join('');
document.querySelector('.my-ul').innerHTML = weathers
}).catch(error => {
console.dir(error);
})
思路:遍歷所有的一級分類數據;遍歷id,創建獲取二級分類請求;合并所有二級分類Promise對象;等待同時成功,開始渲染頁面。
axios({ url: 'http://hmajax.itheima.net/api/category/top' }).then(result => {
console.log(result.data.data);
// 獲取二級列表的Promise對象
const subPromise = result.data.data.map(itemTop => {
return axios({ url: 'http://hmajax.itheima.net/api/category/sub', params: { id: itemTop.id } });
})
// 合并二級Promise對象
const promiseAll = Promise.all(subPromise).then(result => {
console.log(result);
const showStr = result.map(item => {
const subObj = item.data.data;
return `<div class="item">
<h3>${subObj.name}
<ul>
${subObj.children.map(item => {
return `<li>
<a href="javascript:;">
<img src=${item.picture}/>
</a>
<p>${item.name}</p>
</li>`
}).join('')
}
</ul>
</h3>
</div>`
}).join('');
document.querySelector('.sub-list').innerHTML = showStr;
})
})
// 1.1 設置省份下拉菜單數據
// 創建一個全局變量
let pname = '';
axios({ url: 'http://hmajax.itheima.net/api/province' }).then(provinceList => {
const proinceListStr = provinceList.data.list.map(pname => {
return `<option value=${pname}>${pname}</option>`
}).join('');
document.querySelector('.province').innerHTML = `<option value="">省份</option>` + proinceListStr;
})
// 1.2 切換省份
document.querySelector('.province').addEventListener('change', async e => {
// 用戶選中的省份
pname = e.target.value;
const cityList = await axios({ url: 'http://hmajax.itheima.net/api/city', params: { pname: e.target.value } });
console.log(cityList.data.list);
const cityListStr = cityList.data.list.map(cname => {
return `<option value=${cname}>${cname}</option>`
}).join('');
document.querySelector('.city').innerHTML = `<option value="">城市</option>` + cityListStr;
document.querySelector('.area').innerHTML = `<option value="">地區</option>`
})
// 1.3 切換城市
document.querySelector('.city').addEventListener('change', async e => {
// 用戶選擇的城市
const areaList = await axios({ url: 'http://hmajax.itheima.net/api/area', params: { pname: pname, cname: e.target.value }});
console.log(areaList.data.list);
const areaListStr = areaList.data.list.map(aname => {
return `<option value=${aname}>${aname}</option>`
}).join('');
document.querySelector('.area').innerHTML = `<option value="">地區</option>` + areaListStr;
})
// 2.收集數據并提交保存到后端
document.querySelector('.submit').addEventListener('click', async () => {
// 獲取表單中的內容
try{
const form = document.querySelector('.info-form')
const formRes = serialize(form, { hash: true, empty: true });
const res = await axios({ url: 'http://hmajax.itheima.net/api/feedback', method: 'post', data: { ...formRes } });
console.log(res);
alert(res.data.message)
}catch (error){
console.dir(error);
alert(error.response.data.message);
}
})
至此,關于JavaScript AJAX相關知識就介紹到這里,感謝你的閱讀~
本文配套源碼以及圖片資源可見:https://gitee.com/yushengtan/jscode/tree/main/AJAX
*請認真填寫需求信息,我們會在24小時內與您取得聯系。