Skip to content

网络请求:Axios 库

React Native 进阶

Axios 是一个流行的 HTTP 客户端库,它提供了更强大、更灵活的网络请求功能,相比原生的 Fetch API,Axios 提供了更多的特性,如拦截器、自动转换 JSON 数据、请求取消等。本文将详细介绍如何在 React Native 中使用 Axios 进行网络请求。

1. 安装 Axios

首先,需要安装 Axios 库。

bash
# 使用 npm
npm install axios

# 使用 yarn
yarn add axios

2. 基本用法

发送 GET 请求

使用 Axios 发送 GET 请求,获取服务器数据。

jsx
import React, { useState, useEffect } from 'react';
import { View, Text, StyleSheet, FlatList, ActivityIndicator } from 'react-native';
import axios from 'axios';

export default function AxiosGetExample() {
  const [data, setData] = useState([]);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState(null);

  useEffect(() => {
    fetchData();
  }, []);

  const fetchData = async () => {
    try {
      setLoading(true);
      const response = await axios.get('https://jsonplaceholder.typicode.com/posts');
      setData(response.data);
      setError(null);
    } catch (err) {
      setError(err.message);
      setData([]);
    } finally {
      setLoading(false);
    }
  };

  const renderItem = ({ item }) => (
    <View style={styles.item}>
      <Text style={styles.title}>{item.title}</Text>
      <Text style={styles.body}>{item.body}</Text>
    </View>
  );

  if (loading) {
    return (
      <View style={styles.center}>
        <ActivityIndicator size="large" color="#4CAF50" />
        <Text style={styles.loadingText}>加载中...</Text>
      </View>
    );
  }

  if (error) {
    return (
      <View style={styles.center}>
        <Text style={styles.errorText}>{error}</Text>
      </View>
    );
  }

  return (
    <FlatList
      data={data}
      renderItem={renderItem}
      keyExtractor={item => item.id.toString()}
      style={styles.container}
      contentContainerStyle={styles.content}
    />
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#f5f5f5',
  },
  content: {
    padding: 16,
  },
  item: {
    backgroundColor: '#fff',
    padding: 16,
    marginBottom: 12,
    borderRadius: 8,
    shadowColor: '#000',
    shadowOffset: {
      width: 0,
      height: 2,
    },
    shadowOpacity: 0.1,
    shadowRadius: 3.84,
    elevation: 5,
  },
  title: {
    fontSize: 18,
    fontWeight: 'bold',
    marginBottom: 8,
  },
  body: {
    fontSize: 14,
    color: '#666',
  },
  center: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
  },
  loadingText: {
    marginTop: 10,
    fontSize: 16,
    color: '#666',
  },
  errorText: {
    fontSize: 16,
    color: '#f44336',
  },
});

发送 POST 请求

使用 Axios 发送 POST 请求,向服务器提交数据。

jsx
import React, { useState } from 'react';
import { View, Text, StyleSheet, TextInput, TouchableOpacity, Alert } from 'react-native';
import axios from 'axios';

