关于前后端交互的数据的方式总结对比传统ajax

89 阅读3分钟

关于前后端交互的数据的方式总结对比传统ajax

在 FastAPI 网站的前后端交互中,以音乐网站应用为例,主要使用了以下几种交互方法,它们都是 异步 的(因为 FastAPI 是异步框架,现代前端也主要使用异步交互): ## 1. 获取数据 (GET 请求) • 用途 :获取音乐列表、获取单首音乐信息 • 同步/异步 :异步(前端 fetch + 后端 async ) • 示例 : ``` // 前端异步获取音乐列表 async function loadMusicList() { const response = await fetch(${API_BASE_URL}/music/); musicList = await response.json(); }

关于前后端交互的数据的方式总结对比传统ajax

@app.get("/music/", response_model=List[Music]) async def get_music_list(): return fake_music_db

 ## 2. 提交数据 (POST 请求)
 • 用途 :用户登录、上传音乐 • 同步/异步 :异步(前端 `fetch` + 后端 `async` ) • 示例 : ```
// 前端异步提交登录表单
async function login() {
    const response = await fetch(`${API_BASE_URL}/token`, {
        method: "POST",
        body: `username=${username}&password=${password}`
    });
}
# 关于前后端交互的数据的方式总结对比传统ajax
@app.post("/token", response_model=Token)
async def login_for_access_token(form_data: OAuth2PasswordRequestForm = Depends()):
   user = authenticate_user(fake_users_db, form_data.username, form_data.password)
   return {"access_token": access_token, "token_type": "bearer"}

3. 文件上传 (Multipart Form Data)

• 用途 :上传音乐文件 • 同步/异步 :异步(前端 FormData + 后端 UploadFile ) • 示例 : ``` // 前端异步上传文件 async function uploadMusic() { const formData = new FormData(); formData.append("file", file); const response = await fetch(${API_BASE_URL}/upload/, { method: "POST", body: formData }); }

关于前后端交互的数据的方式总结对比传统ajax

@app.post("/upload/") async def upload_music( file: UploadFile = File(...), current_user: User = Depends(get_current_active_user) ): with open(file_path, "wb") as buffer: await buffer.write(await file.read())

 ## 4. 流媒体传输 (Streaming)
 • 用途 :播放音乐(支持 `Range` 请求) • 同步/异步 :异步(前端 `<audio>` + 后端文件流) • 示例 : ```
// 前端用 <audio> 播放
audioPlayer.src = `${API_BASE_URL}/stream/${music.id}`;
# 关于前后端交互的数据的方式总结对比传统ajax
@app.get("/stream/{music_id}")
async def stream_music(music_id: str):
   return FileResponse(music.file_path)

5. WebSocket(可选扩展)

• 用途 :实时聊天、播放同步 • 同步/异步 :异步(双向通信) • 示例 : ```

关于前后端交互的数据的方式总结对比传统ajax

@app.websocket("/ws") async def websocket_endpoint(websocket: WebSocket): await websocket.accept() while True: data = await websocket.receive_text() await websocket.send_text(f"Message: {data}")

