写点什么

5 分钟 get:Uni-App 网络请求完美封装指南!

作者:陇锦
  • 2023-08-01
    北京
  • 本文字数:4288 字

    阅读完需:约 14 分钟

5分钟get:Uni-App网络请求完美封装指南!

大家好,今天我想分享一些关于uni-app开发中封装高效、优雅的 request 请求的经验。之前我在uni-app开发小程序:项目架构以及经验分享的文章中,已经分享了一些有用的经验技巧,包括二次封装uni-app的 API。下面我将详细介绍如何封装一个强大且易用的request请求,这里封装也仅仅是提供一个思路,大家可以在写原生小程序时候也可以进行借鉴。先赞后看,月入百万!!!

为什么需要封装 request 请求?

uni-app开发中,我们经常需要与后端服务器进行数据交互。为了提高开发效率、代码的可维护性以及降低重复代码的使用,我们通常会对网络请求进行封装。


封装 request 请求的好处有很多:


  • 简化代码:将一些重复性的请求处理逻辑抽离出来,使得业务代码更加清晰简洁,易于阅读和维护。

  • 易于管理:统一管理接口地址、错误处理和请求拦截,方便后期维护和更新。增强可扩展性:如果后端接口发生变化,只需要修改请求封装的部分而不影响业务代码。

  • 支持 asyncawait,以提高代码的可读性和简洁性。

  • 在请求库中实现全局的 loading 功能,让用户在发送请求时能够看到加载动画,增强用户体验。

  • 统一处理请求错误,例如网络连接失败时,给予用户友好的错误提示,提高用户满意度。

  • 考虑实现多种请求方式,包括 GET、POST、PUT 等,以满足不同场景下的需求。

  • 为了避免重复请求,可以实现请求拦截功能,在请求发送前判断是否已经在进行相同的请求,如果是,则取消重复请求。


先上一下最终的使用方式:


api.js


// 引入请求import request from '@/utils/request'
//接口示例export const info = data => request.post('/v1/api/info', data)
复制代码


页面中使用


<script>import { info } from '@/api/user.js'export default {  methods: {    async getUserinfo() {      let res = await info()    }  }}</script>
复制代码

coding

1.创建基础请求

首先,我们导入了一些公共方法,比如toast用于显示提示信息,clearStorageSyncgetStorageSync用于操作本地缓存,还有useRouter用于跳转页面等。


然后,我们定义了一个名为baseRequest的异步函数。这个函数接收四个参数:url表示请求的地址,method表示请求方法,默认GET请求,data表示要发送的数据,默认为空对象,loading表示是否显示加载动画,默认为true


在函数内部,我们构建了一个Promise对象,用于支持asyncawait调用。


在异步请求中,我们使用了uni.request方法发送请求。我们在请求中传入了请求地址、请求方法、请求头、数据、和超时时间等信息。


如果请求成功,并且状态码为200,那么我们会处理返回的数据。这里的处理逻辑可以根据实际业务需求来修改。如果返回的resultCodePA-G998(业务逻辑),表示用户未登录或登录过期,我们会清除本地缓存并跳转到登录页面。否则,我们会将请求成功返回的数据传递给 Promise 对象的 reslove 方法。


如果请求失败,我们会显示网络连接失败的提示,并将错误信息传递给Promise对象的reject方法。