export default function AxiosPostExample() {
  const [title, setTitle] = useState('');
  const [body, setBody] = useState('');
  const [loading, setLoading] = useState(false);
  const [responseData, setResponseData] = useState(null);

  const handleSubmit = async () => {
    if (!title || !body) {
      Alert.alert('提示', '请输入标题和内容');
      return;
    }

    try {
      setLoading(true);
      const response = await axios.post('https://jsonplaceholder.typicode.com/posts', {
        title,
        body,
        userId: 1,
      });

      setResponseData(response.data);
      Alert.alert('成功', '数据提交成功');
    } catch (err) {
      Alert.alert('错误', err.message);
    } finally {
      setLoading(false);
    }
  };

  return (
    <View style={styles.container}>
      <Text style={styles.title}>发送 POST 请求</Text>
      
      <View style={styles.formGroup}>
        <Text style={styles.label}>标题</Text>
        <TextInput
          style={styles.input}
          value={title}
          onChangeText={setTitle}
          placeholder="请输入标题"
        />
      </View>

      <View style={styles.formGroup}>
        <Text style={styles.label}>内容</Text>
        <TextInput
          style={[styles.input, styles.textArea]}
          value={body}
          onChangeText={setBody}
          placeholder="请输入内容"
          multiline
          numberOfLines={4}
        />
      </View>

      <TouchableOpacity
        style={[styles.button, loading && styles.buttonDisabled]}
        onPress={handleSubmit}
        disabled={loading}
      >
        <Text style={styles.buttonText}>
          {loading ? '提交中...' : '提交'}
        </Text>
      </TouchableOpacity>

      {responseData && (
        <View style={styles.response}>
          <Text style={styles.responseTitle}>响应数据:</Text>
          <Text style={styles.responseText}>ID: {responseData.id}</Text>
          <Text style={styles.responseText}>标题: {responseData.title}</Text>
          <Text style={styles.responseText}>内容: {responseData.body}</Text>
        </View>
      )}
    </View>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    padding: 20,
    backgroundColor: '#f5f5f5',
  },
  title: {
    fontSize: 24,
    fontWeight: 'bold',
    marginBottom: 20,
    textAlign: 'center',
  },
  formGroup: {
    marginBottom: 16,
  },
  label: {
    fontSize: 16,
    marginBottom: 8,
    fontWeight: '500',
  },
  input: {
    backgroundColor: '#fff',
    borderWidth: 1,
    borderColor: '#ddd',
    borderRadius: 8,
    padding: 12,
    fontSize: 16,
  },
  textArea: {
    height: 120,
    textAlignVertical: 'top',
  },
  button: {
    backgroundColor: '#4CAF50',
    padding: 16,
    borderRadius: 8,
    alignItems: 'center',
    marginTop: 16,
  },
  buttonDisabled: {
    backgroundColor: '#9e9e9e',
  },
  buttonText: {
    color: '#fff',
    fontSize: 16,
    fontWeight: '500',
  },
  response: {
    marginTop: 24,
    padding: 16,
    backgroundColor: '#e8f5e8',
    borderRadius: 8,
  },
  responseTitle: {
    fontSize: 16,
    fontWeight: 'bold',
    marginBottom: 8,
  },
  responseText: {
    fontSize: 14,
    marginBottom: 4,
  },
});

3. 常用配置

设置请求头

在发送请求时设置自定义请求头。

jsx
import React, { useState, useEffect } from 'react';
import { View, Text, StyleSheet, ActivityIndicator } from 'react-native';
import axios from 'axios';

export default function AxiosHeadersExample() {
  const [data, setData] = useState(null);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState(null);

  useEffect(() => {
    fetchWithHeaders();
  }, []);

  const fetchWithHeaders = async () => {
    try {
      setLoading(true);
      const response = await axios.get('https://jsonplaceholder.typicode.com/posts/1', {
        headers: {
          'Content-Type': 'application/json',
          'Authorization': 'Bearer your-token-here',
        },
      });

      setData(response.data);
      setError(null);
    } catch (err) {
      setError(err.message);
      setData(null);
    } finally {
      setLoading(false);
    }
  };

  if (loading) {
    return (
      <View style={styles.center}>
        <ActivityIndicator size="large" color="#4CAF50" />
        <Text style={styles.loadingText}>加载中...</Text>
      </View>
    );
  }

  if (error) {
    return (
      <View style={styles.center}>
        <Text style={styles.errorText}>{error}</Text>
      </View>
    );
  }

  return (
    <View style={styles.container}>
      <Text style={styles.title}>带请求头的 GET 请求</Text>
      <View style={styles.dataContainer}>
        <Text style={styles.dataLabel}>ID:</Text>
        <Text style={styles.dataValue}>{data.id}</Text>
      </View>
      <View style={styles.dataContainer}>
        <Text style={styles.dataLabel}>标题:</Text>
        <Text style={styles.dataValue}>{data.title}</Text>
      </View>
      <View style={styles.dataContainer}>
        <Text style={styles.dataLabel}>内容:</Text>
        <Text style={styles.dataValue}>{data.body}</Text>
      </View>
    </View>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    padding: 20,
    backgroundColor: '#f5f5f5',
  },
  title: {
    fontSize: 24,
    fontWeight: 'bold',
    marginBottom: 20,
    textAlign: 'center',
  },
  center: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
  },
  loadingText: {
    marginTop: 10,
    fontSize: 16,
    color: '#666',
  },
  errorText: {
    fontSize: 16,
    color: '#f44336',
  },
  dataContainer: {
    backgroundColor: '#fff',
    padding: 16,
    marginBottom: 12,
    borderRadius: 8,
  },
  dataLabel: {
    fontSize: 14,
    color: '#666',
    marginBottom: 4,
  },
  dataValue: {
    fontSize: 16,
    fontWeight: '500',
  },
});