// 前端 WebSocket const socket = new WebSocket("ws://localhost:8000/ws"); socket.onmessage = (event) => { console.log(event.data); };

 完整前端代码: ```
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>音乐网站</title>
    <style>
        body {
            font-family: Arial, sans-serif;
            margin: 0;
            padding: 0;
            background-color: #f5f5f5;
        }
        .container {
            max-width: 1200px;
            margin: 0 auto;
            padding: 20px;
        }
        header {
            background-color: #333;
            color: white;
            padding: 10px 0;
            margin-bottom: 20px;
        }
        header h1 {
            margin: 0;
            padding: 0 20px;
        }
        .auth-section {
            margin-bottom: 20px;
            padding: 20px;
            background-color: white;
            border-radius: 5px;
            box-shadow: 0 2px 5px rgba(0,0,0,0.1);
        }
        .upload-section {
            margin-bottom: 20px;
            padding: 20px;
            background-color: white;
            border-radius: 5px;
            box-shadow: 0 2px 5px rgba(0,0,0,0.1);
        }
        .music-list {
            display: grid;
            grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
            gap: 20px;
        }
        .music-card {
            background-color: white;
            border-radius: 5px;
            box-shadow: 0 2px 5px rgba(0,0,0,0.1);
            padding: 15px;
            transition: transform 0.2s;
        }
        .music-card:hover {
            transform: translateY(-5px);
        }
        .music-card h3 {
            margin-top: 0;
        }
        .player {
            position: fixed;
            bottom: 0;
            left: 0;
            right: 0;
            background-color: #333;
            color: white;
            padding: 10px;
            display: flex;
            align-items: center;
        }
        .player audio {
            flex-grow: 1;
            margin: 0 20px;
        }
        button {
            background-color: #4CAF50;
            color: white;
            border: none;
            padding: 8px 16px;
            border-radius: 4px;
            cursor: pointer;
        }
        button:hover {
            background-color: #45a049;
        }
        input, textarea {
            width: 100%;
            padding: 8px;
            margin: 8px 0;
            box-sizing: border-box;
            border: 1px solid #ccc;
            border-radius: 4px;
        }
    </style>
</head>
<body>
    <header>
        <div class="container">
            <h1>音乐网站</h1>
        </div>
    </header>
    
    <div class="container">
        <div class="auth-section" id="authSection">
            <h2>登录</h2>
            <div id="loginForm">
                <input type="text" id="username" placeholder="用户名" required>
                <input type="password" id="password" placeholder="密码" required>
                <button onclick="login()">登录</button>
                <p id="authMessage"></p>
            </div>
            <div id="userInfo" style="display: none;">
                <p>欢迎, <span id="displayUsername"></span></p>
                <button onclick="logout()">登出</button>
            </div>
        </div>
        
        <div class="upload-section" id="uploadSection" style="display: none;">
            <h2>上传音乐</h2>
            <form id="uploadForm">
                <input type="text" id="musicTitle" placeholder="歌曲标题" required>
                <input type="text" id="musicArtist" placeholder="艺术家" required>
                <input type="number" id="musicDuration" placeholder="时长(秒)" required>
                <input type="file" id="musicFile" accept="audio/*" required>
                <button type="button" onclick="uploadMusic()">上传</button>
                <p id="uploadMessage"></p>
            </form>
        </div>
        
        <h2>音乐列表</h2>
        <div class="music-list" id="musicList">
            <!-- 音乐列表将通过JavaScript动态加载 -->
        </div>
    </div>
    
    <div class="player" id="player" style="display: none;">
        <button onclick="previousSong()">上一首</button>
        <audio id="audioPlayer" controls></audio>
        <button onclick="nextSong()">下一首</button>
        <div id="nowPlaying"></div>
    </div>
    
    <script>
        let accessToken = null;
        let currentUser = null;
        let musicList = [];
        let currentPlayingIndex = -1;
        const API_BASE_URL = 'http://localhost:8000';
        
        // 登录函数
        async function login() {
            const username = document.getElementById('username').value;
            const password = document.getElementById('password').value;
            
            try {
                const response = await fetch(`${API_BASE_URL}/token`, {
                    method: 'POST',
                    headers: {
                        'Content-Type': 'application/x-www-form-urlencoded',
                    },
                    body: `username=${encodeURIComponent(username)}&password=${encodeURIComponent(password)}`
                });
                
                if (response.ok) {
                    const data = await response.json();
                    accessToken = data.access_token;
                    currentUser = username;
                    
                    // 更新UI
                    document.getElementById('loginForm').style.display = 'none';
                    document.getElementById('userInfo').style.display = 'block';
                    document.getElementById('displayUsername').textContent = username;
                    document.getElementById('uploadSection').style.display = 'block';
                    document.getElementById('authMessage').textContent = '';
                    
                    // 加载音乐列表
                    loadMusicList();
                } else {
                    document.getElementById('authMessage').textContent = '登录失败,请检查用户名和密码';
                }
            } catch (error) {
                console.error('登录错误:', error);
                document.getElementById('authMessage').textContent = '登录时发生错误';
            }
        }
        
        // 登出函数
        function logout() {
            accessToken = null;
            currentUser = null;
            
            // 更新UI
            document.getElementById('loginForm').style.display = 'block';
            document.getElementById('userInfo').style.display = 'none';
            document.getElementById('uploadSection').style.display = 'none';
            document.getElementById('player').style.display = 'none';
            document.getElementById('authMessage').textContent = '';
        }
        
        // 上传音乐
        async function uploadMusic() {
            if (!accessToken) {
                alert('请先登录');
                return;
            }
            
            const title = document.getElementById('musicTitle').value;
            const artist = document.getElementById('musicArtist').value;
            const duration = document.getElementById('musicDuration').value;
            const fileInput = document.getElementById('musicFile');
            const file = fileInput.files[0];
            
            if (!title || !artist || !duration || !file) {
                document.getElementById('uploadMessage').textContent = '请填写所有字段';
                return;
            }
            
            const formData = new FormData();
            formData.append('title', title);
            formData.append('artist', artist);
            formData.append('duration', duration);
            formData.append('file', file);
            
            try {
                const response = await fetch(`${API_BASE_URL}/upload/`, {
                    method: 'POST',
                    headers: {
                        'Authorization': `Bearer ${accessToken}`
                    },
                    body: formData
                });
                
                if (response.ok) {
                    const data = await response.json();
                    document.getElementById('uploadMessage').textContent = '上传成功!';
                    document.getElementById('uploadForm').reset();
                    
                    // 重新加载音乐列表
                    loadMusicList();
                } else {
                    document.getElementById('uploadMessage').textContent = '上传失败';
                }
            } catch (error) {
                console.error('上传错误:', error);
                document.getElementById('uploadMessage').textContent = '上传时发生错误';
            }
        }
        
        // 加载音乐列表
        async function loadMusicList() {
            try {
                const response = await fetch(`${API_BASE_URL}/music/`);
                if (response.ok) {
                    musicList = await response.json();
                    renderMusicList();
                }
            } catch (error) {
                console.error('加载音乐列表错误:', error);
            }
        }
        
        // 渲染音乐列表
        function renderMusicList() {
            const musicListContainer = document.getElementById('musicList');
            musicListContainer.innerHTML = '';
            
            musicList.forEach((music, index) => {
                const musicCard = document.createElement('div');
                musicCard.className = 'music-card';
                musicCard.innerHTML = `
                    <h3>${music.title}</h3>
                    <p>艺术家: ${music.artist}</p>
                    <p>时长: ${Math.floor(music.duration / 60)}:${(music.duration % 60).toString().padStart(2, '0')}</p>
                    <p>上传者: ${music.uploader}</p>
                    <button onclick="playMusic(${index})">播放</button>
                `;
                musicListContainer.appendChild(musicCard);
            });
        }
        
        // 播放音乐
        function playMusic(index) {
            if (index < 0 || index >= musicList.length) return;
            
            currentPlayingIndex = index;
            const music = musicList[index];
            
            document.getElementById('player').style.display = 'flex';
            document.getElementById('nowPlaying').innerHTML = `
                正在播放: ${music.title} - ${music.artist}
            `;
            
            // 设置音频源
            const audioPlayer = document.getElementById('audioPlayer');
            audioPlayer.src = `${API_BASE_URL}/stream/${music.id}`;
            audioPlayer.play();
        }
        
        // 上一首
        function previousSong() {
            if (currentPlayingIndex <= 0) return;
            playMusic(currentPlayingIndex - 1);
        }
        
        // 下一首
        function nextSong() {
            if (currentPlayingIndex >= musicList.length - 1) return;
            playMusic(currentPlayingIndex + 1);
        }
        
        // 初始化
        document.addEventListener('DOMContentLoaded', () => {
            loadMusicList();
        });
    </script>
</body>
</html>

总结:前后端交互方式

交互方式 HTTP 方法 同步/异步 用途 获取数据 (GET) GET 异步 加载音乐列表、用户信息 提交数据 (POST) POST 异步 登录、表单提交 文件上传 (FormData) POST 异步 上传音乐文件 流媒体 (Streaming) GET 异步 播放音乐(支持断点续传) WebSocket - 异步 实时通信(聊天、播放同步) ### 为什么都是异步?

  1. 前端 :现代前端使用 fetchaxios ,都是基于 Promise 的异步请求。
  2. 后端 :FastAPI 是异步框架,使用 async/await 处理请求,适合高并发场景(如音乐流媒体)。
  3. 用户体验 :异步交互不会阻塞页面,用户可以继续操作(如浏览其他音乐)。

扩展建议

  1. 使用 axios 替代 fetch (更友好、支持拦截器) ``` import axios from "axios"; const response = await axios.get("/music/");

