uni-app 网络请求封装

章节介绍

网络请求是移动应用开发中的核心功能之一,几乎所有应用都需要与后端服务器进行数据交互。uni-app 提供了 uni.request API 用于发起网络请求,但在实际开发中,直接使用原生 API 会导致代码冗余、错误处理不一致等问题。本章节将详细介绍如何在 uni-app 中封装网络请求,包括请求拦截器、响应拦截器和错误处理等核心知识点,以及如何构建统一的网络请求模块,帮助你提升代码质量和开发效率。

核心知识点讲解

1. 网络请求基础

uni-app 提供了 uni.request API 用于发起网络请求,支持 GET、POST 等多种请求方式。

基本使用

uni.request({
  url: 'https://api.example.com/login',
  method: 'POST',
  data: {
    username: 'admin',
    password: '123456'
  },
  header: {
    'Content-Type': 'application/json'
  },
  success: (res) => {
    console.log('请求成功:', res.data);
  },
  fail: (err) => {
    console.error('请求失败:', err);
  },
  complete: () => {
    console.log('请求完成');
  }
});

支持的配置项

  • url:请求地址
  • method:请求方法(GET、POST、PUT、DELETE 等)
  • data:请求数据
  • header:请求头
  • timeout:超时时间
  • dataType:响应数据类型
  • responseType:响应类型
  • sslVerify:是否验证 SSL 证书
  • success:请求成功回调
  • fail:请求失败回调
  • complete:请求完成回调

2. 网络请求封装

为了提高代码的可维护性和复用性,我们需要封装网络请求模块,统一处理请求配置、错误处理、拦截器等。

1. 创建基础请求模块

// utils/request.js

// 基础配置
const BASE_URL = 'https://api.example.com';
const TIMEOUT = 10000; // 10秒超时

/**
 * 基础请求方法
 * @param {Object} config - 请求配置
 * @returns {Promise} - 返回Promise
 */
const request = (config) => {
  return new Promise((resolve, reject) => {
    uni.request({
      url: BASE_URL + config.url,
      method: config.method || 'GET',
      data: config.data || {},
      header: {
        'Content-Type': 'application/json',
        ...config.header
      },
      timeout: config.timeout || TIMEOUT,
      success: (res) => {
        resolve(res);
      },
      fail: (err) => {
        reject(err);
      }
    });
  });
};

export default request;

2. 添加请求方法

// utils/request.js

// 基础配置
const BASE_URL = 'https://api.example.com';
const TIMEOUT = 10000; // 10秒超时

/**
 * 基础请求方法
 * @param {Object} config - 请求配置
 * @returns {Promise} - 返回Promise
 */
const request = (config) => {
  return new Promise((resolve, reject) => {
    uni.request({
      url: BASE_URL + config.url,
      method: config.method || 'GET',
      data: config.data || {},
      header: {
        'Content-Type': 'application/json',
        ...config.header
      },
      timeout: config.timeout || TIMEOUT,
      success: (res) => {
        resolve(res);
      },
      fail: (err) => {
        reject(err);
      }
    });
  });
};

// 常用请求方法
export const get = (url, params = {}, config = {}) => {
  return request({
    url,
    method: 'GET',
    data: params,
    ...config
  });
};

export const post = (url, data = {}, config = {}) => {
  return request({
    url,
    method: 'POST',
    data,
    ...config
  });
};

export const put = (url, data = {}, config = {}) => {
  return request({
    url,
    method: 'PUT',
    data,
    ...config
  });
};

export const del = (url, params = {}, config = {}) => {
  return request({
    url,
    method: 'DELETE',
    data: params,
    ...config
  });
};

export default request;

3. 添加拦截器

拦截器可以在请求发送前和响应返回后执行一些统一的逻辑,如添加 token、处理错误等。

// utils/request.js

// 基础配置
const BASE_URL = 'https://api.example.com';
const TIMEOUT = 10000; // 10秒超时

