作為一名前端開發者,我們每天都在與各種API打交道。從最初的XMLHttpRequest到現在的Fetch API,前端異步請求技術經歷了怎樣的演變?今天就讓我們通過實際代碼來探索這段技術演進的歷程。
前后端分離時代的到來
還記得早期的Web開發嗎?那時候前后端是緊密耦合的,頁面刷新是家常便飯。而現在,我們已經進入了前后端分離的時代:
前后端分離 js 主動請求接口 (異步任務),拿到數據
這種架構讓前端可以"自己做主",通過JavaScript主動拉取資源,實現了真正的Web 2.0動態頁面體驗。
XMLHttpRequest:異步請求的開山鼻祖
理解XMLHttpRequest的工作原理
XMLHttpRequest可以說是前端異步請求的開山鼻祖。雖然它"早期接口請求的對象",但理解它的工作原理對我們掌握現代異步編程至關重要。
讓我們來看看XMLHttpRequest的經典用法:
const getJSON = async(url) => {
return new Promise((resolve,reject) => {
const xhr = new XMLHttpRequest();
console.log(xhr.readyState);
xhr.open('GET','https://api.github.com/users/usersname/repos')
console.log(xhr.readyState);
xhr.send()
xhr.onreadystatechange = function() {
if(xhr.readyState === 4) {
resolve(JSON.parse(xhr.responseText))
}
}
})
}
XMLHttpRequest的readyState詳解
這里有個關鍵概念需要理解:readyState
。它表示XMLHttpRequest對象的狀態:
- 0: 未初始化(還沒有調用open()方法)
- 1: 啟動(已經調用open()方法,但尚未調用send()方法)
- 2: 發送(已經調用send()方法,但尚未接收到響應)
- 3: 接收(已經接收到部分響應數據)
- 4: 完成(已經接收到全部響應數據)
當readyState === 4
時,說明響應內容已經到達,我們就可以處理數據了。
從XML到JSON的數據格式演變
有趣的是,XMLHttpRequest中的"XML"反映了早期的數據交換格式:
<song>
<author>周深</author>
<title>大魚</title>
</song>
而現在我們更多使用的是JSON格式:
{
"author": "林俊杰",
"title": "江南"
}
JSON格式更輕量、更易于JavaScript處理,這也體現了前端技術的不斷進步。
Fetch API:現代異步請求的新寵
告別回調地獄,擁抱Promise
XMLHttpRequest有個明顯的問題:它是"es6之前的對象,連promise都沒有"。這意味著我們只能通過事件監聽和回調函數來處理異步操作,容易陷入回調地獄。
Fetch API的出現徹底改變了這種情況:
document.addEventListener('DOMContentLoaded', async () => {
console.log(fetch('https://api.github.com/users/usersname/repos'))
const result = await fetch('https://api.github.com/users/usersname/repos')
const data = await result.json()
document.getElementById('repos').innerHTML = data.map(item => `<li>${item.name}</li>`).join('')
})
Promise的三種狀態
使用Fetch API時,我們需要理解Promise的三種狀態:
- pending狀態:等待中,異步操作還未完成
- fulfilled狀態:已完成,通過resolve()觸發
- rejected狀態:已失敗,通過reject()觸發
async/await:讓異步代碼像同步代碼一樣
ES8引入的async/await語法糖讓異步代碼變得更加優雅:
fetch('https://api.github.com/users/usersname/repos')
.then(res => res.json())
.then(json => {
})
const result = await fetch('https://api.github.com/users/usersname/repos')
const data = await result.json()
"then的鏈式調用有的繁瑣",而async/await讓代碼"像同步代碼一樣",大大提高了代碼的可讀性和可維護性。
性能優化:DOM加載時機的選擇
DOMContentLoaded vs window.onload
在實際開發中,我們經常需要在DOM加載完成后執行JavaScript代碼。這里有兩個重要的事件:
document.addEventListener('DOMContentLoaded', async () => {
})
window.onload = function() {
}
正如注釋中提到的,window.onload
"有點晚",因為它要等待所有資源(包括圖片、樣式表)都加載完成。而DOMContentLoaded
只需要DOM結構加載完成就可以執行,性能更好。
API接口 vs 網站地址:理解URL的本質
在前端開發中,我們需要區分兩種不同的URL:
// API接口地址 - 返回JSON格式數據
// https://api.github.com/users/usersname/repos
// 網站地址 - 用戶訪問的頁面
// www.bilibili.com
API接口地址是"資源"的概念,前端通過JavaScript主動拉取這些資源,而網站地址是用戶直接訪問的頁面。這種分離讓前端可以更靈活地處理數據和用戶交互。
實戰應用:GitHub倉庫列表展示
讓我們看看如何將獲取到的數據渲染到頁面上:
const data = await getJSON('https://api.github.com/users/usersname/repos')
document.getElementById('repos').innerHTML = data.map(item => `<li>${item.name}</li>`).join('')
這里使用了函數式編程的思想:
- 使用
map()
方法遍歷數組 - 將每個倉庫對象轉換為
<li>
標簽 - 使用
join('')
將數組元素連接成字符串 - 通過
innerHTML
更新DOM
技術演進的思考
從XMLHttpRequest到Fetch API,從回調函數到Promise,從then鏈式調用到async/await,前端異步請求技術的演進體現了幾個重要趨勢:
- 語法簡化:代碼變得更加直觀和易讀
- 錯誤處理:更好的錯誤處理機制
- 性能優化:更高效的資源管理
- 開發體驗:更好的開發者體驗
寫在最后
無論是XMLHttpRequest還是Fetch API,它們都是前端異步編程的重要工具。理解它們的工作原理和使用場景,不僅能幫助我們寫出更好的代碼,也能讓我們在面對復雜的異步場景時游刃有余。
技術在不斷演進,但核心思想是不變的:讓前端能夠主動獲取數據,提供更好的用戶體驗。這正是Web 2.0時代動態頁面的魅力所在。
?轉自https://juejin.cn/post/7524627104581255202
該文章在 2025/7/10 9:57:06 編輯過