2. 添加加载状态 (提升用户体验) ```
async function loadMusicList() {
    setLoading(true);
    try {
        const response = await fetch(...);
        // ...
    } finally {
        setLoading(false);
    }
}
  1. 优化流媒体传输 (使用 Range 请求) ``` @app.get("/stream/{music_id}") async def stream_music(music_id: str, range: str = Header(None)):

    处理 Range 请求(支持断点续传)

    return FileResponse(..., headers={"Accept-Ranges": "bytes"})

 这样你的音乐网站就能高效、流畅地运行!🎵 # 使用 Axios 改写的音乐网站前端
 下面是将之前使用 `fetch` 的音乐网站前端代码改用 `axios` 实现的完整版本。Axios 提供了更简洁的 API、自动 JSON 转换、请求/响应拦截器等优势。 ## HTML 部分 (保持不变)
音乐网站 (Axios 版本) /* 之前的样式代码 */

音乐网站 (Axios 版本)

<div class="container">
    <div class="auth-section" id="authSection">
        <!-- 登录表单保持不变 -->
    </div>
    
    <div class="upload-section" id="uploadSection" style="display: none;">
        <!-- 上传表单保持不变 -->
    </div>
    
    <h2>音乐列表</h2>
    <div class="music-list" id="musicList">
        <!-- 音乐列表将通过JavaScript动态加载 -->
    </div>
