返回博客列表
Taro小程序跨平台开发

Taro 跨平台小程序的请求层设计

在 Taro 中构建统一的 HTTP 请求层,实现 Web 与小程序的一致 API 调用体验

作者: ekent·发布于 2026年1月31日

用 Taro 做跨平台小程序开发时,请求层是第一个要解决的基础设施问题。Web 端用 fetchaxios,小程序端用 Taro.request——API 不同、能力不同、限制也不同。我们在项目中封装了一套统一的请求层,让业务代码不需要关心运行在哪个平台。

平台差异对比

特性Web (H5)微信小程序
请求 APIfetch / XMLHttpRequestwx.request
Cookie自动携带不支持
跨域限制有(需 CORS)无(但需配置合法域名)
请求并发无限制最多 10 个
HTTPS可选强制

这些差异意味着不能简单地用同一个 HTTP 库。Taro 提供了 Taro.request 作为跨平台封装,但它的功能比较基础,缺少拦截器、自动重试等能力。

基础封装

// lib/request.ts
import Taro from '@tarojs/taro';

interface RequestConfig {
  url: string;
  method?: 'GET' | 'POST' | 'PUT' | 'DELETE';
  data?: object;
  headers?: Record<string, string>;
}

interface ApiResponse<T = any> {
  code: number;
  data: T;
  message: string;
}

const BASE_URL = getBaseUrl();

function getBaseUrl(): string {
  // 根据环境切换
  if (process.env.NODE_ENV === 'development') {
    return 'https://dev-api.example.com';
  }
  return 'https://api.example.com';
}

async function request<T>(config: RequestConfig): Promise<T> {
  const token = Taro.getStorageSync('access_token');

  const response = await Taro.request({
    url: `${BASE_URL}${config.url}`,
    method: config.method || 'GET',
    data: config.data,
    header: {
      'Content-Type': 'application/json',
      ...(token ? { Authorization: `Bearer ${token}` } : {}),
      ...config.headers,
    },
  });

  return handleResponse<T>(response);
}

响应处理与错误分发

Taro.request 有个特殊行为:HTTP 4xx/5xx 状态码不会抛异常,它只有在网络错误时才会 reject。所以必须手动检查状态码:

function handleResponse<T>(response: Taro.request.SuccessCallbackResult): T {
  const { statusCode, data } = response;

  if (statusCode === 401) {
    handleUnauthorized();
    throw new Error('Unauthorized');
  }

  if (statusCode === 404) {
    throw new Error('Resource not found');
  }

  if (statusCode >= 400) {
    const message = (data as any)?.message || `请求失败 (${statusCode})`;
    Taro.showToast({ title: message, icon: 'none' });
    throw new Error(message);
  }

  const result = data as ApiResponse<T>;
  return result.data;
}

这是容易踩的大坑——很多开发者用 try/catch 包裹 Taro.request,以为能捕获 400 错误,实际上捕获不到。

401 处理:跳转登录页

小程序的页面跳转和 Web 端不同,不能用 window.location.href

let isRedirecting = false;

function handleUnauthorized() {
  if (isRedirecting) return;
  isRedirecting = true;

  Taro.removeStorageSync('access_token');

  // 小程序用 redirectTo,H5 用 location
  if (process.env.TARO_ENV === 'weapp') {
    Taro.redirectTo({ url: '/pages/login/index' });
  } else {
    window.location.href = '/login';
  }

  setTimeout(() => { isRedirecting = false; }, 3000);
}

process.env.TARO_ENV 是 Taro 的编译时常量,打包时会被替换为具体平台标识,不会引入多余代码。

请求重试

小程序的网络环境不如桌面浏览器稳定,偶发的网络抖动很常见。对于幂等的 GET 请求,加自动重试:

async function requestWithRetry<T>(
  config: RequestConfig,
  retries = 2
): Promise<T> {
  for (let i = 0; i <= retries; i++) {
    try {
      return await request<T>(config);
    } catch (error) {
      if (i === retries) throw error;

      // 只对网络错误重试,业务错误直接抛出
      if (error instanceof Error && error.message.includes('request:fail')) {
        await sleep(1000 * (i + 1)); // 递增延迟
        continue;
      }
      throw error;
    }
  }
  throw new Error('Unreachable');
}

function sleep(ms: number) {
  return new Promise((resolve) => setTimeout(resolve, ms));
}

关键是只对网络错误重试(request:fail 是 Taro 网络错误的标志),业务错误(如参数不合法)重试多少次都没用。

Loading 状态管理

小程序中通常用 Taro.showLoading 显示加载状态。但多个并发请求时,第一个请求完成就关闭了 Loading,其他请求还在进行中:

let loadingCount = 0;

function showLoading() {
  if (loadingCount === 0) {
    Taro.showLoading({ title: '加载中...' });
  }
  loadingCount++;
}

function hideLoading() {
  loadingCount--;
  if (loadingCount <= 0) {
    loadingCount = 0;
    Taro.hideLoading();
  }
}

用引用计数确保所有请求都完成后才关闭 Loading。

导出便捷方法

export const http = {
  get: <T>(url: string, data?: object) =>
    request<T>({ url, method: 'GET', data }),

  post: <T>(url: string, data?: object) =>
    request<T>({ url, method: 'POST', data }),

  put: <T>(url: string, data?: object) =>
    request<T>({ url, method: 'PUT', data }),

  del: <T>(url: string, data?: object) =>
    request<T>({ url, method: 'DELETE', data }),
};

业务代码调用:

const userList = await http.get<User[]>('/api/users');
await http.post('/api/orders', { productId: 123 });

总结

Taro 跨平台请求层的核心挑战在于抹平平台差异:Taro.request 的非抛异常行为、Token 存储方式、页面跳转 API、网络稳定性。封装好这一层后,业务开发只需要 http.get / http.post,不用关心代码运行在浏览器还是小程序中。