乐于分享
好东西不私藏

uni-app开发极简入门(10):HTTP网络请求

uni-app开发极简入门(10):HTTP网络请求

  1. GET、POST请求的各平台兼容性最高,本文就仅考虑这两种。

  2. uni-app有很多第三方封装的拦截器可用,按需选择。本文仅做基于uni-app官方的HTTP请求API。

  3. 展示和上传图片功能对应的服务端是我自己写的,但通常情况下,大家应该都是用各种云提供的OSS,按照官方文档处理即可,我只是演示一下uni-app相关API(chooseImage、uploadFile)。

  4. 服务端代码就不写了,大家手头有现成的就用现成的,没有的可以参考写给前端的Mock Server一文。

创建common/params.ts:

export interface BaseResponse<T> {  codenumber  msgstring  data: T}export interface Page<T>{  currentPage:number  pageSize:number  total:number  list:T}

创建pages/user/userVo.ts:

export interface UserDetail {  id : string  name : string  idCard : string  phone : string  gender : string  birthDate : string  address : string}

结合以前讲TypeScript的文章,不解释了。

创建utils/request.ts:

import { BaseResponse } from "../common/params"import { useTokenStore } from '@/stores/token'const baseURL = 'https://www.example.com/api' //仅为示例,非真实的接口地址export const request = <T>(options : UniApp.RequestOptions) => {    uni.showLoading({ title'加载中...'masktrue });    const tokenStore = useTokenStore();    if (tokenStore.hasToken) {        options.header = {            ...options.header,            "jwt-token": tokenStore.token        }    }    return new Promise<BaseResponse<T>>((resolve, reject) => {        uni.request({            ...options,            url: baseURL + options.url,            success(res) => {                if (res.statusCode >= 200 && res.statusCode < 300) {                    resolve(res.data as BaseResponse<T>)                } else {                    reject(res)                }            },            fail(err) => {                reject(err)            },            complete() => {                uni.hideLoading()            }        })    })}

创建pages/reqDemo/reqDemo.vue:

<template>    <divclass='reqDemo'>        <h1>用户列表</h1>        <buttontype="default" @click="getUserList">POST请求</button>        {{userList}}        <h1>用户详情</h1>        <buttontype="default" @click="getUserById">GET请求</button>        {{userDetail}}        <h1>上传图片</h1>        <buttontype="default" @click="uploadImg">上传图片</button>        <image:src="imgSrc"mode="scaleToFill"></image>    </div></template><scriptlang='ts'setupname='ReqDemo'>    import {        Ref,        ref    } from 'vue'    import {        BaseResponse,        Page    } from '../../common/params'    import {        UserDetail    } from '../user/userVo'    import {        request    } from '../../utils/request'    // POST请求    const userListRef < UserDetail[] > = ref < UserDetail[] > ([])    const getUserList = async () => {        try {            const res = await request < BaseResponse < Page < UserDetail[] >>> ({                url'/user/page',                method'POST',                data: {                    page1,                    pageSize5                }            })            if (res.code === 0) {                const response = res.data as unknown as Page < UserDetail[] > ;                userList.value = response.list                uni.showToast({                    title'列表获取成功',                    icon'success'                })            } else {                throw new Error(res.msg)            }        } catch (error) {            uni.showToast({                title'异常:' + error.message,                icon'none'            })            console.error('POST请求错误:', error)        } finally {            console.log("POST网络请求结束");        }    }    // GET请求    // 用户详情数据    const userDetailRef < UserDetail > = ref < UserDetail > ({        id'-',        name'-',        idCard'-',        phone'-',        gender'male',        birthDate'2021-01-01',        address'-'    })    const getUserById = async () => {        try {            const res = await request < BaseResponse < UserDetail >> ({                url'/user/getUserById',                data: {                    userId"1"                }            })            if (res.code === 0) {                const response = res.data as unknown as UserDetail                userDetail.value = response                uni.showToast({                    title'用户详情获取成功',                    icon'success'                })            } else {                throw new Error(res.msg)            }        } catch (error) {            uni.showToast({                title'异常:' + error.message,                icon"error"            })            console.error('GET请求错误:', error)        } finally {            console.log("GET网络请求结束");        }    }    //上传图片    const imgSrc:Ref<string>=ref<string>("随便一个图片URL地址")    const uploadImg = () => {        uni.chooseImage({            success(chooseImageRes) => {                const tempFilePaths = chooseImageRes.tempFilePaths;                const uploadTask = uni.uploadFile({                    url'https://www.example.com/api/upload'//仅为示例,非真实的接口地址                    filePath: tempFilePaths[0],                    name'file',                    success(uploadFileRes) => {                        console.log("上传成功:", uploadFileRes);                        console.log("图片地址:",JSON.parse(uploadFileRes.data).data.newFileName);                        imgSrc.value="https://www.example.com/uploads/"+JSON.parse(uploadFileRes.data).data.newFileName                    },                    failuploadFileRes => {                        console.error('上传失败', uploadFileRes);                    }                });                uploadTask.onProgressUpdate((res) => {                    console.log('上传进度' + res.progress);                    console.log('已经上传的数据长度' + res.totalBytesSent);                    console.log('预期需要上传的数据总长度' + res.totalBytesExpectedToSend);                    // 测试条件,取消上传任务。                    // if (res.progress > 50) {                    // 	uploadTask.abort();                    // }                });            }        });    }</script><stylescoped>    .reqDemo {        background-color: wheat;    }</style>

