Skip to content

网络请求

在 uni-app 中,网络请求是应用开发中非常重要的一部分。本文将详细介绍如何在 uni-app 中进行网络请求,包括基本用法、进阶技巧以及最佳实践。

uni.request 基础用法

uni-app 提供了统一的网络请求 API uni.request,可以在各个平台上进行网络请求操作。

基本语法

js
uni.request({
  url: 'https://api.example.com/data', // 请求的接口地址
  method: 'GET', // 请求方法,可选:GET、POST、PUT、DELETE 等
  data: {}, // 请求的参数
  header: {}, // 设置请求的 header
  success: (res) => {}, // 请求成功后的回调函数
  fail: (err) => {}, // 请求失败后的回调函数
  complete: () => {} // 请求完成后的回调函数(无论成功与否)
})

GET 请求示例

js
uni.request({
  url: 'https://api.example.com/users',
  method: 'GET',
  data: {
    page: 1,
    limit: 10
  },
  header: {
    'content-type': 'application/json'
  },
  success: (res) => {
    console.log('请求成功:', res.data);
  },
  fail: (err) => {
    console.error('请求失败:', err);
  }
})

POST 请求示例

js
uni.request({
  url: 'https://api.example.com/users',
  method: 'POST',
  data: {
    username: 'test',
    password: '123456'
  },
  header: {
    'content-type': 'application/json'
  },
  success: (res) => {
    console.log('请求成功:', res.data);
  },
  fail: (err) => {
    console.error('请求失败:', err);
  }
})

Promise 风格的网络请求

uni-app 支持使用 Promise 风格的 API,可以让代码更加简洁。

js
// Promise 方式调用
uni.request({
  url: 'https://api.example.com/users',
  method: 'GET'
})
.then(res => {
  console.log('请求成功:', res[1].data);
})
.catch(err => {
  console.error('请求失败:', err);
})

封装网络请求

在实际项目中,我们通常会对网络请求进行封装,以便统一处理请求和响应。

js
// request.js
const baseURL = 'https://api.example.com';

// 请求拦截器
const beforeRequest = (config) => {
  // 添加 token
  const token = uni.getStorageSync('token');
  if (token) {
    config.header = {
      ...config.header,
      'Authorization': `Bearer ${token}`
    };
  }
  return config;
};

// 响应拦截器
const handleResponse = (res) => {
  // 请求成功
  if (res.statusCode >= 200 && res.statusCode < 300) {
    return res.data;
  }
  
  // 未授权
  if (res.statusCode === 401) {
    uni.showToast({
      title: '登录已过期,请重新登录',
      icon: 'none'
    });
    // 跳转到登录页
    setTimeout(() => {
      uni.navigateTo({
        url: '/pages/login/login'
      });
    }, 1500);
    return Promise.reject(new Error('未授权'));
  }
  
  // 其他错误
  uni.showToast({
    title: res.data.message || '请求失败',
    icon: 'none'
  });
  return Promise.reject(res);
};

// 封装请求方法
const request = (options) => {
  const config = beforeRequest({
    ...options,
    url: options.url.startsWith('http') ? options.url : baseURL + options.url,
    header: options.header || {
      'content-type': 'application/json'
    }
  });
  
  return new Promise((resolve, reject) => {
    uni.request({
      ...config,
      success: (res) => {
        try {
          const data = handleResponse(res);
          resolve(data);
        } catch (error) {
          reject(error);
        }
      },
      fail: (err) => {
        uni.showToast({
          title: '网络异常,请检查网络连接',
          icon: 'none'
        });
        reject(err);
      }
    });
  });
};

// 导出请求方法
export default {
  get: (url, data, options = {}) => {
    return request({
      url,
      data,
      method: 'GET',
      ...options
    });
  },
  post: (url, data, options = {}) => {
    return request({
      url,
      data,
      method: 'POST',
      ...options
    });
  },
  put: (url, data, options = {}) => {
    return request({
      url,
      data,
      method: 'PUT',
      ...options
    });
  },
  delete: (url, data, options = {}) => {
    return request({
      url,
      data,
      method: 'DELETE',
      ...options
    });
  }
};

使用封装后的请求方法:

js
import request from '@/utils/request';

// GET 请求
request.get('/users', { page: 1, limit: 10 })
  .then(res => {
    console.log('获取用户列表成功:', res);
  })
  .catch(err => {
    console.error('获取用户列表失败:', err);
  });

// POST 请求
request.post('/users', { username: 'test', password: '123456' })
  .then(res => {
    console.log('创建用户成功:', res);
  })
  .catch(err => {
    console.error('创建用户失败:', err);
  });

文件上传

uni-app 提供了 uni.uploadFile 方法用于文件上传。

js
uni.uploadFile({
  url: 'https://api.example.com/upload',
  filePath: tempFilePath,
  name: 'file',
  formData: {
    'user': 'test'
  },
  success: (res) => {
    console.log('上传成功:', res.data);
  },
  fail: (err) => {
    console.error('上传失败:', err);
  }
});

文件下载

uni-app 提供了 uni.downloadFile 方法用于文件下载。

js
uni.downloadFile({
  url: 'https://example.com/somefile.pdf',
  success: (res) => {
    if (res.statusCode === 200) {
      console.log('下载成功:', res.tempFilePath);
      // 保存文件
      uni.saveFile({
        tempFilePath: res.tempFilePath,
        success: (saveRes) => {
          console.log('文件保存成功:', saveRes.savedFilePath);
        }
      });
    }
  },
  fail: (err) => {
    console.error('下载失败:', err);
  }
});