</div>

<div class="player" id="player" style="display: none;">
    <!-- 播放器控件保持不变 -->
</div>

<!-- 引入 Axios -->
<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
<script>
    // JavaScript 部分将用 Axios 重写
</script>
``` ## JavaScript 部分 (Axios 实现) ``` // 配置 let accessToken = null; let currentUser = null; let musicList = []; let currentPlayingIndex = -1; const API_BASE_URL = 'http://localhost:8000';

// 创建 Axios 实例 const api = axios.create({ baseURL: API_BASE_URL, timeout: 5000, });

// 请求拦截器 (用于添加 JWT Token) api.interceptors.request.use(config => { if (accessToken) { config.headers.Authorization = Bearer ${accessToken}; } return config; }, error => { return Promise.reject(error); });

// 响应拦截器 (统一处理错误) api.interceptors.response.use(response => { return response; }, error => { if (error.response) { switch (error.response.status) { case 401: alert('认证失败,请重新登录'); logout(); break; case 403: alert('没有权限执行此操作'); break; case 404: alert('请求的资源不存在'); break; default: alert(请求错误: ${error.response.status}); } } else if (error.request) { alert('网络错误,请检查网络连接'); } else { alert('请求配置错误'); } return Promise.reject(error); });

// 登录函数 async function login() { const username = document.getElementById('username').value; const password = document.getElementById('password').value;

try {
    // 使用 URLSearchParams 处理表单数据
    const params = new URLSearchParams();
    params.append('username', username);
    params.append('password', password);
    
    const response = await api.post('/token', params, {
        headers: {
            'Content-Type': 'application/x-www-form-urlencoded'
        }
    });
    
    accessToken = response.data.access_token;
    currentUser = username;
    
    // 更新UI
    document.getElementById('loginForm').style.display = 'none';
    document.getElementById('userInfo').style.display = 'block';
    document.getElementById('displayUsername').textContent = username;
    document.getElementById('uploadSection').style.display = 'block';
    document.getElementById('authMessage').textContent = '';
    
    // 加载音乐列表
    loadMusicList();
} catch (error) {
    document.getElementById('authMessage').textContent = '登录失败,请检查用户名和密码';
}

}

// 登出函数 function logout() { accessToken = null; currentUser = null;

// 更新UI
document.getElementById('loginForm').style.display = 'block';
document.getElementById('userInfo').style.display = 'none';
document.getElementById('uploadSection').style.display = 'none';
document.getElementById('player').style.display = 'none';
document.getElementById('authMessage').textContent = '';

}

// 上传音乐 async function uploadMusic() { if (!accessToken) { alert('请先登录'); return; }

const title = document.getElementById('musicTitle').value;
const artist = document.getElementById('musicArtist').value;
const duration = document.getElementById('musicDuration').value;
const fileInput = document.getElementById('musicFile');
const file = fileInput.files[0];

if (!title || !artist || !duration || !file) {
    document.getElementById('uploadMessage').textContent = '请填写所有字段';
    return;
}

const formData = new FormData();
formData.append('title', title);
formData.append('artist', artist);
formData.append('duration', duration);
formData.append('file', file);