创建 Axios 实例

创建一个配置好的 Axios 实例,方便在整个应用中使用。

jsx
// utils/axiosInstance.js
import axios from 'axios';

const API_BASE_URL = 'https://jsonplaceholder.typicode.com';

const axiosInstance = axios.create({
  baseURL: API_BASE_URL,
  timeout: 10000, // 10秒超时
  headers: {
    'Content-Type': 'application/json',
  },
});

// 请求拦截器
axiosInstance.interceptors.request.use(
  (config) => {
    // 在这里可以添加认证 token 等
    const token = 'your-token-here';
    if (token) {
      config.headers.Authorization = `Bearer ${token}`;
    }
    return config;
  },
  (error) => {
    return Promise.reject(error);
  }
);

// 响应拦截器
axiosInstance.interceptors.response.use(
  (response) => {
    // 在这里可以处理响应数据
    return response;
  },
  (error) => {
    // 在这里可以处理错误
    if (error.response) {
      // 服务器返回错误状态码
      console.error('Response error:', error.response.data);
    } else if (error.request) {
      // 请求已发送但没有收到响应
      console.error('Request error:', error.request);
    } else {
      // 请求配置出错
      console.error('Error:', error.message);
    }
    return Promise.reject(error);
  }
);

export default axiosInstance;

使用创建的 Axios 实例:

jsx
import React, { useState, useEffect } from 'react';
import { View, Text, StyleSheet, ActivityIndicator } from 'react-native';
import axiosInstance from '../utils/axiosInstance';

export default function AxiosInstanceExample() {
  const [data, setData] = useState([]);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState(null);

  useEffect(() => {
    fetchData();
  }, []);

  const fetchData = async () => {
    try {
      setLoading(true);
      const response = await axiosInstance.get('/posts');
      setData(response.data);
      setError(null);
    } catch (err) {
      setError(err.message);
      setData([]);
    } finally {
      setLoading(false);
    }
  };

  if (loading) {
    return (
      <View style={styles.center}>
        <ActivityIndicator size="large" color="#4CAF50" />
        <Text style={styles.loadingText}>加载中...</Text>
      </View>
    );
  }

  if (error) {
    return (
      <View style={styles.center}>
        <Text style={styles.errorText}>{error}</Text>
      </View>
    );
  }

  return (
    <View style={styles.container}>
      <Text style={styles.title}>使用 Axios 实例</Text>
      <Text style={styles.count}>获取到 {data.length} 条数据</Text>
    </View>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    padding: 20,
    backgroundColor: '#f5f5f5',
  },
  title: {
    fontSize: 24,
    fontWeight: 'bold',
    marginBottom: 20,
    textAlign: 'center',
  },
  count: {
    fontSize: 18,
    textAlign: 'center',
  },
  center: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
  },
  loadingText: {
    marginTop: 10,
    fontSize: 16,
    color: '#666',
  },
  errorText: {
    fontSize: 16,
    color: '#f44336',
  },
});

4. 高级功能

处理超时

设置请求超时时间。

jsx
import React, { useState, useEffect } from 'react';
import { View, Text, StyleSheet, ActivityIndicator } from 'react-native';
import axios from 'axios';

export default function AxiosTimeoutExample() {
  const [data, setData] = useState(null);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState(null);

  useEffect(() => {
    fetchWithTimeout();
  }, []);

  const fetchWithTimeout = async () => {
    try {
      setLoading(true);
      const response = await axios.get('https://jsonplaceholder.typicode.com/posts', {
        timeout: 5000, // 5秒超时
      });
      setData(response.data);
      setError(null);
    } catch (err) {
      setError(err.message);
      setData(null);
    } finally {
      setLoading(false);
    }
  };

  if (loading) {
    return (
      <View style={styles.center}>
        <ActivityIndicator size="large" color="#4CAF50" />
        <Text style={styles.loadingText}>加载中...</Text>
      </View>
    );
  }

  if (error) {
    return (
      <View style={styles.center}>
        <Text style={styles.errorText}>{error}</Text>
      </View>
    );
  }

  return (
    <View style={styles.container}>
      <Text style={styles.title}>超时处理示例</Text>
      <Text style={styles.count}>获取到 {data.length} 条数据</Text>
    </View>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    padding: 20,
    backgroundColor: '#f5f5f5',
  },
  title: {
    fontSize: 24,
    fontWeight: 'bold',
    marginBottom: 20,
    textAlign: 'center',
  },
  count: {
    fontSize: 18,
    textAlign: 'center',
  },
  center: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
  },
  loadingText: {
    marginTop: 10,
    fontSize: 16,
    color: '#666',
  },
  errorText: {
    fontSize: 16,
    color: '#f44336',
  },
});

