目录
[(2)使用 uni.addInterceptor 添加拦截器:](#(2)使用 uni.addInterceptor 添加拦截器:)
[(5)发起 HTTP 请求并处理返回的数据:](#(5)发起 HTTP 请求并处理返回的数据:)
一.什么是uni-app?
uni-app 是一个使用 Vue.js 开发所有前端应用的框架,它支持一次开发,多个平台运行。具体来说,uni-app 可以编译成 iOS、Android、H5、以及各种小程序(如微信小程序、支付宝小程序、百度小程序等)应用,使得开发者能够在多个平台上以同一套代码实现应用。总的来说,uni-app 是一个非常适合需要多平台支持的项目的开发框架,尤其适合快速开发和迭代的产品。
下面是uniapp的官网:uni-app官网
二.创建项目:
uni-app
支持通过 可视化界面、vue-cli命令行 两种方式快速创建项目。
我们在开发uniapp通常使用其自主开发的编译器:HBuilderX,下面是下载链接地址:HBuilderX-高效极客技巧,HBuilderX是通用的前端开发工具,但为uni-app
做了特别强化。
- 打开 HBuilderX。
- 点击左上角的 文件 > 新建 > 项目。
- 选择 uni-app,选择一个模板(例如"Hello uni-app"模板),然后设置项目名称和路径。
- 点击 创建,HBuilderX 会自动为你生成一个初始项目。
同时我们也可以按照官网教程使用 cli 脚手架创建项目工程:uni-app官网
npm install -g @vue/cli
vue create -p dcloudio/uni-preset-vue my-project
这将通过 Vue CLI 创建一个基于 uni-app 的项目。
三.Vue与小程序的区别?
我们可以参考下面的博客:https://segmentfault.com/a/1190000015684864
四.pages.json详解:
TypeScript
{
// 组件自动引入规则
"easycom": {
"autoscan": true, // 开启自动扫描
"custom": {
// uni-ui 规则如下配置
"^uni-(.*)": "@dcloudio/uni-ui/lib/uni-$1/uni-$1.vue"
}
},
"pages": [
//pages数组中第一项表示应用启动页,参考:https://uniapp.dcloud.io/collocation/pages
{
"path": "pages/my/my",
"style": {
"navigationBarTitleText": "我的"
}
},
// ...
],
"globalStyle": {
"navigationBarTextStyle": "black",
"navigationBarTitleText": "",
"navigationBarBackgroundColor": "#F8F8F8",
"backgroundColor": "#F8F8F8"
},
// 设置 TabBar
"tabBar": {
"color": "#333",
"selectedColor": "#27ba9b",
"backgroundColor": "#fff",
"borderStyle": "white",
"list": [
{
"text": "首页",
"pagePath": "pages/index/index",
"iconPath": "static/tabs/home_default.png",
"selectedIconPath": "static/tabs/home_selected.png"
},
// ...
]
}
}
1.组件自动引入规则(easycom
):
TypeScript
"easycom": {
"autoscan": true, // 开启自动扫描
"custom": {
"^uni-(.*)": "@dcloudio/uni-ui/lib/uni-$1/uni-$1.vue"
}
}
easycom
是 uni-app 的 Easycom 组件自动引入功能的配置。
autoscan: true
: 开启自动扫描功能,允许自动扫描项目中的组件,无需手动引入。uni-app 会自动根据组件名称进行匹配,加载对应的组件。custom
: 自定义组件的自动引入规则。"^uni-(.*)"
表示所有以uni-
开头的组件都会自动从@dcloudio/uni-ui/lib/uni-$1/uni-$1.vue
路径引入。这是针对uni-ui
的自定义规则。比如uni-button
组件会自动引入@dcloudio/uni-ui/lib/uni-button/uni-button.vue
。
2.应用页面配置(pages
):
TypeScript
"pages": [
{
"path": "pages/my/my",
"style": {
"navigationBarTitleText": "我的"
}
},
// ...
]
pages
数组定义了 uni-app 应用的各个页面及其配置信息。每个对象代表一个页面的配置:
path
: 页面路径,指向相应页面的.vue
文件(以pages/
开头的路径表示项目内的页面)。style
: 页面样式配置,设置页面的navigationBarTitleText
即导航栏的标题文字。例如,首页页面设置了"首页"
作为标题。
3.全局样式配置(globalStyle
):
TypeScript
"globalStyle": {
"navigationBarTextStyle": "black",
"navigationBarTitleText": "",
"navigationBarBackgroundColor": "#F8F8F8",
"backgroundColor": "#F8F8F8"
}
globalStyle
配置了全局的样式:
TypeScript
navigationBarTextStyle: 设置导航栏文字颜色,这里设置为黑色 ("black")。
navigationBarTitleText: 设置默认的导航栏标题文本,这里为空字符串 "" 表示没有默认标题。
navigationBarBackgroundColor: 设置导航栏的背景颜色,这里设置为 #F8F8F8,即浅灰色。
backgroundColor: 设置应用的背景色,这里也设置为浅灰色 #F8F8F8。
4.TabBar 配置(tabBar
):
TypeScript
"tabBar": {
"color": "#333",
"selectedColor": "#27ba9b",
"backgroundColor": "#fff",
"borderStyle": "white",
"list": [
{
"text": "首页",
"pagePath": "pages/index/index",
"iconPath": "static/tabs/home_default.png",
"selectedIconPath": "static/tabs/home_selected.png"
},
// ...
]
}
tabBar
配置了应用底部导航栏的外观和内容:
color
: 设置 TabBar 中未选中时的文字颜色,这里是灰色#333
。
selectedColor
: 设置 TabBar 中选中时的文字颜色,这里是绿色#27ba9b
。
backgroundColor
: 设置 TabBar 的背景色,这里设置为白色#fff
。
borderStyle
: 设置 TabBar 的边框样式,这里为白色white
,表示没有边框。
list
:list
数组定义了 TabBar 的各个项。每个对象配置了:
TypeScripttext: TabBar 项的文本(例如 "首页"、"分类")。 pagePath: 点击该 Tab 时跳转的页面路径。 iconPath: 未选中状态下的图标路径。 selectedIconPath: 选中状态下的图标路径。
五.入门uniapp:
1.项目结构:
uni-app 项目结构通常包括以下文件和文件夹:
/pages
:存放应用页面的目录,每个页面都是一个.vue
文件。/components
:存放自定义组件的目录。/static
:静态资源,如图片、字体、文件等。/unpackage
:项目打包生成的文件目录。pages.json
:配置文件,用于配置页面路径、导航栏样式等。manifest.json
:项目的配置信息,包括应用名称、版本号等。global.css
:全局样式文件。uni.scss
:统一的样式文件,适用于全局的布局、颜色等
2.编写第一个页面:
在 pages
文件夹中,我们默认会看到一个 index.vue
页面。我们来修改这个页面,添加一个简单的内容。
打开 pages/index/index.vue
,将文件内容修改为:
html
<template>
<!-- 类似于 div 的容器元素,uni-app 使用 view 替代 HTML 中的 div -->
<view class="content">
<!-- 似于 span 或 p,用来显示文本内容 -->
<text>{{ message }}</text>
<!-- 用来创建按钮,点击时调用 changeMessage 方法更新文本 -->
<button @click="changeMessage">Change Message</button>
</view>
</template>
<script>
export default {
data() {
return {
message: "Hello, uni-app!"
};
},
methods: {
changeMessage() {
this.message = "Hello, uni-app updated!";
}
}
};
</script>
<style scoped>
.content {
text-align: center;
margin-top: 50rpx;
}
button {
margin-top: 20rpx;
}
</style>
3.运行项目:
(1)在 HBuilderX 中,点击 运行 按钮,选择 运行到浏览器,你的应用将会在 Web 浏览器中启动。
(2)如果想在微信小程序等平台上运行:
- 在 HBuilderX 中,选择 运行 > 运行到小程序。
- 选择你的小程序平台(如微信、支付宝等)。
- HBuilderX 会自动打开该平台的开发者工具,并加载你的应用。
4.配置微信小程序的AppID:

每一个小程序都有一个唯一的 AppID ,它用于标识该小程序,并与微信平台进行关联。当你在微信开发者工具或真实环境中访问小程序时,系统需要通过 AppID 来确定这个小程序的身份。没有 AppID,微信无法识别和加载你的程序。
- 登录微信公众平台,注册并创建一个小程序。
- 在小程序的管理后台,你可以找到你的 AppID,它是一个由字母和数字组成的字符串。
在 uni-app 中,你需要在 manifest.json
文件中配置你的 AppID。
html
{
"mp-weixin": {
"appid": "your-app-id-here"
}
}
5.配置页面路由:
在 uni-app 中,页面和路由是通过
pages.json
文件来配置的。你可以在该文件中设置页面路径和页面的导航栏配置。
pages.json
配置示例:
html
{
"pages": [
{
"path": "pages/index/index",
"style": {
"navigationBarTitleText": "Home"
}
},
{
"path": "pages/about/about",
"style": {
"navigationBarTitleText": "About"
}
}
]
}
"path"
:指定页面的路径。"style"
:设置页面的样式和配置(如导航栏标题)。
我们可以在 pages
文件夹中创建一个新页面,如 about.vue
,然后在 pages.json
中配置它。
6.pinia的使用:
在 uni-app 中使用 Pinia 进行持久化存储,可以确保当用户刷新页面或重新打开应用时,状态数据不会丢失。

首先index.ts文件中导入pinia需要的配置代码:
TypeScript
import { createPinia } from 'pinia'
import persist from 'pinia-plugin-persistedstate'
// 创建 pinia 实例
const pinia = createPinia()
// 使用持久化存储插件
pinia.use(persist)
// 默认导出,给 main.ts 使用
export default pinia
// 模块统一导出
export * from './modules/member'
然后我们在modules目录下创建ts文件:
TypeScript
import { defineStore } from 'pinia'
import { ref } from 'vue'
// 定义 Store
export const useMemberStore = defineStore(
'member',
() => {
// 会员信息
const profile = ref<any>()
// 保存会员信息,登录时使用
const setProfile = (val: any) => {
profile.value = val
}
// 清理会员信息,退出时使用
const clearProfile = () => {
profile.value = undefined
}
// 记得 return
return {
profile,
setProfile,
clearProfile,
}
},
// TODO: 持久化
{
// 网页端配置:
// persist: true,
// 小程序端配置:
// persist:{
// storage:{
// getItem(key){
// return uni.getStorageSync(key)
// },
// setItem(key,value){
// uni.setStorageSync(key,value)
// }
// }
// }
// 网页端配置: process.env.VUE_APP_PLATFORM 来判断当前平台
persist: process.env.VUE_APP_PLATFORM === 'h5' ? true : {
storage: {
getItem(key) {
return uni.getStorageSync(key); // 同步
},
setItem(key, value) {
uni.setStorageSync(key, value); // 同步
}
}
}
},
)
当平台不是 H5 时,
persist
选项被设置为一个自定义的存储对象,其中包含getItem
和setItem
方法,用于指定如何读取和写入存储。这里使用的是uni.getStorageSync
和uni.setStorageSync
。有关storage的知识可以跳转下面链接:uni.setStorage(OBJECT) @setstorage | uni-app官网
随后我们就可以在vue项目文件中来使用持久化存储的数据:
TypeScript
<script setup lang="ts">
import { useMemberStore } from '@/stores';
const memberStore = useMemberStore();
</script>
<template>
<view class="my">
<view>会员信息:{{ memberStore.profile }}</view>
<button
@tap="
memberStore.setProfile({
username: '记得开心一点嘛',
token : '12345'
})
"
size="mini"
plain
type="primary"
>
保存用户信息
</button>
<button @tap="memberStore.clearProfile()" size="mini" plain type="warn">清理用户信息</button>
</view>
</template>
<style lang="scss">
//
</style>
7.拦截器的使用:

TypeScript
import { useMemberStore } from "@/stores";
const baseURL = "https://pcapi-xiaotuxian-front-devtest.itheima.net";
// 添加拦截器
const httpInterceptor = {
// 拦截前触发,该方法会在每次发起 HTTP 请求之前被调用
invoke(options:UniApp.RequestOptions){
// 1.非 http 开头需拼接地址
if(!options.url.startsWith('http')){
options.url = baseURL + options.url
}
// 2.请求超时,60s
options.timeout = 10000;
// 3.添加小程序端请求头标识
options.header = {
...options.header,
'source-client' : 'miniapp',
}
// 4.添加 token 请求头标识
const memberStore = useMemberStore();
const token = memberStore.profile?.token;
if(token){
options.header.Authorization = token
}
console.log(options);
}
}
uni.addInterceptor('request',httpInterceptor)
uni.addInterceptor('uploadFile',httpInterceptor)
// 请求函数封装
interface Data<T>{
code: string,
msg: string,
data: T
}
export const http = <T>(options:UniApp.RequestOptions) => {
// 1.返回 Promise 对象
return new Promise<Data<T>>((resolve,reject)=>{
uni.request({
...options,
// 2.请求成功
success(res){
if(res.statusCode >= 200 && res.statusCode < 300){
// 获取数据成功,调用 resolve
resolve(res.data as Data<T>) // 断言
}else if(res.statusCode === 401){
// 401错误,调用 reject,清理用户信息并跳转到登录页
const memberStore = useMemberStore()
memberStore.clearProfile();
// 跳转
uni.navigateTo({
url: '/src/pages/login/login'
})
reject(res)
}else{
// 通用错误,调用 reject
uni.showToast({
icon: 'none',
title: (res.data as Data<T>).msg || '请求错误'
})
reject(res)
}
},
// 响应失败
fail(err){
// 网络错误,调用reject
uni.showToast({
icon: 'none',
title: '网络错误,换个网络试试'
})
reject(err)
}
})
})
}
①请求拦截器 (
httpInterceptor
):用于在请求发送之前对请求进行修改和增强,比如拼接请求 URL、设置请求头、添加 token 等。②封装请求函数 (
http
):使用uni.request
封装一个返回Promise
的 HTTP 请求函数,统一处理请求成功和失败的逻辑,并根据响应的状态码进行不同的处理。
下面我们来分段解析一下上述代码:
(1)定义请求拦截器:
invoke
方法:这是一个请求拦截器的回调函数,它在每次发起 HTTP 请求之前都会被调用。你可以在此修改请求的配置,加入统一的参数、头部信息等。
TypeScript
const httpInterceptor = {
// 拦截前触发,该方法会在每次发起 HTTP 请求之前被调用
invoke(options: UniApp.RequestOptions) {
// 1.非 http 开头需拼接地址
if (!options.url.startsWith('http')) {
options.url = baseURL + options.url;
}
// 2.请求超时,60s
options.timeout = 10000;
// 3.添加小程序端请求头标识(为了标识当前请求来源于小程序)
options.header = {
...options.header, // ...options.header 是 JavaScript 的扩展运算符语法
// 它用于将原来的请求头属性保留并展开,同时可以新增或覆盖某些字段。
'source-client': 'miniapp', // 添加 source-client 标识
};
// 4.添加 token 请求头标识
const memberStore = useMemberStore();
const token = memberStore.profile?.token;
if (token) {
options.header.Authorization = token;
}
console.log(options); // 在控制台打印请求的配置信息,便于调试
}
};
(2)使用 uni.addInterceptor
添加拦截器:
TypeScript
uni.addInterceptor('request', httpInterceptor);
uni.addInterceptor('uploadFile', httpInterceptor);
- 使用
uni.addInterceptor
添加请求拦截器和文件上传拦截器。httpInterceptor
是前面定义的拦截器对象,它会在每次uni.request
和uni.uploadFile
请求之前触发,进行统一的配置修改。- 这样,所有的 HTTP 请求和文件上传请求都可以使用统一 的拦截器进行处理 ,确保请求头、URL 拼接、token 等的统一管理。
(3)请求函数封装:
TypeScript
interface Data<T> {
code: string;
msg: string;
data: T;
}
Data<T>
:这是一个泛型接口,用于定义请求成功后的数据结构。code
和msg
是 API 返回的状态码和消息,data
是返回的实际数据。通过泛型<T>
,我们可以灵活地处理不同类型的响应数据。
(4)请求封装:
TypeScript
export const http = <T>(options: UniApp.RequestOptions) => {
// 1.返回 Promise 对象
return new Promise<Data<T>>((resolve, reject) => {
uni.request({
...options, // 2.将传入的请求配置(options)解构并传入 uni.request
// 通过 ...options 将传入的 options 配置对象解构并传给 uni.request
// 这样可以动态修改请求的各项设置(如 URL、请求头等)。
这段代码是一个 uni-app 中的请求封装,使用了
Promise
和uni.request
来处理 HTTP 请求。它的功能是向服务器发起请求并根据响应结果执行不同的逻辑。
return new Promise<Data<T>>((resolve, reject) => {...})
:该函数返回一个
Promise
对象,类型为Data<T>
,表示异步操作的最终结果 。Data<T>
是一个接口,包含了code
、msg
和data
三个字段,其中data
的类型是泛型T
,即返回的数据类型。resolve
用于处理成功的回调,reject
用于处理失败的回调。
TypeScript
uni.request({
...options,
// 2.请求成功
success(res){
if(res.statusCode >= 200 && res.statusCode < 300){
// 获取数据成功,调用 resolve
resolve(res.data as Data<T>) // 断言
}else if(res.statusCode === 401){
// 401错误,调用 reject,清理用户信息并跳转到登录页
const memberStore = useMemberStore()
memberStore.clearProfile();
// 跳转
uni.navigateTo({
url: '/src/pages/login/login'
})
reject(res)
}else{
// 通用错误,调用 reject
uni.showToast({
icon: 'none',
title: (res.data as Data<T>).msg || '请求错误'
})
reject(res)
}
}
}
①success(res)
:当请求成功并且响应返回时,
success
回调函数会被触发,res
是响应的结果对象。
②resolve(res.data as Data<T>)
:如果响应成功且
statusCode
合法,调用resolve
返回数据,res.data
是服务器返回的数据。as Data<T>
是 TypeScript 的类型断言,告诉编译器res.data
确实符合Data<T>
类型,这样可以确保返回的数据结构是我们期望的。③**
uni.navigateTo({ url: '/src/pages/login/login' })
**:跳转到登录页面,让用户重新登录。
④**
reject(res)
**:调用
reject
,将响应对象res
作为参数传递,表示请求失败。后续可以通过catch
来处理这个错误。⑤**
uni.showToast({ icon: 'none', title: (res.data as Data<T>).msg || '请求错误' })
**:使用
uni.showToast
显示一个提示消息,提示用户请求出错。消息内容是从res.data
中提取的msg
字段(如果存在),如果没有,则使用默认消息'请求错误'
。
TypeScript
fail(err) {
// 网络错误,调用 reject
uni.showToast({
icon: 'none',
title: '网络错误,换个网络试试'
});
reject(err);
}
fail(err)
:
fail
回调函数用于处理请求失败的情况,如网络不可用、请求超时等错误。
(5)发起 HTTP 请求并处理返回的数据:
TypeScript
<script setup lang="ts">
import { useMemberStore } from '@/stores'
import '@/utils/http'
import { http } from '@/utils/http';
const memberStore = useMemberStore()
const getData = async () =>{
const res = await http<string[]>({
method:'GET',
url: '/home/banner',
header : {},
})
console.log("请求成功",res);
}
</script>
<template>
<view class="my">
<view>会员信息:{{ memberStore.profile }}</view>
<button
@tap="
memberStore.setProfile({
nickname: '黑马先锋',
token : '12345'
})
"
size="mini"
plain
type="primary"
>
保存用户信息
</button>
<button @tap="memberStore.clearProfile()" size="mini" plain type="warn">清理用户信息</button>
<button @tap="getData()" size="mini" plain type="warn">测试请求</button>
</view>
</template>
<style lang="scss">
//
</style>
await http<string[]>({...})
:调用之前引入的
http
函数发起请求。这里传入了请求的配置:
method: 'GET'
:请求方法是 GET,意味着从服务器获取数据。url: '/home/banner'
:请求的 URL 地址,这里是请求首页的横幅数据。header: {}
:请求头,这里为空对象,表示没有添加额外的请求头(实际应用中可能需要添加认证信息、请求类型等)。
await
:等待
http
请求返回的结果。由于http
返回的是一个Promise
对象,await
会让函数暂停,直到Promise
被解决(即请求完成)。
const res
:请求的响应结果会赋值给
res
,类型是string[]
(字符串数组)。这意味着我们期望返回的是一个字符串数组,可能是首页的横幅数据。