// 拦截器
const interceptors = {
  // 请求拦截器
  request: (config) => {
    // 添加 token
    const token = uni.getStorageSync('token');
    if (token) {
      config.header = {
        ...config.header,
        'Authorization': 'Bearer ' + token
      };
    }
    
    // 显示加载中
    uni.showLoading({
      title: '加载中...',
      mask: true
    });
    
    return config;
  },
  
  // 响应拦截器
  response: (response) => {
    // 隐藏加载中
    uni.hideLoading();
    
    return response;
  },
  
  // 错误拦截器
  error: (error) => {
    // 隐藏加载中
    uni.hideLoading();
    
    return Promise.reject(error);
  }
};

/**
 * 基础请求方法
 * @param {Object} config - 请求配置
 * @returns {Promise} - 返回Promise
 */
const request = (config) => {
  // 应用请求拦截器
  config = interceptors.request(config);
  
  return new Promise((resolve, reject) => {
    uni.request({
      url: BASE_URL + config.url,
      method: config.method || 'GET',
      data: config.data || {},
      header: {
        'Content-Type': 'application/json',
        ...config.header
      },
      timeout: config.timeout || TIMEOUT,
      success: (res) => {
        // 应用响应拦截器
        const response = interceptors.response(res);
        resolve(response);
      },
      fail: (err) => {
        // 应用错误拦截器
        const error = interceptors.error(err);
        reject(error);
      }
    });
  });
};

// 常用请求方法
export const get = (url, params = {}, config = {}) => {
  return request({
    url,
    method: 'GET',
    data: params,
    ...config
  });
};

export const post = (url, data = {}, config = {}) => {
  return request({
    url,
    method: 'POST',
    data,
    ...config
  });
};

export const put = (url, data = {}, config = {}) => {
  return request({
    url,
    method: 'PUT',
    data,
    ...config
  });
};

export const del = (url, params = {}, config = {}) => {
  return request({
    url,
    method: 'DELETE',
    data: params,
    ...config
  });
};

export default request;

3. 响应处理和错误处理

统一响应处理

// utils/request.js

// 基础配置
const BASE_URL = 'https://api.example.com';
const TIMEOUT = 10000; // 10秒超时

// 拦截器
const interceptors = {
  // 请求拦截器
  request: (config) => {
    // 添加 token
    const token = uni.getStorageSync('token');
    if (token) {
      config.header = {
        ...config.header,
        'Authorization': 'Bearer ' + token
      };
    }
    
    // 显示加载中
    uni.showLoading({
      title: '加载中...',
      mask: true
    });
    
    return config;
  },
  
  // 响应拦截器
  response: (response) => {
    // 隐藏加载中
    uni.hideLoading();
    
    const res = response.data;
    
    // 统一处理响应
    if (res.code !== 200) {
      // 显示错误信息
      uni.showToast({
        title: res.message || '请求失败',
        icon: 'none'
      });
      
      // 处理特殊错误码
      if (res.code === 401) {
        // 未授权,跳转到登录页
        uni.navigateTo({
          url: '/pages/login/login'
        });
      }
      
      return Promise.reject(res);
    }
    
    return res;
  },
  
  // 错误拦截器
  error: (error) => {
    // 隐藏加载中
    uni.hideLoading();
    
    // 统一处理错误
    let errorMessage = '网络请求失败';
    
    if (error.errMsg.includes('timeout')) {
      errorMessage = '请求超时,请检查网络连接';
    } else if (error.errMsg.includes('network')) {
      errorMessage = '网络错误,请检查网络连接';
    } else if (error.errMsg.includes('404')) {
      errorMessage = '请求地址不存在';
    }
    
    // 显示错误信息
    uni.showToast({
      title: errorMessage,
      icon: 'none'
    });
    
    return Promise.reject(error);
  }
};

/**
 * 基础请求方法
 * @param {Object} config - 请求配置
 * @returns {Promise} - 返回Promise
 */
