📚 目录

ℹ️ 说明:axios 三方库在适配开源鸿蒙过程不涉及代码修改,可直接引入。

🎯 目标与背景

📌 项目需求

在 React Native + 开源鸿蒙项目中,需要集成 Axios 库来实现网络请求功能,并通过 GitCode API 获取并展示用户信息。

🧰 技术选型

  • Axios: 基于 Promise 的 HTTP 客户端,支持浏览器和 Node.js
  • 优势:
    • 自动转换 JSON 数据
    • 请求/响应拦截器
    • 取消请求
    • 超时处理
    • 更好的错误处理机制
    • 支持 TypeScript

🔗 API 接口

  • 接口地址: https://api.gitcode.com/api/v5/users/{username}
  • 请求方法: GET
  • 返回格式: JSON
  • 示例: https://api.gitcode.com/api/v5/users/CodexBai

⚡️ Axios 简介与优势

为什么选择 Axios?

相比 React Native 原生的 fetch API,Axios 提供了更丰富的功能和更好的开发体验:

特性 Fetch Axios
自动 JSON 转换 ❌ 需要手动调用 .json() ✅ 自动转换
请求超时 ❌ 需要手动实现 ✅ 内置支持
请求取消 ⚠️ 需要 AbortController ✅ 内置支持
拦截器 ❌ 不支持 ✅ 支持请求/响应拦截
错误处理 ⚠️ 需要手动检查状态码 ✅ 自动处理 HTTP 错误
TypeScript 支持 ⚠️ 需要额外类型定义 ✅ 原生支持
请求/响应数据转换 ❌ 不支持 ✅ 支持

🛠️ 安装与配置

📦 使用 npm 安装

在RNOH Bundle工程根目录打开终端,执行以下命令安装axios

npm install axios@1.6.7

ℹ️ 说明:@1.6.7 为指定安装版本。

image-20251121145503923

image-20251121175120773

🧩 项目集成步骤

🗂️ 目录结构

bundles/ 目录下创建新的模块 gitcodeProfile/

bundles/
└── gitcodeProfile/
    ├── api.ts          # API 接口定义和请求函数
    ├── hooks.ts        # 自定义 Hook(数据获取逻辑)
    ├── screen.tsx      # UI 组件(用户信息展示)
    └── index.ts        # 模块导出

🔧 集成到主应用

  1. 更新 bundles/tabs.ts:添加新的 Tab 项
  2. 更新 App.tsx:扩展 TabKey 类型

🧠 代码实现详解

1️⃣ API 接口层 (api.ts)

✍️ 类型定义
export interface GitCodeUser {
  id: number;
  login: string;
  name: string;
  email?: string;
  avatar_url?: string;
  bio?: string;
  blog?: string;
  company?: string;
  location?: string;
  public_repos?: number;
  public_gists?: number;
  followers?: number;
  following?: number;
  created_at?: string;
  updated_at?: string;
}

📝 设计要点

  • 使用 ? 标记可选字段,因为 API 可能不返回所有字段
  • 明确的类型定义有助于 TypeScript 类型检查和 IDE 自动补全
📨 请求函数实现
import axios, { AxiosResponse } from 'axios';

export async function fetchGitCodeUser(username: string = 'CodexBai'): Promise<GitCodeUser> {
  try {
    const url = `https://api.gitcode.com/api/v5/users/${username}`;
    const response: AxiosResponse<GitCodeUser> = await axios.get(url, {
      timeout: 10000, // 10秒超时
      headers: {
        'Accept': 'application/json',
        'User-Agent': 'DailyHotBundle/1.0',
      },
    });
    return response.data;
  } catch (error) {
    // 错误处理逻辑(见下文)
  }
}

🔧 关键配置说明

  1. timeout: 设置请求超时时间为 10 秒,避免长时间等待
  2. headers:
    • Accept: 指定期望的响应格式
    • User-Agent: 标识客户端应用
  3. 类型泛型: AxiosResponse<GitCodeUser> 确保响应数据的类型安全

2️⃣ 错误处理详解

Axios 的错误处理比 fetch 更加完善:

