Harmony OS5 基于axios进行封装

306 阅读2分钟

基于Axios的ArkTS网络请求封装指南

在现代前端开发中,网络请求是必不可少的部分。Axios作为最流行的HTTP客户端之一,提供了简洁的API和强大的功能。本文将介绍如何在ArkTS中基于Axios进行网络请求的封装,以提高代码的可维护性和复用性。

1. 安装Axios

首先,确保你的项目已经安装了Axios:

bash
复制
npm install axios
# 或者
yarn add axios

2. 基础封装实现

2.1 创建基础请求类

typescript
复制
// utils/http.ts
import axios, { AxiosInstance, AxiosRequestConfig, AxiosResponse } from 'axios';

class HttpRequest {
  private instance: AxiosInstance;
  
  constructor(config: AxiosRequestConfig) {
    this.instance = axios.create(config);
    
    // 请求拦截器
    this.instance.interceptors.request.use(
      (config) => {
        // 可以在这里添加token等公共请求头
        const token = localStorage.getItem('token');
        if (token) {
          config.headers.Authorization = `Bearer ${token}`;
        }
        return config;
      },
      (error) => {
        return Promise.reject(error);
      }
    );
    
    // 响应拦截器
    this.instance.interceptors.response.use(
      (response: AxiosResponse) => {
        // 对响应数据做统一处理
        const { data } = response;
        if (data.code === 200) {
          return data;
        } else {
          // 处理业务错误
          return Promise.reject(new Error(data.message || '请求失败'));
        }
      },
      (error) => {
        // 处理HTTP错误
        if (error.response) {
          switch (error.response.status) {
            case 401:
              // 跳转到登录页
              break;
            case 403:
              // 提示权限不足
              break;
            case 404:
              // 提示资源不存在
              break;
            case 500:
              // 提示服务器错误
              break;
          }
        }
        return Promise.reject(error);
      }
    );
  }
  
  public request<T = any>(config: AxiosRequestConfig): Promise<T> {
    return this.instance.request(config);
  }
  
  public get<T = any>(url: string, config?: AxiosRequestConfig): Promise<T> {
    return this.instance.get(url, config);
  }
  
  public post<T = any>(url: string, data?: any, config?: AxiosRequestConfig): Promise<T> {
    return this.instance.post(url, data, config);
  }
  
  public put<T = any>(url: string, data?: any, config?: AxiosRequestConfig): Promise<T> {
    return this.instance.put(url, data, config);
  }
  
  public delete<T = any>(url: string, config?: AxiosRequestConfig): Promise<T> {
    return this.instance.delete(url, config);
  }
}

// 创建默认实例
const http = new HttpRequest({
  baseURL: process.env.API_BASE_URL || '/api',
  timeout: 10000,
  headers: {
    'Content-Type': 'application/json',
  },
});

export default http;

2.2 使用封装后的请求

typescript
复制
// api/user.ts
import http from '../utils/http';

export const getUserInfo = (userId: string) => {
  return http.get(`/user/${userId}`);
};

export const updateUserInfo = (userId: string, data: any) => {
  return http.put(`/user/${userId}`, data);
};

export const login = (username: string, password: string) => {
  return http.post('/auth/login', { username, password });
};

3. 高级功能扩展

3.1 添加取消请求功能

typescript
复制
// 在HttpRequest类中添加
private cancelTokenSource = axios.CancelToken.source();

public cancelRequest(message?: string) {
  this.cancelTokenSource.cancel(message);
  this.cancelTokenSource = axios.CancelToken.source(); // 重置
}

// 修改request方法
public request<T = any>(config: AxiosRequestConfig): Promise<T> {
  const newConfig = {
    ...config,
    cancelToken: this.cancelTokenSource.token
  };
  return this.instance.request(newConfig);
}

3.2 添加请求重试机制

typescript
复制
// 在响应拦截器中添加重试逻辑
this.instance.interceptors.response.use(
  (response) => response,
  async (error) => {
    const config = error.config;
    
    // 如果配置了重试次数且当前重试次数小于配置值
    if (config.retry && config.retryCount < config.retry) {
      config.retryCount = config.retryCount || 0;
      config.retryCount++;
      
      // 等待一段时间后重试
      await new Promise(resolve => setTimeout(resolve, config.retryDelay || 1000));
      
      return this.instance(config);
    }
    
    return Promise.reject(error);
  }
);

3.3 添加缓存功能

typescript
复制
// 在HttpRequest类中添加缓存功能
private cache = new Map<string, any>();