const request = (config) => {
  // 应用请求拦截器
  config = interceptors.request(config);
  
  return new Promise((resolve, reject) => {
    uni.request({
      url: BASE_URL + config.url,
      method: config.method || 'GET',
      data: config.data || {},
      header: {
        'Content-Type': 'application/json',
        ...config.header
      },
      timeout: config.timeout || TIMEOUT,
      success: (res) => {
        try {
          // 应用响应拦截器
          const response = interceptors.response(res);
          resolve(response);
        } catch (error) {
          reject(error);
        }
      },
      fail: (err) => {
        // 应用错误拦截器
        const error = interceptors.error(err);
        reject(error);
      }
    });
  });
};

// 常用请求方法
export const get = (url, params = {}, config = {}) => {
  return request({
    url,
    method: 'GET',
    data: params,
    ...config
  });
};

export const post = (url, data = {}, config = {}) => {
  return request({
    url,
    method: 'POST',
    data,
    ...config
  });
};

export const put = (url, data = {}, config = {}) => {
  return request({
    url,
    method: 'PUT',
    data,
    ...config
  });
};

export const del = (url, params = {}, config = {}) => {
  return request({
    url,
    method: 'DELETE',
    data: params,
    ...config
  });
};

export default request;

自定义错误处理

// utils/request.js

// 错误处理函数
export const handleError = (error) => {
  let errorMessage = '未知错误';
  
  if (error.code) {
    // 后端返回的错误
    errorMessage = error.message || '请求失败';
  } else if (error.errMsg) {
    // 网络错误
    if (error.errMsg.includes('timeout')) {
      errorMessage = '请求超时,请检查网络连接';
    } else if (error.errMsg.includes('network')) {
      errorMessage = '网络错误,请检查网络连接';
    } else if (error.errMsg.includes('404')) {
      errorMessage = '请求地址不存在';
    }
  }
  
  return errorMessage;
};

// 使用
try {
  const res = await get('/api/user/list');
  console.log(res);
} catch (error) {
  const errorMessage = handleError(error);
  uni.showToast({
    title: errorMessage,
    icon: 'none'
  });
}

4. 环境配置

在实际开发中,我们通常需要区分开发环境、测试环境和生产环境,使用不同的 API 地址。

环境配置

// config/env.js

// 环境配置
const env = {
  // 开发环境
  development: {
    baseUrl: 'https://dev-api.example.com'
  },
  // 测试环境
  test: {
    baseUrl: 'https://test-api.example.com'
  },
  // 生产环境
  production: {
    baseUrl: 'https://api.example.com'
  }
};

// 获取当前环境
const getEnv = () => {
  // #ifdef H5
  return 'development';
  // #endif
  
  // #ifdef MP-WEIXIN
  return 'production';
  // #endif
  
  // 默认环境
  return 'development';
};

// 导出当前环境的配置
export default env[getEnv()];

使用环境配置

// utils/request.js

import env from '../config/env';

// 基础配置
const BASE_URL = env.baseUrl;
const TIMEOUT = 10000; // 10秒超时

// 其他代码...

5. API 模块化

为了更好地组织代码,我们可以将 API 按照功能模块进行拆分,每个模块对应一个文件。

创建 API 模块

// api/user.js

import { get, post, put, del } from '../utils/request';

// 用户相关 API
export const userApi = {
  // 登录
  login: (data) => {
    return post('/login', data);
  },
  
  // 注册
  register: (data) => {
    return post('/register', data);
  },
  
  // 获取用户信息
  getUserInfo: () => {
    return get('/user/info');
  },
  
  // 更新用户信息
  updateUserInfo: (data) => {
    return put('/user/info', data);
  },
  
  // 退出登录
  logout: () => {
    return post('/logout');
  }
};

export default userApi;
// api/product.js

import { get, post, put, del } from '../utils/request';

// 产品相关 API
export const productApi = {
  // 获取产品列表
  getProductList: (params) => {
    return get('/products', params);
  },
  
  // 获取产品详情
  getProductDetail: (id) => {
    return get(`/products/${id}`);
  },
  
  // 添加产品
  addProduct: (data) => {
    return post('/products', data);
  },
  
  // 更新产品
  updateProduct: (id, data) => {
    return put(`/products/${id}`, data);
  },
  
  // 删除产品
  deleteProduct: (id) => {
    return del(`/products/${id}`);
  }
};

export default productApi;