catch (error) {
  if (axios.isAxiosError(error)) {
    // Axios 错误(网络错误、HTTP 错误等)
    if (error.response) {
      // 服务器返回了错误状态码(4xx, 5xx)
      throw new Error(
        `请求失败: ${error.response.status} - ${error.response.statusText}`
      );
    } else if (error.request) {
      // 请求已发出但没有收到响应(网络问题)
      throw new Error('网络请求失败,请检查网络连接');
    } else {
      // 请求配置错误
      throw new Error(`请求配置错误: ${error.message}`);
    }
  } else {
    // 非 Axios 错误
    throw new Error(`未知错误: ${String(error)}`);
  }
}

📚 错误类型说明

错误类型 触发条件 处理方式
error.response 服务器返回 4xx/5xx 显示 HTTP 状态码和错误信息
error.request 网络连接失败 提示用户检查网络
其他错误 配置错误等 显示通用错误信息

3️⃣ 自定义 Hook (hooks.ts)

🔄 实现数据获取逻辑
import { useEffect, useState } from 'react';
import { fetchGitCodeUser, GitCodeUser } from './api';

export function useGitCodeUser(username: string = 'CodexBai') {
  const [data, setData] = useState<GitCodeUser | null>(null);
  const [loading, setLoading] = useState<boolean>(false);
  const [error, setError] = useState<string | null>(null);

  const fetchData = async () => {
    let mounted = true;
    setLoading(true);
    setError(null);
    try {
      const userData = await fetchGitCodeUser(username);
      if (mounted) {
        setData(userData);
      }
    } catch (e) {
      if (mounted) {
        setError(String(e instanceof Error ? e.message : e));
      }
    } finally {
      if (mounted) {
        setLoading(false);
      }
    }
  };

  useEffect(() => {
    fetchData();
    return () => {
      // 清理函数:防止组件卸载后更新状态
    };
  }, [username]);

  return { data, loading, error, refetch: fetchData };
}

📝 设计要点

  1. 状态管理

    • data: 存储用户数据
    • loading: 加载状态
    • error: 错误信息
  2. 内存泄漏防护

    • 使用 mounted 标志防止组件卸载后更新状态
    • useEffect 的清理函数中重置标志
  3. 暴露 refetch 方法

    • 允许外部手动触发数据刷新
    • 支持下拉刷新等交互

4️⃣ UI 组件实现 (screen.tsx)

⭐ 核心功能
  1. 加载状态展示
  2. 错误处理与重试
  3. 用户信息展示
  4. 下拉刷新
🧷 关键代码片段
export function GitCodeProfileScreen() {
  const { data, loading, error, refetch } = useGitCodeUser('CodexBai');
  const [refreshing, setRefreshing] = React.useState(false);

  const onRefresh = React.useCallback(async () => {
    setRefreshing(true);
    await refetch();
    setRefreshing(false);
  }, [refetch]);

  // 加载中状态
  if (loading && !data) {
    return (
      <View style={styles.centerContainer}>
        <ActivityIndicator size="large" color="#007AFF" />
        <Text style={styles.loadingText}>加载中...</Text>
      </View>
    );
  }

  // 错误状态
  if (error) {
    return (
      <ScrollView
        contentContainerStyle={styles.centerContainer}
        refreshControl={
          <RefreshControl refreshing={refreshing} onRefresh={onRefresh} />
        }
      >
        <Text style={styles.errorText}>❌ 加载失败</Text>
        <Text style={styles.errorDetail}>{error}</Text>
        <Pressable style={styles.retryButton} onPress={onRefresh}>
          <Text style={styles.retryButtonText}>重试</Text>
        </Pressable>
      </ScrollView>
    );
  }

  // 数据展示
  return (
    <ScrollView
      style={styles.container}
      refreshControl={
        <RefreshControl refreshing={refreshing} onRefresh={onRefresh} />
      }
    >
      {/* 用户信息展示 */}
    </ScrollView>
  );
}

🚨 错误处理与异常管理

🌐 网络错误处理