取消请求

实现网络请求的取消功能。

jsx
import React, { useState, useRef } from 'react';
import { View, Text, StyleSheet, TouchableOpacity, ActivityIndicator } from 'react-native';
import axios from 'axios';

export default function AxiosCancelExample() {
  const [data, setData] = useState(null);
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState(null);
  const cancelTokenRef = useRef(null);

  const fetchData = async () => {
    try {
      setLoading(true);
      setError(null);
      
      // 创建取消令牌
      const source = axios.CancelToken.source();
      cancelTokenRef.current = source;

      const response = await axios.get('https://jsonplaceholder.typicode.com/posts', {
        cancelToken: source.token,
      });

      setData(response.data);
    } catch (err) {
      if (axios.isCancel(err)) {
        setError('请求已取消');
      } else {
        setError(err.message);
      }
      setData(null);
    } finally {
      setLoading(false);
    }
  };

  const cancelFetch = () => {
    if (cancelTokenRef.current) {
      cancelTokenRef.current.cancel('请求已取消');
    }
  };

  return (
    <View style={styles.container}>
      <Text style={styles.title}>取消请求示例</Text>
      
      <View style={styles.buttonContainer}>
        <TouchableOpacity
          style={[styles.button, styles.fetchButton, loading && styles.buttonDisabled]}
          onPress={fetchData}
          disabled={loading}
        >
          <Text style={styles.buttonText}>
            {loading ? '加载中...' : '开始请求'}
          </Text>
        </TouchableOpacity>
        
        <TouchableOpacity
          style={[styles.button, styles.cancelButton, !loading && styles.buttonDisabled]}
          onPress={cancelFetch}
          disabled={!loading}
        >
          <Text style={styles.buttonText}>取消请求</Text>
        </TouchableOpacity>
      </View>

      {error && (
        <View style={styles.messageContainer}>
          <Text style={styles.errorText}>{error}</Text>
        </View>
      )}

      {data && (
        <View style={styles.messageContainer}>
          <Text style={styles.successText}>成功获取到 {data.length} 条数据</Text>
        </View>
      )}
    </View>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    padding: 20,
    backgroundColor: '#f5f5f5',
  },
  title: {
    fontSize: 24,
    fontWeight: 'bold',
    marginBottom: 30,
    textAlign: 'center',
  },
  buttonContainer: {
    flexDirection: 'row',
    justifyContent: 'space-between',
    marginBottom: 30,
  },
  button: {
    flex: 1,
    padding: 16,
    borderRadius: 8,
    alignItems: 'center',
    marginHorizontal: 10,
  },
  fetchButton: {
    backgroundColor: '#4CAF50',
  },
  cancelButton: {
    backgroundColor: '#f44336',
  },
  buttonDisabled: {
    backgroundColor: '#9e9e9e',
  },
  buttonText: {
    color: '#fff',
    fontSize: 16,
    fontWeight: '500',
  },
  messageContainer: {
    padding: 16,
    borderRadius: 8,
    marginTop: 20,
  },
  errorText: {
    fontSize: 16,
    color: '#f44336',
  },
  successText: {
    fontSize: 16,
    color: '#4CAF50',
  },
});

并发请求

同时发送多个网络请求。

jsx
import React, { useState, useEffect } from 'react';
import { View, Text, StyleSheet, ActivityIndicator } from 'react-native';
import axios from 'axios';