统一导出 API

// api/index.js

import userApi from './user';
import productApi from './product';

// 统一导出所有 API
export default {
  user: userApi,
  product: productApi
};

使用 API 模块

// 导入 API
import api from '@/api';

// 登录
try {
  const res = await api.user.login({
    username: 'admin',
    password: '123456'
  });
  console.log('登录成功:', res);
} catch (error) {
  console.error('登录失败:', error);
}

// 获取产品列表
try {
  const res = await api.product.getProductList({
    page: 1,
    pageSize: 10
  });
  console.log('产品列表:', res);
} catch (error) {
  console.error('获取产品列表失败:', error);
}

实用案例分析

案例:封装统一的网络请求模块

1. 创建完整的请求模块

// utils/request.js

import env from '../config/env';

// 基础配置
const BASE_URL = env.baseUrl;
const TIMEOUT = 10000; // 10秒超时

// 拦截器
const interceptors = {
  // 请求拦截器
  request: (config) => {
    // 添加 token
    const token = uni.getStorageSync('token');
    if (token) {
      config.header = {
        ...config.header,
        'Authorization': 'Bearer ' + token
      };
    }
    
    // 显示加载中
    if (config.showLoading !== false) {
      uni.showLoading({
        title: config.loadingText || '加载中...',
        mask: config.loadingMask !== false
      });
    }
    
    return config;
  },
  
  // 响应拦截器
  response: (response) => {
    // 隐藏加载中
    uni.hideLoading();
    
    const res = response.data;
    
    // 统一处理响应
    if (res.code !== 200) {
      // 显示错误信息
      uni.showToast({
        title: res.message || '请求失败',
        icon: 'none'
      });
      
      // 处理特殊错误码
      if (res.code === 401) {
        // 未授权,跳转到登录页
        uni.navigateTo({
          url: '/pages/login/login'
        });
      }
      
      return Promise.reject(res);
    }
    
    return res;
  },
  
  // 错误拦截器
  error: (error) => {
    // 隐藏加载中
    uni.hideLoading();
    
    // 统一处理错误
    let errorMessage = '网络请求失败';
    
    if (error.errMsg.includes('timeout')) {
      errorMessage = '请求超时,请检查网络连接';
    } else if (error.errMsg.includes('network')) {
      errorMessage = '网络错误,请检查网络连接';
    } else if (error.errMsg.includes('404')) {
      errorMessage = '请求地址不存在';
    }
    
    // 显示错误信息
    uni.showToast({
      title: errorMessage,
      icon: 'none'
    });
    
    return Promise.reject(error);
  }
};

/**
 * 基础请求方法
 * @param {Object} config - 请求配置
 * @returns {Promise} - 返回Promise
 */
const request = (config) => {
  // 应用请求拦截器
  config = interceptors.request(config);
  
  return new Promise((resolve, reject) => {
    uni.request({
      url: BASE_URL + config.url,
      method: config.method || 'GET',
      data: config.data || {},
      header: {
        'Content-Type': 'application/json',
        ...config.header
      },
      timeout: config.timeout || TIMEOUT,
      success: (res) => {
        try {
          // 应用响应拦截器
          const response = interceptors.response(res);
          resolve(response);
        } catch (error) {
          reject(error);
        }
      },
      fail: (err) => {
        // 应用错误拦截器
        const error = interceptors.error(err);
        reject(error);
      }
    });
  });
};

// 常用请求方法
export const get = (url, params = {}, config = {}) => {
  return request({
    url,
    method: 'GET',
    data: params,
    ...config
  });
};

export const post = (url, data = {}, config = {}) => {
  return request({
    url,
    method: 'POST',
    data,
    ...config
  });
};

export const put = (url, data = {}, config = {}) => {
  return request({
    url,
    method: 'PUT',
    data,
    ...config
  });
};

export const del = (url, params = {}, config = {}) => {
  return request({
    url,
    method: 'DELETE',
    data: params,
    ...config
  });
};