try {
    const response = await api.post('/upload/', formData, {
        headers: {
            'Content-Type': 'multipart/form-data'
        }
    });
    
    document.getElementById('uploadMessage').textContent = '上传成功!';
    document.getElementById('uploadForm').reset();
    
    // 重新加载音乐列表
    loadMusicList();
} catch (error) {
    document.getElementById('uploadMessage').textContent = '上传失败';
}

}

// 加载音乐列表 async function loadMusicList() { try { const response = await api.get('/music/'); musicList = response.data; renderMusicList(); } catch (error) { console.error('加载音乐列表错误:', error); } }

// 渲染音乐列表 function renderMusicList() { const musicListContainer = document.getElementById('musicList'); musicListContainer.innerHTML = '';

musicList.forEach((music, index) => {
    const musicCard = document.createElement('div');
    musicCard.className = 'music-card';
    musicCard.innerHTML = `
        <h3>${music.title}</h3>
        <p>艺术家: ${music.artist}</p>
        <p>时长: ${Math.floor(music.duration / 60)}:${(music.duration % 60).toString().padStart(2, '0')}</p>
        <p>上传者: ${music.uploader}</p>
        <button onclick="playMusic(${index})">播放</button>
    `;
    musicListContainer.appendChild(musicCard);
});

}

// 播放音乐 function playMusic(index) { if (index < 0 || index >= musicList.length) return;

currentPlayingIndex = index;
const music = musicList[index];

document.getElementById('player').style.display = 'flex';
document.getElementById('nowPlaying').innerHTML = `
    正在播放: ${music.title} - ${music.artist}
`;

// 设置音频源
const audioPlayer = document.getElementById('audioPlayer');
audioPlayer.src = `${API_BASE_URL}/stream/${music.id}`;
audioPlayer.play();

}

// 上一首 function previousSong() { if (currentPlayingIndex <= 0) return; playMusic(currentPlayingIndex - 1); }

// 下一首 function nextSong() { if (currentPlayingIndex >= musicList.length - 1) return; playMusic(currentPlayingIndex + 1); }

// 初始化 document.addEventListener('DOMContentLoaded', () => { loadMusicList(); });

 ## Axios 改进点说明
 1. 自动 JSON 处理 : • Axios 自动解析 JSON 响应,无需手动调用 `response.json()` • 示例对比: ```
// fetch
const response = await fetch(url);
const data = await response.json();

// axios
const response = await axios.get(url);
const data = response.data; // 自动解析
  1. 请求/响应拦截器 : • 统一添加 JWT Token 到请求头 • 统一处理错误响应 • 示例: ``` // 请求拦截器 api.interceptors.request.use(config => { if (accessToken) { config.headers.Authorization = Bearer ${accessToken}; } return config; });

// 响应拦截器 api.interceptors.response.use(response => { return response; }, error => { // 统一错误处理 });


3. 更简洁的 API : • 直接使用 `axios.get()` , `axios.post()` 等方法 • 自动处理 Content-Type 头(根据数据类型)
4. 更好的错误处理 : • 区分网络错误、请求错误和响应错误 • 示例: ```
try {
    await axios.get(url);
} catch (error) {
    if (error.response) {
        // 请求已发出,服务器返回状态码不在 2xx 范围
    } else if (error.request) {
        // 请求已发出但没有收到响应
    } else {
        // 设置请求时出错
    }
}
  1. 取消请求 : • 支持使用 CancelToken 取消请求 • 示例: ``` const source = axios.CancelToken.source();

axios.get(url, { cancelToken: source.token });

// 取消请求 source.cancel('操作被用户取消');


