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

在 FastAPI 网站的前后端交互中,以音乐网站应用为例,主要使用了以下几种交互方法,它们都是异步的(因为 FastAPI 是异步框架,现代前端也主要使用异步交互):


1. 获取数据 (GET 请求)

用途:获取音乐列表、获取单首音乐信息
同步/异步:异步(前端 fetch + 后端 async
示例

// 前端异步获取音乐列表
async function loadMusicList() {
    const response = await fetch(`${API_BASE_URL}/music/`);
    musicList = await response.json();
}
# 后端异步返回数据
@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}`
    });
}
# 后端异步处理登录
@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
    });
}
# 后端异步接收文件
@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}`;
# 后端返回文件流(FastAPI 会自动处理 Range 请求)
@app.get("/stream/{music_id}")
async def stream_music(music_id: str):
    return FileResponse(music.file_path)

5. WebSocket(可选扩展)

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

# 后端 WebSocket
@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);
        }
    }
    
  3. 优化流媒体传输(使用 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 部分 (保持不变)

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>音乐网站 (Axios 版本)</title>
    <!-- 样式部分保持不变 -->
    <style>
        /* 之前的样式代码 */
    </style>
</head>
<body>
    <!-- HTML 结构保持不变 -->
    <header>
        <div class="container">
            <h1>音乐网站 (Axios 版本)</h1>
        </div>
    </header>
    
    <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>
</body>
</html>

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; // 自动解析
    
  2. 请求/响应拦截器
    • 统一添加 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 {
            // 设置请求时出错
        }
    }
    
  5. 取消请求
    • 支持使用 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 APIaxios 进行异步请求,而传统的 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. 为什么现代项目更推荐 fetchaxios

  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

posted @ 2025-04-15 12:03  学不会xuebuhui  阅读(88)  评论(0)    收藏  举报
Language: javascript //图片预览