// 错误处理函数
export const handleError = (error) => {
  let errorMessage = '未知错误';
  
  if (error.code) {
    // 后端返回的错误
    errorMessage = error.message || '请求失败';
  } else if (error.errMsg) {
    // 网络错误
    if (error.errMsg.includes('timeout')) {
      errorMessage = '请求超时,请检查网络连接';
    } else if (error.errMsg.includes('network')) {
      errorMessage = '网络错误,请检查网络连接';
    } else if (error.errMsg.includes('404')) {
      errorMessage = '请求地址不存在';
    }
  }
  
  return errorMessage;
};

export default request;

2. 创建环境配置文件

// config/env.js

// 环境配置
const env = {
  // 开发环境
  development: {
    baseUrl: 'https://dev-api.example.com'
  },
  // 测试环境
  test: {
    baseUrl: 'https://test-api.example.com'
  },
  // 生产环境
  production: {
    baseUrl: 'https://api.example.com'
  }
};

// 获取当前环境
const getEnv = () => {
  // #ifdef H5
  return 'development';
  // #endif
  
  // #ifdef MP-WEIXIN
  return 'production';
  // #endif
  
  // 默认环境
  return 'development';
};

// 导出当前环境的配置
export default env[getEnv()];

3. 创建 API 模块

// api/user.js

import { get, post, put, del } from '../utils/request';

// 用户相关 API
export const userApi = {
  // 登录
  login: (data) => {
    return post('/login', data, {
      showLoading: false
    });
  },
  
  // 注册
  register: (data) => {
    return post('/register', data, {
      showLoading: false
    });
  },
  
  // 获取用户信息
  getUserInfo: () => {
    return get('/user/info');
  },
  
  // 更新用户信息
  updateUserInfo: (data) => {
    return put('/user/info', data, {
      loadingText: '更新中...'
    });
  },
  
  // 退出登录
  logout: () => {
    return post('/logout');
  }
};

export default userApi;
// api/product.js

import { get, post, put, del } from '../utils/request';

// 产品相关 API
export const productApi = {
  // 获取产品列表
  getProductList: (params) => {
    return get('/products', params);
  },
  
  // 获取产品详情
  getProductDetail: (id) => {
    return get(`/products/${id}`);
  },
  
  // 添加产品
  addProduct: (data) => {
    return post('/products', data, {
      loadingText: '添加中...'
    });
  },
  
  // 更新产品
  updateProduct: (id, data) => {
    return put(`/products/${id}`, data, {
      loadingText: '更新中...'
    });
  },
  
  // 删除产品
  deleteProduct: (id) => {
    return del(`/products/${id}`, {}, {
      loadingText: '删除中...'
    });
  }
};

export default productApi;
// api/index.js

import userApi from './user';
import productApi from './product';

// 统一导出所有 API
export default {
  user: userApi,
  product: productApi
};

4. 使用示例

<!-- pages/login/login.vue -->
<template>
  <view class="container">
    <view class="login-form">
      <text class="title">登录</text>
      
      <view class="form-item">
        <input 
          type="text" 
          v-model="userInfo.username" 
          placeholder="请输入用户名"
          class="input"
        />
      </view>
      
      <view class="form-item">
        <input 
          type="password" 
          v-model="userInfo.password" 
          placeholder="请输入密码"
          class="input"
        />
      </view>
      
      <text v-if="error" class="error-message">{{ error }}</text>
      
      <button 
        @click="handleLogin" 
        :disabled="loading"
        class="login-button"
      >
        {{ loading ? '登录中...' : '登录' }}
      </button>
    </view>
  </view>
</template>

<script>
import api from '@/api';

export default {
  data() {
    return {
      userInfo: {
        username: '',
        password: ''
      },
      loading: false,
      error: ''
    };
  },
  methods: {
    async handleLogin() {
      if (!this.userInfo.username || !this.userInfo.password) {
        this.error = '请输入用户名和密码';
        return;
      }
      
      this.loading = true;
      this.error = '';
      
      try {
        const res = await api.user.login(this.userInfo);
        
        // 存储 token
        uni.setStorageSync('token', res.data.token);
        
        // 跳转到首页
        uni.switchTab({
          url: '/pages/index/index'
        });
      } catch (error) {
        this.error = error.message || '登录失败';
      } finally {
        this.loading = false;
      }
    }
  }
};
</script>