export default function AxiosConcurrentExample() {
  const [data, setData] = useState({
    posts: null,
    comments: null,
    albums: null,
  });
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState(null);

  useEffect(() => {
    fetchConcurrentData();
  }, []);

  const fetchConcurrentData = async () => {
    try {
      setLoading(true);

      // 同时发送多个请求
      const [postsResponse, commentsResponse, albumsResponse] = await Promise.all([
        axios.get('https://jsonplaceholder.typicode.com/posts'),
        axios.get('https://jsonplaceholder.typicode.com/comments'),
        axios.get('https://jsonplaceholder.typicode.com/albums'),
      ]);

      setData({
        posts: postsResponse.data,
        comments: commentsResponse.data,
        albums: albumsResponse.data,
      });
      setError(null);
    } catch (err) {
      setError(err.message);
      setData({
        posts: null,
        comments: null,
        albums: null,
      });
    } finally {
      setLoading(false);
    }
  };

  if (loading) {
    return (
      <View style={styles.center}>
        <ActivityIndicator size="large" color="#4CAF50" />
        <Text style={styles.loadingText}>加载中...</Text>
      </View>
    );
  }

  if (error) {
    return (
      <View style={styles.center}>
        <Text style={styles.errorText}>{error}</Text>
      </View>
    );
  }

  return (
    <View style={styles.container}>
      <Text style={styles.title}>并发请求示例</Text>
      
      <View style={styles.dataContainer}>
        <Text style={styles.dataTitle}>帖子数量: {data.posts.length}</Text>
        <Text style={styles.dataTitle}>评论数量: {data.comments.length}</Text>
        <Text style={styles.dataTitle}>相册数量: {data.albums.length}</Text>
      </View>
    </View>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    padding: 20,
    backgroundColor: '#f5f5f5',
  },
  title: {
    fontSize: 24,
    fontWeight: 'bold',
    marginBottom: 20,
    textAlign: 'center',
  },
  dataContainer: {
    backgroundColor: '#fff',
    padding: 20,
    borderRadius: 8,
  },
  dataTitle: {
    fontSize: 18,
    marginBottom: 10,
  },
  center: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
  },
  loadingText: {
    marginTop: 10,
    fontSize: 16,
    color: '#666',
  },
  errorText: {
    fontSize: 16,
    color: '#f44336',
  },
});

5. 最佳实践

1. 封装 Axios 实例

创建一个配置好的 Axios 实例,统一处理请求和响应。

2. 使用拦截器

利用 Axios 的拦截器功能,处理认证、错误等通用逻辑。

3. 错误处理

实现全面的错误处理,包括网络错误、服务器错误和业务逻辑错误。

4. 取消请求

在组件卸载或用户操作时,取消正在进行的请求,避免内存泄漏。

5. 超时设置

为请求设置合理的超时时间,避免请求无限期等待。

6. 并发请求

使用 Promise.all 处理并发请求,提高性能。

7. 缓存策略

对于频繁访问的数据,实现合理的缓存策略,减少网络请求。

6. 常见问题与解决方案

问题 1:CORS 错误

问题:在开发过程中遇到 CORS 错误。

解决方案

  • 在开发环境中使用代理服务器
  • 确保服务器设置了正确的 CORS 头
  • 考虑使用 JSONP 或其他跨域解决方案

问题 2:认证失败

问题:需要认证的请求失败。

解决方案

  • 确保正确设置认证头
  • 实现 token 刷新机制
  • 处理认证错误,引导用户重新登录

问题 3:请求取消不生效

问题:调用取消方法后,请求仍然继续执行。

解决方案

  • 确保正确使用 CancelToken
  • 在请求配置中正确设置 cancelToken
  • 检查取消逻辑是否在请求发送前执行

问题 4:拦截器不工作

问题:请求或响应拦截器不执行。

解决方案

  • 确保正确配置拦截器
  • 检查拦截器的返回值
  • 确保使用的是配置了拦截器的 Axios 实例

7. 总结

Axios 是一个功能强大的 HTTP 客户端库,它提供了比原生 Fetch API 更多的特性,如拦截器、自动转换 JSON 数据、请求取消等。通过本文的学习,你应该掌握了以下内容:

  1. 安装和基本使用 Axios
  2. 发送 GET 和 POST 请求
  3. 设置请求头和创建 Axios 实例
  4. 使用拦截器处理请求和响应
  5. 实现超时处理和请求取消
  6. 发送并发请求
  7. 最佳实践和常见问题的解决方案

在实际开发中,合理使用 Axios 可以创建出更加可靠、高效的网络请求功能。通过结合其他技术,如状态管理和缓存策略,可以创建出更加专业、用户友好的应用。

© 2026 编程马·菜鸟教程 版权所有