网络状态监听

uni-app 提供了网络状态监听的 API,可以实时获取网络状态变化。

js
// 获取当前网络状态
uni.getNetworkType({
  success: (res) => {
    console.log('当前网络类型:', res.networkType);
  }
});

// 监听网络状态变化
uni.onNetworkStatusChange((res) => {
  console.log('网络类型:', res.networkType);
  console.log('是否有网络连接:', res.isConnected);
});

WebSocket 支持

uni-app 提供了 WebSocket 相关的 API,可以实现实时通信。

js
// 创建 WebSocket 连接
const socketTask = uni.connectSocket({
  url: 'wss://example.com/socket',
  header: {
    'content-type': 'application/json'
  },
  success: () => {
    console.log('WebSocket 连接成功');
  }
});

// 监听 WebSocket 打开
socketTask.onOpen(() => {
  console.log('WebSocket 已打开');
  // 发送消息
  socketTask.send({
    data: JSON.stringify({ type: 'login', data: { userId: '123' } }),
    success: () => {
      console.log('消息发送成功');
    }
  });
});

// 监听 WebSocket 接收消息
socketTask.onMessage((res) => {
  console.log('收到服务器消息:', res.data);
});

// 监听 WebSocket 错误
socketTask.onError((res) => {
  console.error('WebSocket 错误:', res);
});

// 监听 WebSocket 关闭
socketTask.onClose(() => {
  console.log('WebSocket 已关闭');
});

// 关闭 WebSocket 连接
// socketTask.close();

最佳实践

1. 统一错误处理

在网络请求封装中,统一处理错误信息,避免在每个请求中重复处理。

2. 请求取消

在页面销毁时,取消未完成的请求,避免内存泄漏。

js
let requestTask = null;

// 发起请求
requestTask = uni.request({
  url: 'https://api.example.com/data',
  // ...其他配置
});

// 在页面 onUnload 生命周期中取消请求
onUnload() {
  if (requestTask) {
    requestTask.abort();
  }
}

3. 请求重试

对于重要的请求,可以实现请求重试机制。

js
const requestWithRetry = (options, maxRetries = 3) => {
  return new Promise((resolve, reject) => {
    const attempt = (retryCount) => {
      request(options)
        .then(resolve)
        .catch(error => {
          if (retryCount < maxRetries) {
            console.log(`请求失败,第 ${retryCount + 1} 次重试`);
            setTimeout(() => {
              attempt(retryCount + 1);
            }, 1000 * Math.pow(2, retryCount)); // 指数退避策略
          } else {
            reject(error);
          }
        });
    };
    attempt(0);
  });
};

4. 数据缓存

对于不经常变化的数据,可以实现本地缓存,减少网络请求。

js
const cachedRequest = (url, expireTime = 60 * 1000) => {
  const cacheKey = `cache_${url}`;
  const cacheData = uni.getStorageSync(cacheKey);
  
  if (cacheData && cacheData.timestamp) {
    const now = new Date().getTime();
    if (now - cacheData.timestamp < expireTime) {
      return Promise.resolve(cacheData.data);
    }
  }
  
  return request.get(url).then(res => {
    uni.setStorageSync(cacheKey, {
      data: res,
      timestamp: new Date().getTime()
    });
    return res;
  });
};

5. 接口模块化

将接口按照业务模块进行分类,便于管理和维护。

js
// api/user.js
import request from '@/utils/request';

export default {
  login: (data) => request.post('/user/login', data),
  register: (data) => request.post('/user/register', data),
  getUserInfo: () => request.get('/user/info'),
  updateUserInfo: (data) => request.put('/user/info', data)
};

// api/product.js
import request from '@/utils/request';

export default {
  getProductList: (params) => request.get('/products', params),
  getProductDetail: (id) => request.get(`/products/${id}`),
  createProduct: (data) => request.post('/products', data),
  updateProduct: (id, data) => request.put(`/products/${id}`, data),
  deleteProduct: (id) => request.delete(`/products/${id}`)
};

// 使用
import userApi from '@/api/user';
import productApi from '@/api/product';

// 登录
userApi.login({ username: 'test', password: '123456' })
  .then(res => {
    console.log('登录成功:', res);
  });

// 获取产品列表
productApi.getProductList({ page: 1, limit: 10 })
  .then(res => {
    console.log('产品列表:', res);
  });

跨域处理

在开发环境中,可能会遇到跨域问题。uni-app 提供了代理配置来解决跨域问题。

在项目根目录下创建 vue.config.js 文件:

js
module.exports = {
  devServer: {
    proxy: {
      '/api': {
        target: 'https://api.example.com',
        changeOrigin: true,
        pathRewrite: {
          '^/api': ''
        }
      }
    }
  }
};

小程序特殊处理

在小程序中,网络请求需要配置合法域名,否则会被拦截。

  1. 在小程序管理后台的"开发设置"中添加服务器域名
  2. 在开发环境中,可以在开发工具中勾选"不校验合法域名"选项

总结

本文介绍了 uni-app 中网络请求的基本用法、进阶技巧以及最佳实践。通过合理封装和使用网络请求,可以提高代码的可维护性和可复用性,同时也能提升应用的用户体验。在实际开发中,应根据项目需求选择合适的网络请求方式和策略。

一次开发,多端部署 - 让跨平台开发更简单