6. 上传进度 : • 支持监听上传进度 • 示例: ```
axios.post(url, data, {
    onUploadProgress: progressEvent => {
        const percent = Math.round((progressEvent.loaded * 100) / progressEvent.total);
        console.log(`上传进度: ${percent}%`);
    }
});

总结

使用 Axios 改写的音乐网站前端具有以下优势: 1. 代码更简洁易读 2. 自动处理 JSON 数据 3. 统一的错误处理机制 4. 支持请求/响应拦截 5. 更好的浏览器兼容性(包括 IE11) 6. 提供更多高级功能(取消请求、进度监控等) 这种实现方式更适合生产环境,特别是需要处理复杂请求和错误场景的应用。 ## 和ajax的区别 在 FastAPI 前后端交互中,我们主要使用 fetch API 或 axios 进行异步请求,而传统的 AJAX(基于 XMLHttpRequest ) 也是一种前后端交互方式。以下是它们的核心区别: ## 1. 基本概念 特性 Fetch API AJAX (XMLHttpRequest) 诞生时间 ES6(2015)引入,较新 1999年由微软提出,较老 语法 基于 Promise ,更简洁 基于回调函数,代码冗长 数据格式 默认支持 JSONFormDataBlob 需要手动解析 JSON 错误处理 需检查 response.ok 通过 onerror 回调处理 兼容性 现代浏览器(IE 不兼容) 所有浏览器(包括旧版 IE) ## 2. 代码对比

(1) 发起 GET 请求

Fetch API
async function getMusicList() {
   try {
       const response = await fetch("/music/");
       if (!response.ok) throw new Error("请求失败");
       const data = await response.json();
       console.log(data);
   } catch (error) {
       console.error("Error:", error);
   }
}
AJAX (XMLHttpRequest)
function getMusicList() {
   const xhr = new XMLHttpRequest();
   xhr.open("GET", "/music/");
   xhr.onload = function() {
       if (xhr.status === 200) {
           const data = JSON.parse(xhr.responseText);
           console.log(data);
       } else {
           console.error("请求失败");
       }
   };
   xhr.onerror = function() {
       console.error("网络错误");
   };
   xhr.send();
}

(2) 发起 POST 请求(提交 JSON)

Fetch API
async function login(username, password) {
   const response = await fetch("/token", {
       method: "POST",
       headers: { "Content-Type": "application/json" },
       body: JSON.stringify({ username, password }),
   });
   const data = await response.json();
}
AJAX (XMLHttpRequest)
function login(username, password) {
   const xhr = new XMLHttpRequest();
   xhr.open("POST", "/token");
   xhr.setRequestHeader("Content-Type", "application/json");
   xhr.onload = function() {
       const data = JSON.parse(xhr.responseText);
   };
   xhr.send(JSON.stringify({ username, password }));
}

3. 核心区别

对比项 Fetch API AJAX (XMLHttpRequest) 语法简洁性 ✅ 更简洁( async/await ) ❌ 回调嵌套,代码冗长 Promise 支持 ✅ 原生基于 Promise ❌ 需手动封装成 Promise Streaming 支持 ✅ 支持 response.body (流式读取) ❌ 不支持 请求取消 ✅ 通过 AbortController ✅ 通过 xhr.abort() 超时控制 ❌ 需手动封装 ✅ 原生支持 xhr.timeout 进度监控 ❌ 不支持 ✅ 通过 xhr.upload.onprogress 兼容性 ❌ 不兼容 IE ✅ 兼容所有浏览器 ## 4. 为什么现代项目更推荐 fetch 或 axios?

  1. 更简洁的语法 • fetch 使用 Promise ,配合 async/await 代码更清晰。 • AJAX 需要手动处理回调,容易陷入“回调地狱”。
  2. 更好的数据格式支持 • fetch 直接支持 JSONFormDataBlob 等格式。 • AJAX 需要手动解析 JSON 或设置 xhr.responseType
  3. 更现代的扩展功能 • fetch 支持 Streaming (流式读取大数据)。 • axios 提供拦截器、自动转换 JSON、请求取消等功能。
  4. 未来趋势 • 新浏览器已全面支持 fetch ,而 XMLHttpRequest 是旧技术。

5. 什么情况下仍需要用 AJAX?

  1. 需要兼容旧版浏览器(如 IE11) • fetch 不兼容 IE,但 axios 内部会回退到 XMLHttpRequest
  2. 需要上传进度监控 ``` xhr.upload.onprogress = (event) => { const percent = Math.round((event.loaded / event.total) * 100); console.log(上传进度: ${percent}%); };

3. 需要精确控制超时 ```
xhr.timeout = 5000; // 5秒超时
xhr.ontimeout = () => console.log("请求超时");

6. 总结

技术 推荐场景 不推荐场景 Fetch API 现代浏览器、简单请求 需要进度监控、兼容 IE Axios 需要拦截器、自动 JSON 转换、兼容 IE 对包体积敏感( axios 需要额外引入) AJAX 需要上传进度、超时控制、兼容旧代码 新项目开发 建议: • 新项目优先用 fetchaxios (后者功能更全)。 • 旧项目或特殊需求(如上传进度)才用 XMLHttpRequest

原文链接: www.cnblogs.com/codedingzhe…