这里额外多说一个大家可能会遇到的问题。

看我们上面的代码,我们发HTTP请求的时候,为了用户友好,并防重复请求,都会显示加载的提示框,并在HTTP请求结束后将其隐藏。为了统一操作,节省代码,其对应的就是request.ts里的

uni.showLoading({ title'加载中...'masktrue })uni.hideLoading()

在进行业务开发时,为了通用性,我们通常会将某个请求封装成一个方法(例如getUserById()),真正使用的时候,还是有很多上下文逻辑,一般是通过其他方法来调用,例如页面有个重置按钮,前后做一些逻辑处理,中间getUserById,弹出个显示信息什么的,例如:

const handleReset = () => {    // 一些其他逻辑...    // 重新加载原始数据    getUserById()    uni.showToast({        title'重置成功!',        icon'success'    })    // 一些其他逻辑...}

很直接的代码,但是这么写,控制台一般会报“请注意 showLoading 与 hideLoading 必须配对使用”,如下图:

很多人就摸不着头脑了,因为代码里showLoading、hideLoading就是配对使用的,为什么报这个错。

直接揭晓答案:因为用了其他弹出框,例如showToast。showLoading和showToast使用的同一个实例,handleReset的代码是异步的,getUserById整个代码没执行完,它的showLoading,被下面的showToast给冲掉了,最后hideLoading自然找不到对应的实例,就报异常了。

解决方案也简单:

第一种:无视告警。反正也不影响程序的正常运行,用户也看不到控制台,眼不见心不烦就行。

你说我有强迫症,必须搞掉这个告警,那就第二种,上同步:

const handleReset = async () => {    // 一些其他逻辑...    // 重新加载原始数据    await getUserById()    uni.showToast({        title'重置成功!',        icon'success'    })    // 一些其他逻辑...}

等HTTP请求hideLoading之后,再showToast。

但是也有人说了,一切HTTP请求都同步,性能不给力,还是想搞异步,那就第三种:request.ts不再统一处理loading提示框,交由上层的function独立处理,且hideLoading必须放在showToast的前面。

大家有兴趣可以去uni-app官网看看,这个问题,2019年就存在了,2025年5月份还有人讨论这个问题。

再补充几句,即便是按照上面的写法,哪怕是不加uni.showToast了,在微信小程序真机调试模式下(注意是真机调试,不是在微信开发工具上调试),控制台依然会报“hideLoading:fail toast can’t be found”,如图:

原因我也懒得查了,要么无视,要么直接去微信开发工具修改代码。

通过uni-app编译过去的微信小程序代码如:

// 对应的uni-app代码// uni.showLoading({ title: '加载中...', mask: true })// 编译后的微信小程序代码common_vendor.index.showLoading({ title"加载中..."masktrue });// 手动修改为// wx.showLoading({ title: "加载中...", mask: true });// 对应的uni-app代码// uni.hideLoading()// 编译后的微信小程序代码common_vendor.index.hideLoading();// 手动修改为// wx.hideLoading();

报错就消失了。

我说什么来着,兼容性是个大问题吧!!

本站文章均为手工撰写未经允许谢绝转载:夜雨聆风 » uni-app开发极简入门(10):HTTP网络请求

评论 抢沙发

9 + 6 =
  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址
×
订阅图标按钮