<style scoped>
.container {
  flex: 1;
  display: flex;
  justify-content: center;
  align-items: center;
  padding: 40rpx;
  background-color: #f5f5f5;
}

.login-form {
  width: 100%;
  max-width: 500rpx;
  background-color: #fff;
  padding: 60rpx;
  border-radius: 20rpx;
  box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.1);
}

.title {
  font-size: 36rpx;
  font-weight: bold;
  text-align: center;
  margin-bottom: 60rpx;
  color: #333;
}

.form-item {
  margin-bottom: 40rpx;
}

.input {
  width: 100%;
  height: 80rpx;
  border: 2rpx solid #e0e0e0;
  border-radius: 10rpx;
  padding: 0 20rpx;
  font-size: 28rpx;
}

.error-message {
  color: #ff4d4f;
  font-size: 24rpx;
  margin-bottom: 30rpx;
  display: block;
}

.login-button {
  width: 100%;
  height: 80rpx;
  background-color: #409eff;
  color: #fff;
  font-size: 28rpx;
  font-weight: bold;
  border-radius: 10rpx;
  margin-top: 20rpx;
}

.login-button:disabled {
  background-color: #c0c4cc;
}
</style>
<!-- pages/index/index.vue -->
<template>
  <view class="container">
    <view class="product-list">
      <view 
        v-for="product in productList" 
        :key="product.id"
        class="product-item"
        @click="navigateToDetail(product.id)"
      >
        <image :src="product.image" class="product-image"></image>
        <view class="product-info">
          <text class="product-name">{{ product.name }}</text>
          <text class="product-price">¥{{ product.price }}</text>
        </view>
      </view>
    </view>
  </view>
</template>

<script>
import api from '@/api';

export default {
  data() {
    return {
      productList: []
    };
  },
  onLoad() {
    this.getProductList();
  },
  methods: {
    async getProductList() {
      try {
        const res = await api.product.getProductList({
          page: 1,
          pageSize: 10
        });
        this.productList = res.data.list;
      } catch (error) {
        console.error('获取产品列表失败:', error);
      }
    },
    
    navigateToDetail(id) {
      uni.navigateTo({
        url: `/pages/detail/detail?id=${id}`
      });
    }
  }
};
</script>

<style scoped>
.container {
  flex: 1;
  padding: 20rpx;
  background-color: #f5f5f5;
}

.product-list {
  display: flex;
  flex-direction: column;
  gap: 20rpx;
}

.product-item {
  display: flex;
  padding: 20rpx;
  background-color: #fff;
  border-radius: 10rpx;
  box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.1);
}

.product-image {
  width: 160rpx;
  height: 160rpx;
  border-radius: 10rpx;
  margin-right: 20rpx;
}

.product-info {
  flex: 1;
  display: flex;
  flex-direction: column;
  justify-content: space-between;
}

.product-name {
  font-size: 28rpx;
  color: #333;
  margin-bottom: 10rpx;
}

.product-price {
  font-size: 32rpx;
  color: #ff4d4f;
  font-weight: bold;
}
</style>

常见问题与解决方案

1. 请求超时

问题:网络请求经常超时,可能的原因包括网络连接不稳定、服务器响应慢等。

解决方案

  • 增加超时时间,根据实际情况调整
  • 添加网络状态检测,在网络不好时提示用户
  • 实现请求重试机制,在超时后自动重试
  • 优化服务器性能,减少响应时间

2. 跨域问题

问题:在 H5 端开发时,可能会遇到跨域问题,导致请求失败。

解决方案

  • 后端配置 CORS(跨域资源共享)
  • 使用代理服务器转发请求
  • 在 uni-app 中配置 H5 端的代理

3. Token 过期

问题:用户登录后,Token 过期导致请求失败。

解决方案

  • 在请求拦截器中检查 Token 是否存在
  • 在响应拦截器中处理 401 错误,跳转到登录页
  • 实现 Token 刷新机制,在 Token 即将过期时自动刷新