// 在 api.ts 中
catch (error) {
  if (axios.isAxiosError(error)) {
    if (error.code === 'ECONNABORTED') {
      throw new Error('请求超时,请稍后重试');
    } else if (error.code === 'ENOTFOUND' || error.code === 'ECONNREFUSED') {
      throw new Error('无法连接到服务器,请检查网络');
    }
    // ... 其他错误处理
  }
}

🔢 HTTP 状态码处理

if (error.response) {
  switch (error.response.status) {
    case 404:
      throw new Error('用户不存在');
    case 403:
      throw new Error('访问被拒绝');
    case 500:
      throw new Error('服务器错误,请稍后重试');
    default:
      throw new Error(`请求失败: ${error.response.status}`);
  }
}

🙂 用户友好的错误提示

在 UI 层展示错误时,使用友好的提示信息:

<Text style={styles.errorText}>❌ 加载失败</Text>
<Text style={styles.errorDetail}>{error}</Text>
<Pressable style={styles.retryButton} onPress={onRefresh}>
  <Text style={styles.retryButtonText}>重试</Text>
</Pressable>

🪝 自定义 Hook 封装

🧱 Hook 设计模式

自定义 Hook 的优势:

  • 逻辑复用: 多个组件可以共享数据获取逻辑
  • 关注点分离: UI 组件专注于展示,Hook 处理数据逻辑
  • 易于测试: 可以单独测试 Hook 逻辑

➕ 扩展功能

可以进一步扩展 Hook,添加更多功能:

export function useGitCodeUser(username: string = 'CodexBai') {
  // ... 基础实现

  // 添加缓存机制
  const [cache, setCache] = useState<Map<string, GitCodeUser>>(new Map());

  // 添加重试机制
  const retry = async (maxRetries = 3) => {
    for (let i = 0; i < maxRetries; i++) {
      try {
        return await fetchGitCodeUser(username);
      } catch (e) {
        if (i === maxRetries - 1) throw e;
        await new Promise(resolve => setTimeout(resolve, 1000 * (i + 1)));
      }
    }
  };

  return { data, loading, error, refetch, retry };
}

🎨 UI 组件实现

🧩 组件结构

GitCodeProfileScreen
├── 加载状态 (ActivityIndicator)
├── 错误状态 (错误信息 + 重试按钮)
└── 数据展示
    ├── 头部 (头像 + 姓名 + 简介)
    ├── 统计信息 (仓库数、粉丝数、关注数)
    └── 详细信息 (公司、位置、博客等)

🧵 样式设计要点

  1. 卡片式布局: 使用白色背景和阴影效果
  2. 响应式设计: 适配不同屏幕尺寸
  3. 加载状态: 清晰的加载指示器
  4. 错误处理: 友好的错误提示和重试按钮

🏆 最佳实践与优化建议

⚙️ Axios 实例配置

创建 Axios 实例,统一配置:

// utils/axiosInstance.ts
import axios from 'axios';

const axiosInstance = axios.create({
  baseURL: 'https://api.gitcode.com/api/v5',
  timeout: 10000,
  headers: {
    'Accept': 'application/json',
    'User-Agent': 'DailyHotBundle/1.0',
  },
});

// 请求拦截器
axiosInstance.interceptors.request.use(
  (config) => {
    // 可以在这里添加 token 等
    return config;
  },
  (error) => {
    return Promise.reject(error);
  }
);

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

export default axiosInstance;

⛔ 请求取消

处理组件卸载时的请求取消:

useEffect(() => {
  const cancelTokenSource = axios.CancelToken.source();
  
  fetchGitCodeUser(username, {
    cancelToken: cancelTokenSource.token
  }).then(setData).catch(setError);

  return () => {
    cancelTokenSource.cancel('Component unmounted');
  };
}, [username]);

🔁 请求重试机制

实现自动重试:

async function fetchWithRetry(url: string, retries = 3) {
  for (let i = 0; i < retries; i++) {
    try {
      return await axios.get(url);
    } catch (error) {
      if (i === retries - 1) throw error;
      await new Promise(resolve => setTimeout(resolve, 1000 * (i + 1)));
    }
  }
}

