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

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、来生成唯一ID
3.请求完成后,把当前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

往期推荐

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,293评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,604评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,958评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,729评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,719评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,630评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,000评论 3 397
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,665评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,909评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,646评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,726评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,400评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,986评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,959评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,197评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,996评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,481评论 2 342

推荐阅读更多精彩内容