4. 错误处理不一致

问题:不同地方的错误处理方式不一致,导致用户体验差。

解决方案

  • 封装统一的错误处理函数
  • 在响应拦截器中统一处理错误
  • 使用 try/catch 捕获异步错误
  • 提供友好的错误提示

5. 重复请求

问题:用户快速点击按钮,导致重复发送相同的请求。

解决方案

  • 实现请求防抖,防止重复点击
  • 在请求发送前禁用按钮
  • 实现请求缓存,对于相同的请求直接返回缓存结果
  • 使用请求队列,管理并发请求

代码优化建议

1. 优化请求配置

// 优化前:每次请求都需要配置
uni.request({
  url: 'https://api.example.com/user/list',
  method: 'GET',
  header: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer ' + token
  },
  success: (res) => {
    console.log(res);
  },
  fail: (err) => {
    console.error(err);
  }
});

// 优化后:使用封装的请求方法
import api from '@/api';

try {
  const res = await api.user.getUserList({ page: 1, pageSize: 10 });
  console.log(res);
} catch (error) {
  console.error(error);
}

2. 优化错误处理

// 优化前:重复的错误处理代码
try {
  const res = await uni.request({ url: '/api/user/list' });
  if (res.statusCode !== 200) {
    uni.showToast({ title: '请求失败', icon: 'none' });
    return;
  }
  console.log(res.data);
} catch (error) {
  uni.showToast({ title: '网络错误', icon: 'none' });
  console.error(error);
}

// 优化后:使用统一的错误处理
import api from '@/api';
import { handleError } from '@/utils/request';

try {
  const res = await api.user.getUserList({ page: 1, pageSize: 10 });
  console.log(res);
} catch (error) {
  const errorMessage = handleError(error);
  uni.showToast({ title: errorMessage, icon: 'none' });
}

3. 优化 API 管理

// 优化前:API 散落在各个组件中
// 组件 A
uni.request({ url: '/api/user/login' });

// 组件 B
uni.request({ url: '/api/user/info' });

// 优化后:集中管理 API
// api/user.js
export const userApi = {
  login: (data) => post('/login', data),
  getUserInfo: () => get('/user/info')
};

// 组件中使用
import api from '@/api';

api.user.login(data);
api.user.getUserInfo();

4. 优化加载状态

// 优化前:手动管理加载状态
uni.showLoading({ title: '加载中...' });
try {
  const res = await uni.request({ url: '/api/user/list' });
  console.log(res);
} catch (error) {
  console.error(error);
} finally {
  uni.hideLoading();
}

// 优化后:使用拦截器自动管理
import api from '@/api';

// 拦截器会自动处理加载状态
const res = await api.user.getUserList({ page: 1, pageSize: 10 });
console.log(res);

5. 优化环境配置

// 优化前:硬编码 API 地址
const url = 'https://api.example.com/login';

// 优化后:使用环境配置
import env from '@/config/env';

const url = env.baseUrl + '/login';

章节总结

本章节详细介绍了 uni-app 中的网络请求封装方法,包括:

  1. 网络请求基础:介绍了 uni.request API 的基本使用方法和配置项
  2. 网络请求封装:创建了基础请求模块,添加了常用请求方法
  3. 拦截器:实现了请求拦截器、响应拦截器和错误拦截器
  4. 响应处理和错误处理:统一处理响应数据和错误
  5. 环境配置:区分开发环境、测试环境和生产环境
  6. API 模块化:按照功能模块组织 API 代码
  7. 实用案例:通过完整的示例展示了如何封装和使用网络请求
  8. 常见问题与解决方案:针对网络请求中的常见问题提供了解决方案
  9. 代码优化建议:提供了网络请求相关的代码优化建议

通过本章节的学习,你应该能够掌握 uni-app 中的网络请求封装方法,构建更可靠、更易维护的网络请求系统。在实际开发中,应根据项目的具体需求,灵活调整网络请求的封装方式,确保代码质量和用户体验。

« 上一篇 uni-app 路由管理 下一篇 » uni-app 数据缓存策略