🗃️ 缓存策略

实现简单的内存缓存:

const cache = new Map<string, { data: GitCodeUser; timestamp: number }>();
const CACHE_DURATION = 5 * 60 * 1000; // 5分钟

export async function fetchGitCodeUser(username: string): Promise<GitCodeUser> {
  const cached = cache.get(username);
  if (cached && Date.now() - cached.timestamp < CACHE_DURATION) {
    return cached.data;
  }
  
  const data = await axios.get(`/users/${username}`);
  cache.set(username, { data, timestamp: Date.now() });
  return data;
}

🧬 TypeScript 类型安全

充分利用 TypeScript 类型:

// 定义响应类型
interface ApiResponse<T> {
  data: T;
  status: number;
  message?: string;
}

// 使用泛型
async function fetchData<T>(url: string): Promise<T> {
  const response = await axios.get<ApiResponse<T>>(url);
  return response.data.data;
}

✅ 测试与验证

🧪 功能测试

  • ✅ 正常加载用户信息
  • ✅ 网络错误处理
  • ✅ 超时处理
  • ✅ 下拉刷新
  • ✅ 错误重试

🧨 边界情况测试

  • ✅ 用户不存在(404)
  • ✅ 网络断开
  • ✅ 服务器错误(500)
  • ✅ 组件卸载时的内存泄漏

▶️ 运行验证

# Android
npm run android

# iOS
npm run ios

# 开源鸿蒙
npm run harmony

image-20251121181037111

📥 5. 将新的 bundle.harmony.js 文件拷贝到开源鸿蒙壳工程资源目录 rawfile 中,替换已有的 bundle.harmony.js

image-20251121181815953

▶️ 6. 运行应用

image-20251121181628929

❓ 常见问题与解决方案

❓ Q1: Axios 在 React Native 中无法使用?

A: React Native 0.72+ 已经内置了网络请求支持,Axios 可以直接使用。如果遇到问题,检查:

  • 网络权限配置(Android: AndroidManifest.xml
  • iOS 网络安全配置(iOS 9+ 需要 HTTPS)
  • 开源鸿蒙需要在module.json5配置文件中配置网络权限

❓ Q2: 如何处理 CORS 错误?

A: React Native 不受浏览器 CORS 限制,但如果 API 服务器有 CORS 限制,需要:

  • 联系后端开发者配置 CORS
  • 或使用代理服务器

❓ Q3: 请求超时如何设置?

A: 在 Axios 配置中设置 timeout

axios.get(url, {
  timeout: 10000, // 10秒
});

❓ Q4: 如何取消正在进行的请求?

A: 使用 CancelTokenAbortController

const source = axios.CancelToken.source();
axios.get(url, { cancelToken: source.token });
source.cancel('Operation canceled');

❓ Q5: 如何处理并发请求?

A: 使用 Promise.allaxios.all

const [user1, user2] = await Promise.all([
  fetchGitCodeUser('user1'),
  fetchGitCodeUser('user2'),
]);

📌 总结

🧾 核心收获

  1. Axios 集成: 成功在 React Native 项目中集成 Axios,实现了更强大的网络请求功能
  2. 错误处理: 完善的错误处理机制,提升用户体验
  3. 代码组织: 清晰的分层架构(API → Hook → UI),易于维护和扩展
  4. TypeScript: 充分利用类型系统,提高代码质量和开发效率

🗂️ 项目结构

bundles/gitcodeProfile/
├── api.ts          # API 接口层:类型定义 + 请求函数
├── hooks.ts        # 数据层:自定义 Hook
├── screen.tsx      # UI 层:用户界面
└── index.ts        # 导出层:模块导出

🧰 技术栈

  • React Native 0.72.5: 跨平台框架
  • TypeScript 4.8.4: 类型安全
  • Axios 1.6.7: HTTP 客户端
  • 开源鸿蒙适配: 支持鸿蒙平台

🔗 参考资源

Logo

作为“人工智能6S店”的官方数字引擎,为AI开发者与企业提供一个覆盖软硬件全栈、一站式门户。

更多推荐