public getWithCache<T = any>(url: string, config?: AxiosRequestConfig & { cacheKey?: string, cacheTime?: number }): Promise<T> {
  const cacheKey = config?.cacheKey || url;
  const cacheTime = config?.cacheTime || 60000; // 默认缓存1分钟
  
  if (this.cache.has(cacheKey)) {
    return Promise.resolve(this.cache.get(cacheKey));
  }
  
  return this.get(url, config).then(response => {
    this.cache.set(cacheKey, response);
    setTimeout(() => this.cache.delete(cacheKey), cacheTime);
    return response;
  });
}

4. 最佳实践建议

  1. ​统一错误处理​​:在拦截器中统一处理错误,避免在每个请求中重复处理
  2. ​类型安全​​:为API响应定义明确的类型,提高代码的可维护性
  3. ​环境配置​​:根据不同的环境(开发、测试、生产)配置不同的baseURL
  4. ​请求节流​​:对于频繁触发的请求(如搜索建议),添加防抖或节流机制
  5. ​Mock数据​​:开发阶段可以使用Mock数据,便于前后端并行开发

5. 完整示例

typescript
复制
// utils/http.ts
import axios, { AxiosInstance, AxiosRequestConfig, AxiosResponse } from 'axios';

interface CustomConfig extends AxiosRequestConfig {
  retry?: number;
  retryCount?: number;
  retryDelay?: number;
  cacheKey?: string;
  cacheTime?: number;
}

class HttpRequest {
  private instance: AxiosInstance;
  private cancelTokenSource = axios.CancelToken.source();
  private cache = new Map<string, any>();
  
  constructor(config: AxiosRequestConfig) {
    this.instance = axios.create(config);
    this.setupInterceptors();
  }
  
  private setupInterceptors() {
    // 请求拦截器
    this.instance.interceptors.request.use(
      (config) => {
        const token = localStorage.getItem('token');
        if (token) {
          config.headers.Authorization = `Bearer ${token}`;
        }
        return config;
      },
      (error) => Promise.reject(error)
    );
    
    // 响应拦截器
    this.instance.interceptors.response.use(
      (response: AxiosResponse) => {
        const { data } = response;
        if (data.code === 200) {
          return data;
        }
        return Promise.reject(new Error(data.message || '请求失败'));
      },
      async (error) => {
        const config = error.config as CustomConfig;
        
        // 请求重试
        if (config.retry && (config.retryCount || 0) < config.retry) {
          config.retryCount = (config.retryCount || 0) + 1;
          await new Promise(resolve => setTimeout(resolve, config.retryDelay || 1000));
          return this.instance(config);
        }
        
        // 错误处理
        if (error.response) {
          // 处理HTTP错误状态码
          // ...
        }
        
        return Promise.reject(error);
      }
    );
  }
  
  public cancelRequest(message?: string) {
    this.cancelTokenSource.cancel(message);
    this.cancelTokenSource = axios.CancelToken.source();
  }
  
  public request<T = any>(config: CustomConfig): Promise<T> {
    const newConfig = {
      ...config,
      cancelToken: this.cancelTokenSource.token
    };
    return this.instance.request(newConfig);
  }
  
  public get<T = any>(url: string, config?: CustomConfig): Promise<T> {
    return this.request({ ...config, method: 'GET', url });
  }
  
  public getWithCache<T = any>(url: string, config?: CustomConfig): Promise<T> {
    const cacheKey = config?.cacheKey || url;
    const cacheTime = config?.cacheTime || 60000;
    
    if (this.cache.has(cacheKey)) {
      return Promise.resolve(this.cache.get(cacheKey));
    }
    
    return this.get(url, config).then(response => {
      this.cache.set(cacheKey, response);
      setTimeout(() => this.cache.delete(cacheKey), cacheTime);
      return response;
    });
  }
  
  public post<T = any>(url: string, data?: any, config?: CustomConfig): Promise<T> {
    return this.request({ ...config, method: 'POST', url, data });
  }
  
  public put<T = any>(url: string, data?: any, config?: CustomConfig): Promise<T> {
    return this.request({ ...config, method: 'PUT', url, data });
  }
  
  public delete<T = any>(url: string, config?: CustomConfig): Promise<T> {
    return this.request({ ...config, method: 'DELETE', url });
  }
}

const http = new HttpRequest({
  baseURL: process.env.API_BASE_URL || '/api',
  timeout: 10000,
  headers: {
    'Content-Type': 'application/json',
  },
});

export default http;

结语

通过以上封装,我们实现了Axios在ArkTS中的高级封装,包括基础请求、拦截器、取消请求、请求重试和缓存等功能。这种封装方式可以提高代码的复用性,统一错误处理,并使网络请求更加健壮和易于维护。根据项目实际需求,你可以进一步扩展或调整这些功能。