import {toast, clearStorageSync, getStorageSync, useRouter} from './utils' // 公共方法import {BASE_URL} from '@/config/index' //获取请求域名
const baseRequest = async (url, method, data = {}, loading = true) =>{ let header = {} return new Promise((reslove, reject) => { uni.request({ url: BASE_URL + url, method: method || 'GET', header: header, timeout: 10000, data: data || {}, success: (successData) => { const res = successData.data if(successData.statusCode == 200){ // 业务逻辑,自行修改 if(res.resultCode == 'PA-G998'){ clearStorageSync() useRouter('/pages/login/index', 'reLaunch') }else{ reslove(res.data) } }else{ toast('网络连接失败,请稍后重试') reject(res) } }, fail: (msg) => { toast('网络连接失败,请稍后重试') reject(msg) } }) })}
复制代码

2.简化入参

上面只是封装了一个最最基础的请求,该方法接受的参数比较多,这个时候我们就需要去做一次简化参数的操作:


首先我们创建一个名为request的对象,并使用forEach方法遍历包含不同HTTP请求方法的数组。对于每个 HTTP 请求方法,它会定义一个对应的函数,并将其作为request对象的属性。


这样,在使用request对象时,可以直接调用request.GET()request.POST()等方法来发起不同类型的 HTTP 请求,而不需要每次都显式地指定请求的方法。这样可以使代码更加简洁和易于维护。


最后导出request对象


const request = {}
['options', 'get', 'post', 'put', 'head', 'delete', 'trace', 'connect'].forEach((method) => { request[method] = (api, data, loading) => baseRequest(api, method, data, loading)})
export default request
复制代码


使用方式如下:


/api/user.js 文件


import request from '@/utils/request'
//个人信息export const info = data => request.post('/v1/api/info', data)
复制代码


页面使用:


import { info } from '@/api/user.js'export default {  onLoad() {    this.getUserinfo()  },  methods: {    async getUserinfo() {      let res = await info()    }  }}
复制代码

禁止重复请求

为了节省网络资源、提高性能和响应速度以及避免数据错误,我们需要对发起的请求做一些限制来避免重复请求,常见的限制方法如下:


1.使用防抖和节流:可以使用防抖和节流的技术来控制请求的触发频率,确保在一段时间内只发起一次请求。2.设置请求锁:可以在发起请求之前设置一个请求锁,防止重复触发请求。3.合理设计页面和交互逻辑:在页面设计和交互逻辑中,合理安排请求的时机,避免不必要的重复请求。


我们可以在封装的接口请求中添加一个请求队列,如果有当前发起且没有返回结果的,就不允许再次请求,具体实现思路如下:


1.创建一个存放唯一 ID 的Map对象 2.当请求接口时候通过拿到的methodurlparams、来生成唯一ID3.请求完成后,把当前ID从对象中删除。


我们把检测唯一ID这个功能提炼出来,单独去封装一个class去实现;具体实现代码如下:


新建/utils/requestManager.js文件,创建一个对象,并初始化一个名为idMap的对象,最后导出对象


class RequestManager {    constructor() {        this.idMap = new Map()    }}
export default RequestManager
复制代码


根据methodurlparams、来生成唯一ID,这里要注意的是我们的params需要进行序列化处理,不然如果同一个接口、相同的请求方式、参数顺序不同也会判断为不同的请求。


class RequestManager {    /**     * 生成唯一ID的方法     * @param {string} method - 请求方法     * @param {string} url - 请求URL     * @param {object} params - 请求参数     * @returns {string} - 生成的唯一ID     */    generateUniqueId(method, url, params) {        const idString = `${method}-${url}-${this.serializeObject(params)}`        let id = 0;        for (let i = 0; i < idString.length; i++) {            id = ((id << 5) - id) + idString.charCodeAt(i)            id |= 0;        }        return id.toString()    }
/** * 序列化对象为字符串 * @param {object} obj - 要序列化的对象 * @returns {string} - 序列化后的字符串 */ serializeObject(obj) { const keys = Object.keys(obj).sort() const serializedObj = {} for (let key of keys) { const value = obj[key] if (value !== null && typeof value === 'object') { serializedObj[key] = this.serializeObject(value) } else { serializedObj[key] = value } } return JSON.stringify(serializedObj) }}
复制代码


写完生成方法,相对应的实现一下删除方法:


class RequestManager {    /**     * 根据ID删除map对象中的请求信息     * @param {string} id - 要删除的唯一ID     */    deleteById(id) {        this.idMap.delete(id)    }}
复制代码


上面就实现了基本的功能,下面我们写一个方法,去组合一下上面的功能,简化使用:


class RequestManager {    /**     * 生成唯一ID,并将ID和请求信息存储到map对象中     * @param {string} method - 请求方法     * @param {string} url - 请求URL     * @param {object} params - 请求参数     * @returns {string|boolean} - 生成的唯一ID,如果存在相同id则返回false     */    generateId(method, url, params) {        const id = this.generateUniqueId(method, url, params)        if (this.idMap.has(id)) {            return false        }        this.idMap.set(id, { method, url, params })        return id    }}
复制代码


到这里我们的方法就写完了,下面来看一下如何使用:


//引入方法import RequestManager from '@/utils/requestManager.js' 
const manager = new RequestManager() //创建
const baseRequest = async (url, method, data = {}, loading = true) =>{
// 生成唯一ID, 如果返回false 代表重复请求 let requestId = manager.generateId(method, url, data) if(!requestId) { console.log('重复请求') return false } return new Promise((reslove, reject) => { uni.request({ complete: ()=>{ // 请求完成,清除当前请求的唯一ID manager.deleteById(requestId) }, }) })}
复制代码

添加全局 loading

添加全局loading就比较简单了,我们前面定义了入参数loading,如果为true,在创建Promise后,调用uni.showLoading即可。同时需要在uni.request中添加complete方法,在请求完成后去关闭loading


const baseRequest = async (url, method, data = {}, loading = true) =>{  return new Promise((reslove, reject) => {    // 开启loading    loading && uni.showLoading({title: 'loading'})    uni.request({      // ...      complete: ()=>{        // 关闭loading        uni.hideLoading()      },      // ...省略下方代码    })  })}
复制代码

结尾

所有代码已放到github;请访问 uni-app-template,如果觉得不错,记得给个star

往期推荐

发布于: 刚刚阅读数: 4
用户头像

陇锦

关注

还未添加个人签名 2019-11-11 加入

还未添加个人简介

评论

发布
暂无评论
5分钟get:Uni-App网络请求完美封装指南!_微信小程序_陇锦_InfoQ写作社区