10946 字
55 分钟

uni-app快速上手

发布于 2024-12-29
NOTE

本文的大部分内容源于uni-app官方教程Vue官方教程的二次整合

0. uni-app#

uni-app 是一个使用 Vue.js 开发所有前端应用的框架,开发者编写一套代码,可发布到iOS、Android、Web(响应式)、以及各种小程序(微信/支付宝/百度/头条/飞书/QQ/快手/钉钉/淘宝)、快应用等多个平台。(摘自uni-app官网的介绍)

在上手uni-app之前,推荐下载官方提供的HBuilderX以便后续开发,具体的教程可以参考uni-app 快速上手

一个uni-app工程,默认包含如下目录及文件(源自工程简介 | uni-app官网):

┌─uniCloud 云空间目录,支付宝小程序云为uniCloud-alipay,阿里云为uniCloud-aliyun,腾讯云为uniCloud-tcb
│─components 符合vue组件规范的uni-app组件目录
│ └─comp-a.vue 可复用的a组件
├─utssdk 存放uts文件(已废弃)
├─pages 业务页面文件存放的目录
│ ├─index
│ │ └─index.vue index页面
│ └─list
│ └─list.vue list页面
├─static 存放应用引用的本地静态资源(如图片、视频等)的目录,注意:静态资源都应存放于此目录
├─uni_modules 存放uni_module
├─platforms 存放各平台专用页面的目录
├─nativeplugins App原生语言插件
├─nativeResources App端原生资源目录
│ ├─android Android原生资源目录
| └─ios iOS原生资源目录
├─hybrid App端存放本地html文件的目录
├─wxcomponents 存放微信小程序、QQ小程序组件的目录
├─mycomponents 存放支付宝小程序组件的目录
├─swancomponents 存放百度小程序组件的目录
├─ttcomponents 存放抖音小程序、飞书小程序组件的目录
├─kscomponents 存放快手小程序组件的目录
├─jdcomponents 存放京东小程序组件的目录
├─unpackage 非工程代码,一般存放运行或发行的编译结果
├─main.js Vue初始化入口文件
├─App.vue 应用配置,用来配置App全局样式以及监听 应用生命周期
├─pages.json 配置页面路由、导航条、选项卡等页面类信息
├─manifest.json 配置应用名称、appid、logo、版本等打包信息
├─AndroidManifest.xml Android原生应用清单文件
├─Info.plist iOS原生应用配置文件
└─uni.scss 内置的常用样式变量

.vue文件的结构

<!-- 负责定义组件的 HTML 结构 和 UI 模板 -->
<template>
</template>
<!-- 定义组件的 逻辑和数据 -->
<script setup>
</script>
<!-- 定义组件的 样式 -->
<style lang="scss">
</style>

在对uni-app有了基本的了解之后,将开始上手学习如何编写一个uni-app程序

1. 基本组件#

1.1 视图容器#

1. view#

view是最基本的视图容器,可以用于包裹各种元素内容。

常用属性:

属性名类型默认值说明
hover-classStringnone指定按下去的样式类。当 hover-class=“none” 时,没有点击态效果
hover-start-timeNumber50按住后多久出现点击态,单位毫秒
hover-stay-timeNumber400手指松开后点击态保留时间,单位毫秒

2. scroll-view#

scroll-view为可滚动视图区域,用于区域滚动。

常用属性:

属性名类型默认值说明
scroll-xBooleanfalse允许横向滚动
scroll-yBooleanfalse允许纵向滚动
show-scrollbarBooleanfalse控制是否出现滚动条

横向滚动:

当我们希望做出横向滚动的效果时,如果只配置scroll-x会发现并不能正常生效。此时,我们还需要使用css进行额外配置,如下所示:

<template>
<view>
<scroll-view scroll-x>
<view class="box" style=" background-color: #f00;">1</view>
<view class="box" style=" background-color: #0f0;">2</view>
<view class="box" style=" background-color: #00f;">3</view>
<view class="box" style=" background-color: #f0f;">4</view>
<view class="box" style=" background-color: #ff0;">5</view>
<view class="box" style=" background-color: #f00;">6</view>
<view class="box" style=" background-color: #0f0;">7</view>
<view class="box" style=" background-color: #00f;">8</view>
<view class="box" style=" background-color: #f0f;">9</view>
<view class="box" style=" background-color: #ff0;">10</view>
</scroll-view>
</view>
</template>
<style>
scroll-view {
width: 300px;
height: 50px;
/* 确保内容不换行 */
white-space: nowrap;
.box {
/* 使得这些元素以行内块的形式显示 */
display: inline-block;
}
}
</style>

3. swiper#

swiper是滑块视图容器,一般用于左右滑动或上下滑动,比如banner轮播图。

常用属性:

属性名类型默认值说明
indicator-dotsBooleanfalse是否显示面板指示点
indicator-colorColorrgba(0, 0, 0, .3)指示点颜色
indicator-active-colorColor#000000当前选中的指示点颜色
autoplayBooleanfalse是否自动切换
intervalNumber5000自动切换时间间隔
durationNumber500滑动动画时长
circularBooleanfalse是否采用衔接滑动,即播放到末尾后重新回到开头
verticalBooleanfalse滑动方向是否为纵向
previous-marginString0px前边距,可用于露出前一项的一小部分,接受 px 和 rpx 值
next-marginString0px后边距,可用于露出后一项的一小部分,接受 px 和 rpx 值

当在swiper中显示内容时,可以使用swiper-item来进行显示,其宽高会自动设置为100%(相对于父组件)。

<template>
<view>
<swiper indicator-dots="" autoplay="" interval="3000">
<swiper-item>
<image src="../../../static/p1.jpg" mode="aspectFit"></image>
</swiper-item>
<swiper-item>
<image src="../../../static/p2.jpg" mode="aspectFit"></image>
</swiper-item>
<swiper-item>
<image src="../../../static/p3.jpg" mode="aspectFit"></image>
</swiper-item>
<swiper-item>
<image src="../../../static/p4.jpg" mode="aspectFit"></image>
</swiper-item>
</swiper>
</view>
</template>

1.2 基础内容#

1. text#

text是文本组件,用于包裹文本内容。

常用属性:

属性名类型默认值说明平台差异说明
selectableBooleanfalse文本是否可选
user-selectBooleanfalse文本是否可选微信小程序
spaceString显示连续空格钉钉小程序不支持
decodeBooleanfalse是否解码百度、钉钉小程序不支持

space 值说明

说明
ensp中文字符空格一半大小
emsp中文字符空格大小
nbsp根据字体设置的空格大小

1.3 表单组件#

1. button#

button为按钮组件。

常用属性:

属性名类型默认值说明
sizeStringdefault按钮的大小(default:默认大小;mini:小尺寸)
typeStringdefault按钮的样式类型
plainBooleanfalse按钮是否镂空,背景色透明
disabledBooleanfalse是否禁用
loadingBooleanfalse名称前是否带 loading 图标
hover-classStringbutton-hover指定按钮按下去的样式类。当 hover-class=“none” 时,没有点击态效果
hover-start-timeNumber20按住后多久出现点击态,单位毫秒
hover-stay-timeNumber70手指松开后点击态保留时间,单位毫秒

2. input#

input是单行输入框。

常用属性:

属性名类型默认值说明
valueString输入框的初始内容
typeStringtextinput 的类型 有效值
passwordBooleanfalse是否是密码类型
placeholderString输入框为空时占位符
disabledBooleanfalse是否禁用
maxlengthNumber140最大输入长度,设置为 -1 的时候不限制最大长度
@focusEventHandle输入框聚焦时触发,event.detail = { value, height },height 为键盘高度
@blurEventHandle输入框失去焦点时触发,event.detail = {value: value}
@confirmEventHandle点击完成按钮时触发(电脑端敲回车),event.detail = {value: value}

type 有效值

说明平台差异说明
text文本输入键盘
number数字输入键盘均支持,App平台、H5平台 3.1.22 以下版本 vue 页面在 iOS 平台显示的键盘包含负数和小数。
idcard身份证输入键盘微信、支付宝、百度、QQ小程序、快手小程序、京东小程序
digit带小数点的数字键盘均支持,App平台、H5平台 vue 页面在 iOS 平台显示的键盘包含负数(原生键盘不支持负号)。
tel电话输入键盘
safe-password密码安全输入键盘微信小程序
nickname昵称输入键盘微信小程序

3. switch#

switch是开关选择器。

常用属性:

属性名类型默认值说明
checkedBooleanfalse是否选中
disabledBooleanfalse是否禁用
typeStringswitch样式,有效值:switch, checkbox
colorColorswitch 的颜色,同 css 的 color
@changeEventHandlechecked 改变时触发 change 事件,event.detail={ value}

4. checkbox#

checkbox-group为多选框组,用于包裹一组checkbox

常用属性:

属性名类型说明
@changeEventHandle<checkbox-group>中选中项发生改变是触发 change 事件,detail = {value:[选中的checkbox的value的数组]}

checkbox为多选项。在1组check-group中可选择多个

常用属性:

属性名类型默认值说明
valueString<checkbox> 标识,选中时触发 <checkbox-group> 的 change 事件,并携带 <checkbox> 的 value。
disabledBooleanfalse是否禁用
checkedBooleanfalse当前是否选中,可用来设置默认选中

使用案例:

<template>
<view>
<div>Checked names: {{ checkedNames }}</div>
<checkbox-group @change="onChange">
<checkbox value="Jack" /><text> Jack </text>
<checkbox value="Mike" /><text> Mike </text>
<checkbox value="John" /><text> John </text>
</checkbox-group>
</view>
</template>
<script setup>
import {ref} from "vue"
const checkedNames = ref([])
const onChange = (e) => {
checkedNames.value = e.detail.value;
}
</script>

1.4 路由与页面跳转#

1. navigator#

navigator用于页面跳转,该组件类似HTML中的<a>组件,但只能跳转本地页面。目标页面必须在pages.json中注册。

属性说明

属性名类型默认值说明
urlString应用内的跳转链接,值为相对路径或绝对路径,如:”../first/first”,“/pages/first/first”,注意不能加 .vue 后缀
open-typeStringnavigate跳转方式
deltaNumber当 open-type 为 ‘navigateBack’ 时有效,表示回退的层数
hover-classStringnavigator-hover指定点击时的样式类,当hover-class=“none”时,没有点击态效果

open-type 有效值

说明平台差异说明
navigate对应 uni.navigateTo 的功能
redirect对应 uni.redirectTo 的功能
switchTab对应 uni.switchTab 的功能
reLaunch对应 uni.reLaunch 的功能抖音小程序与飞书小程序不支持
navigateBack对应 uni.navigateBack 的功能
exit退出小程序,target=“miniProgram”时生效微信2.1.0+、百度2.5.2+、QQ1.4.7+

1.5 媒体组件#

1. image#

imgae为图片组件,用于显示图片。

常用属性:

属性名类型默认值说明平台差异说明
srcString图片资源地址
modeString‘scaleToFill’图片裁剪、缩放的模式
lazy-loadBooleanfalse图片懒加载。只针对page与scroll-view下的image有效微信小程序、百度小程序、抖音小程序、飞书小程序
webpbooleanfalse在系统不支持webp的情况下是否单独启用webp。默认false,只支持网络资源。webp支持详见下面说明微信小程序2.9.0

mode 有效值

mode 有 14 种模式,其中 5 种是缩放模式,9 种是裁剪模式。

模式说明
缩放scaleToFill不保持纵横比缩放图片,使图片的宽高完全拉伸至填满 image 元素
缩放aspectFit保持纵横比缩放图片,使图片的长边能完全显示出来。也就是说,可以完整地将图片显示出来。
缩放aspectFill保持纵横比缩放图片,只保证图片的短边能完全显示出来。也就是说,图片通常只在水平或垂直方向是完整的,另一个方向将会发生截取。
缩放widthFix宽度不变,高度自动变化,保持原图宽高比不变
缩放heightFix高度不变,宽度自动变化,保持原图宽高比不变
裁剪top不缩放图片,只显示图片的顶部区域
裁剪bottom不缩放图片,只显示图片的底部区域
裁剪center不缩放图片,只显示图片的中间区域
裁剪left不缩放图片,只显示图片的左边区域
裁剪right不缩放图片,只显示图片的右边区域
裁剪top left不缩放图片,只显示图片的左上边区域
裁剪top right不缩放图片,只显示图片的右上边区域
裁剪bottom left不缩放图片,只显示图片的左下边区域
裁剪bottom right不缩放图片,只显示图片的右下边区域

2. vue3 语法#

此部分内容基于Vue 官方文档整合

2.1 模板语法#

1. 插值表达式#

最基本的数据绑定形式是插值表达式,用于在视图中动态地显示变量、表达式或计算结果。插值表达式通过双大括号 {{}} 的形式嵌入在模板中。

<template>
<view>
{{name}}
</view>
</template>
<script setup>
const name = "张三"
</script>
<style>
</style>

插值表达式支持以下内容:

  1. 简单变量

    <p>{{ message }}</p>
  2. 复杂表达式

    <p>{{ number1 + number2 }}</p>
  3. 三元运算符

    <p>{{ condition ? '是' : '否' }}</p>
  4. 方法调用

    <p>{{ greet('小明') }}</p>

2. Attribute 绑定#

双大括号不能在 HTML attributes 中使用。想要响应式地绑定一个 attribute,应该使用v-bind 指令。

<div v-bind:id="dynamicId"></div>

因为 v-bind 非常常用,因此官方提供了特定的简写语法:

<div :id="dynamicId"></div>

使用举例:

<template>
<view>
<image :src = "url"></image>
</view>
</template>
<script setup>
let url = "https://img.yzcdn.cn/vant/cat.jpeg"
</script>

2.2 响应式基础#

1. ref#

在组合式 API 中,推荐使用ref()函数来声明响应式状态。值通过 .value 访问或修改。在模板中使用时会自动拆箱,不需要使用.value

示例:

<template>
<view>
<text>{{name}}今年 {{age}} 岁了</text>
<text>\n{{user.name}}今年 {{user.age}} 岁了</text>
</view>
</template>
<script setup>
import {
ref
} from "vue"
const name = "张三"
const age = ref(18)
let user = ref({
name: "李四",
age: 20
})
setInterval(() => {
age.value++
user.value.age += 10
}, 1000)
</script>
<style>
</style>

2.3 class与style绑定#

1. 绑定HTML class#

通过绑定绑定HTML class,我们可以实现动态渲染。并且,:class 指令也可以和一般的 class attribute 共存。举例来说,下面这样的状态:

const isActive = ref(true)
const hasError = ref(false)

配合以下模板:

<div
class="static"
:class="{ active: isActive, 'text-danger': hasError }"
></div>

渲染的结果会是:

<div class="static active"></div>

isActive 或者 hasError 改变时,class 列表会随之更新。举例来说,如果 hasError 变为 true,class 列表也会变成 "static active text-danger"

2. 绑定内联样式#

:style 支持绑定 JavaScript 对象值,对应的是HTML元素的 style 属性:

<script>
const activeColor = ref('red')
const fontSize = ref(30)
</script>
<div :style="{ color: activeColor, fontSize: fontSize + 'px' }"></div>

尽管推荐使用 camelCase,但 :style 也支持 kebab-cased 形式的 CSS 属性 key (对应其 CSS 中的实际名称),例如:

<div :style="{ 'font-size': fontSize + 'px' }"></div>

为了使代码更加简洁,可可以直接绑定一个样式对象

<script>
const styleObject = reactive({
color: 'red',
fontSize: '30px'
})
</script>
<div :style="styleObject"></div>

2.4 事件处理#

我们可以使用 v-on 指令 (简写为 @) 来监听 DOM 事件,并在事件触发时执行对应的 JavaScript。用法:v-on:click="handler"@click="handler"

下面给出一个结合多个组件的事件处理案例:

<template>
<view>
<switch @change="isChange"/>
<button type=" primary" :loading="isLoading" :disabled="isLoading"> 按钮 </button>
</view>
</template>
<script setup>
import {
ref
} from "vue"
const isLoading = ref(false)
function isChange(e) {
isLoading.value = e.detail.value
}
</script>

2.5 条件渲染#

v-if 指令用于条件性地渲染一块内容。这块内容只会在指令的表达式返回真值时才被渲染。v-else-if 提供的是相应于 v-ifelse if区块。v-else则是对应的else区块。

使用案例:

<div v-if="type === 'A'">
A
</div>
<div v-else-if="type === 'B'">
B
</div>
<div v-else-if="type === 'C'">
C
</div>
<div v-else>
Not A/B/C
</div>

因为 v-if 是一个指令,他必须依附于某个元素。但如果我们想要切换不止一个元素呢?在这种情况下我们可以在一个 <template> 元素上使用 v-if,这只是一个不可见的包装器元素,最后渲染的结果并不会包含这个 <template> 元素。

<template v-if="ok">
<h1>Title</h1>
<p>Paragraph 1</p>
<p>Paragraph 2</p>
</template>

另一个可以用来按条件显示一个元素的指令是 v-show。其用法基本一样:

<h1 v-show="ok">Hello!</h1>

不同之处在于 v-show 会在 DOM 渲染中保留该元素;v-show 仅切换了该元素上名为 display 的 CSS 属性。并且v-show 不支持在 <template> 元素上使用,也不能和 v-else 搭配使用。

2.6 列表渲染#

我们可以使用 v-for 指令基于一个数组来渲染一个列表。v-for 指令的值需要使用 item in items 形式的特殊语法,其中 items 是源数据的数组,而 item 是迭代项的别名, index是对应元素的下标:

<template>
<view class="">
<text v-for="(item, index) in peoples" :key="item.id">
{{ item.id }}: {{ item.name }} 今年 {{ item.age }} \n
</text>
</view>
</template>
<script setup>
import {
ref
} from "vue";
const peoples = ref([{
id: 1,
name: "张三",
age: 18
},
{
id: 2,
name: "李四",
age: 20
},
{
id: 3,
name: "王五",
age: 22
}
])
</script>
<style lang="scss" scoped>
</style>
NOTE

key最好使用数组中元素的唯一值而不要直接使用index索引,以免出现问题

2.7 表单输入绑定#

在前端处理表单时,我们常常需要将表单输入框的内容同步给 JavaScript 中相应的变量。

<input
:value="text"
@input="event => text = event.target.value">

v-model 指令提供了一种简化的绑定方式:

<input v-model="text">

我们也可以将多个复选框绑定到同一个数组或集合:

<template>
<div>Checked names: {{ checkedNames }}</div>
<input type="checkbox" id="jack" value="Jack" v-model="checkedNames" />
<label for="jack">Jack</label>
<input type="checkbox" id="john" value="John" v-model="checkedNames" />
<label for="john">John</label>
<input type="checkbox" id="mike" value="Mike" v-model="checkedNames" />
<label for="mike">Mike</label>
</template>
<script>
const checkedNames = ref([])
</script>

2.8 计算属性#

如果在模板中写太多逻辑,会让模板变得臃肿,难以维护。例如下面这个例子:

<template>
<p>Has published books:</p>
<span>{{ author.books.length > 0 ? 'Yes' : 'No' }}</span>
</template>
<script>
const author = reactive({
name: 'John Doe',
books: [
'Vue 2 - Advanced Guide',
'Vue 3 - Basic Guide',
'Vue 4 - The Mystery'
]
})
</script>

因此官方推荐使用计算属性来描述依赖响应式状态的复杂逻辑。这是重构后的示例:

<script setup>
import { reactive, computed } from 'vue'
const author = reactive({
name: 'John Doe',
books: [
'Vue 2 - Advanced Guide',
'Vue 3 - Basic Guide',
'Vue 4 - The Mystery'
]
})
// 一个计算属性 ref
const publishedBooksMessage = computed(() => {
return author.books.length > 0 ? 'Yes' : 'No'
})
</script>
<template>
<p>Has published books:</p>
<span>{{ publishedBooksMessage }}</span>
</template>

并且由于计算属性值会基于其响应式依赖被缓存,一个计算属性仅会在其响应式依赖更新时才重新计算。这意味着只要 author.books 不改变,无论多少次访问 publishedBooksMessage 都会立即返回先前的计算结果,而不用重复执行 getter 函数。

2.9 监听器#

在组合式 API 中,我们可以使用watch函数在每次响应式状态发生变化时触发回调函数。watch 的第一个参数可以是不同形式的“数据源”:它可以是一个 ref (包括计算属性)、一个响应式对象、一个 getter 函数、或多个数据源组成的数组。

const x = ref(0)
const y = ref(0)
// 单个 ref
watch(x, (newX) => {
console.log(`x is ${newX}`)
})
// getter 函数
watch(
() => x.value + y.value,
(sum) => {
console.log(`sum of x + y is: ${sum}`)
}
)
// 多个来源组成的数组
watch([x, () => y.value], ([newX, newY]) => {
console.log(`x is ${newX} and y is ${newY}`)
})

深层监听器

直接给 watch() 传入一个响应式对象,会隐式地创建一个深层侦听器——该回调函数在所有嵌套的变更时都会被触发:

const obj = reactive({ count: 0 })
watch(obj, (newValue, oldValue) => {
// 在嵌套的属性变更时触发
// 注意:`newValue` 此处和 `oldValue` 是相等的
// 因为它们是同一个对象!
})
obj.count++

或者通过显式指定的方式,将其强制转成深层侦听器:

watch(
() => state.someObject,
(newValue, oldValue) => {
// 注意:`newValue` 此处和 `oldValue` 是相等的
// *除非* state.someObject 被整个替换了
},
{ deep: true }
)

3. 组件#

组件是 Vue(或其他现代框架)中的核心概念之一,表示一个独立、可复用的 UI 单元。它可以包含 HTML 模板、JavaScript 逻辑 和 CSS 样式,用于构建页面的功能模块。在开发中,我们通常将页面拆分为多个组件,每个组件负责独立的功能或展示特定的内容,比如按钮、表单、导航栏等。

3.1 注册组件#

在uni-app工程根目录下的 components 目录,创建并存放自定义组件:

│─components 符合vue组件规范的uni-app组件目录
│ └─componentA 符合‘components/组件名称/组件名称.vue’目录结构,easycom方式可直接使用组件
│ └─componentA.vue 可复用的componentA组件
│ └─component-a.vue 可复用的component-a组件

在注册一个组件的时候,我们始终需要给它一个名字。 定义组件名的方式有两种:

  • 使用 kebab-case:当使用 kebab-case (短横线分隔命名) 定义一个组件时,你也必须在引用这个自定义元素时使用 kebab-case,例如 <my-component-name>

  • 使用 PascalCase:当使用 PascalCase (首字母大写命名) 定义一个组件时,你在引用这个自定义元素时两种命名法都可以使用。 也就是说 <my-component-name><MyComponentName> 都是可接受的。

全局注册:

uni-app 支持配置全局组件,需在 main.js 里进行全局注册,注册后就可在所有页面里使用该组件。

  1. main.js 里进行全局导入和注册
import App from './App'
import {createSSRApp} from 'vue'
//引入组件
import myComponent from './components/my-component/my-component.vue'
export function createApp() {
const app = createSSRApp(App)
//调用app.component方法全局注册组件
app.component('my-component', myComponent)
return {
app
}
}
  1. index.vue 里可直接使用组件
<template>
<view>
<my-component></my-component>
</view>
</template>

局部注册:

  1. 传统vue规范: 在 index.vue 页面中,通过 import 方式引入组件 ,在 components 选项中定义你想要使用的组件。

    对于 components 对象中的每个 property 来说,其 property 名就是自定义元素的名字,其 property 值就是这个组件的选项对象。

    在对象中放一个类似 uniBadge 的变量名其实是 uniBadge : uniBadge 的缩写,即这个变量名同时是:

    • 用在模板中的自定义元素的名称
    • 包含了这个组件选项的变量名(仅支持驼峰法命名)
<!-- 在index.vue引入 uni-badge 组件-->
<template>
<view>
<uni-badge text="1"></uni-badge><!-- 3.使用组件 -->
</view>
</template>
<script>
import uniBadge from '@/components/uni-badge/uni-badge.vue';//1.导入组件(这步属于传统vue规范,但在uni-app的easycom下可以省略这步)
export default {
components:{uniBadge }//2.注册组件(这步属于传统vue规范,但在uni-app的easycom下可以省略这步)
}
</script>
  1. 通过uni-app的easycom: 将组件引入精简为一步。只要组件安装在项目的 components 目录下,并符合 components/组件名称/组件名称.vue 目录结构。就可以不用引用、注册,直接在页面中使用。
<!-- 在index.vue引入 uni-badge 组件-->
<template>
<view>
<uni-badge text="1"></uni-badge><!-- 3.使用组件 -->
</view>
</template>
<script>
// 这里不用import引入,也不需要在components内注册uni-badge组件。template里就可以直接用
export default {
data() {
return {
}
}
}
</script>

3.2 props传值#

props 可以是数组或对象,用于接收来自父组件的数据。props 可以是简单的数组,或者使用对象作为替代,对象允许配置高级选项,如类型检测、自定义验证和设置默认值。

选项类型说明
typeStringNumberBooleanArrayObjectDateFunctionSymbol ,任何自定义构造函数、或上述内容组成的数组会检查一个 prop 是否是给定的类型,否则抛出警告,复杂数据类型需要通过 PropType 标记类型。
defaultany为该 prop 指定一个默认值。如果该 prop 没有被传入,则换做用这个值。对象或数组的默认值必须从一个工厂函数返回。
requiredBoolean定义该 prop 是否是必填项。
validatorFunction自定义验证函数会将该 prop 的值作为唯一的参数代入。在非生产环境下,如果该函数返回一个 false 的值 (也就是验证失败),一个控制台警告将会被抛出。

使用示例:

<template>
<view class="user-info">
<image :src="user.avatar" mode="aspectFill"></image>
<text> 用户名:{{user.name}} </text>
</view>
</template>
<script setup>
defineProps({
user: {
type: Object,
default () {
return {
name: "游客",
avatar: "../../../static/avatar.jpg"
}
}
}
});
</script>
<style lang="scss" scoped>
.user-info {
display: flex;
align-items: center;
flex-direction: column;
padding: 10px;
background-color: #eee;
border-bottom: 1px solid #f0f0f0;
margin: 10px;
}
.user-info image {
width: 100px;
height: 100px;
margin: 10px 0;
border-radius: 50%;
}
.user-info text {
font-size: 16px;
}
</style>

3.3 插槽#

插槽内容

插槽(Slot) 是 Vue 提供的一种机制,允许父组件向子组件传递自定义内容,并在子组件的特定位置进行渲染。插槽的主要用途是让子组件更加灵活和通用,适用于需要动态内容的场景。

它允许你像这样合成组件:

<todo-button>
Add todo
</todo-button>

然后在 todo-button 的模板中,你可能有:

<!-- todo-button 组件模板 -->
<button class="btn-primary">
<slot></slot>
</button>

当组件渲染的时候,将会被替换为“Add Todo”

<!-- 渲染 HTML -->
<button class="btn-primary">
Add todo
</button>

后备内容

有时为一个插槽设置具体的后备 (也就是默认的) 内容是很有用的,它只会在没有提供内容的时候被渲染。例如在一个 submit-button 组件中,我们可能希望这个 button 内绝大多数情况下都渲染文本“Submit”。为了将“Submit”作为后备内容,我们可以将它放在 slot 标签内:

<button type="submit">
<slot>Submit</slot>
</button>

现在当我在一个父级组件中使用 submit-button 并且不提供任何插槽内容时,后备内容“Submit”将会被渲染:

<button type="submit">
Submit
</button>

但是如果我们提供内容:

<submit-button>
Save
</submit-button>

则这个提供的内容将会被渲染从而取代后备内容:

<button type="submit">
Save
</button>

具名插槽

有时我们需要多个插槽,对于这样的情况,slot 元素有一个特殊的 attribute:name。这个 attribute 可以用来定义额外的插槽:

<view class="container">
<header>
<slot name="header"></slot>
</header>
<main>
<slot></slot>
</main>
<footer>
<slot name="footer"></slot>
</footer>
</view>

一个不带 nameslot 出口会带有隐含的名字default。在向具名插槽提供内容的时候,我们可以在一个 template 元素上使用 v-slot 指令,并以 v-slot 的参数的形式提供其名称:

<template>
<view>
<!-- 父组件使用子组件`<base-layout>`,节点上使用v-slot特性: -->
<base-layout>
<template v-slot:header>
<view>Here might be a page title</view>
</template>
<template v-slot:default>
<view>A paragraph for the main content.</view>
<view>And another one.</view>
</template>
<template v-slot:footer>
<view>Here's some contact info</view>
</template>
</base-layout>
</view>
</template>

v-onv-bind 一样,v-slot 也有缩写,即把参数之前的所有内容 (v-slot:) 替换为字符 #。例如 v-slot:header 可以被重写为 #header

<base-layout>
<template #header>
<view>Here might be a page title</view>
</template>
<template #default>
<view>A paragraph for the main content.</view>
<view>And another one.</view>
</template>
<template #footer>
<view>Here's some contact info</view>
</template>
</base-layout>

3.4 emit#

emit 是一种用于实现父子组件通信的机制。通过 emit,子组件可以向父组件发送事件,父组件可以监听这些事件并作出响应。

示例:

子组件代码

<template>
<button @click="handleClick">点击我</button>
</template>
<script setup>
const emit = defineEmits(['customEvent']); // 定义事件
const handleClick = () => {
emit('customEvent', 'Hello from child!'); // 触发事件,并传递数据
};
</script>

父组件代码

<template>
<ChildComponent @customEvent="handleEvent" />
</template>
<script setup>
import ChildComponent from './ChildComponent.vue';
const handleEvent = (message) => {
console.log('父组件接收到消息:', message);
};
</script>

3.5 expose#

在 Vue 3 的 <script setup> 语法中,defineExpose 用于显式地向父组件暴露子组件的属性或方法。这让父组件可以通过 ref 访问子组件中的指定内容。

使用示例:

子组件

<template>
<button @click="increment">点击增加</button>
</template>
<script setup>
import { ref } from 'vue';
const count = ref(0);
const increment = () => {
count.value++;
};
// 显式暴露 count 和 increment 给父组件
defineExpose({
count,
increment,
});
</script>

父组件

<template>
<ChildComponent ref="childRef" />
<button @click="logCount">获取子组件计数</button>
</template>
<script setup>
import { ref } from 'vue';
import ChildComponent from './ChildComponent.vue';
const childRef = ref(null);
const logCount = () => {
console.log('子组件计数值:', childRef.value.count); // 访问子组件的 count
childRef.value.increment(); // 调用子组件的方法
};
</script>

3.6 组件生命周期#

Vue 3 中的生命周期指组件从创建到销毁的完整过程,包括初始化、渲染、更新、销毁等阶段。Vue 提供了一系列生命周期钩子函数,让开发者可以在特定的时刻执行逻辑。

生命周期阶段#

  1. 创建阶段 组件实例初始化,各种属性如 propsdata 等被设置。
  2. 挂载阶段 模板编译为虚拟 DOM 并插入真实 DOM。
  3. 更新阶段 当响应式数据发生变化时,组件会重新渲染并更新 DOM。
  4. 销毁阶段 组件实例被销毁,清理资源。

生命周期钩子函数#

创建阶段

钩子函数说明
setup()组件初始化时调用。组合式 API 的入口点,定义响应式数据、方法等。
onBeforeMount在组件挂载到 DOM 之前调用。适合执行初始化逻辑,但此时无 DOM 结构。
onMounted组件挂载完成后调用。此时 DOM 可用,常用于操作 DOM 或发送请求。

更新阶段

钩子函数说明
onBeforeUpdate组件更新前调用,数据已变化,但 DOM 尚未更新。
onUpdated组件更新完成后调用,此时 DOM 已更新。

销毁阶段

钩子函数说明
onBeforeUnmount组件销毁前调用,适合清理资源。
onUnmounted组件销毁后调用,所有数据和事件监听器已被移除。

下面的组件示例包含几个常用的生命周期钩子:

<template>
<div>
<p>计数:{{ count }}</p>
<button @click="increment">增加</button>
</div>
</template>
<script setup>
import { ref, onMounted, onUpdated, onUnmounted } from 'vue';
const count = ref(0);
const increment = () => {
count.value++;
};
// 在组件挂载时执行
onMounted(() => {
console.log('组件已挂载');
});
// 在组件更新时执行
onUpdated(() => {
console.log('组件已更新,当前计数:', count.value);
});
// 在组件销毁时执行
onUnmounted(() => {
console.log('组件已销毁');
});
</script>

4. 页面相关#

4.1 css语法#

1. rpx#

rpxuni-app 中用于页面布局的一种响应式单位,全称为 “responsive pixel”,即“响应式像素”。它通过动态计算屏幕宽度,自动适配不同设备,特别适用于多屏适配需求的场景。

rpx 会根据设备屏幕宽度的不同而自动调整实际的像素值。例如:

  • 在屏幕宽度为 750px 的设备上,1rpx 等于 1px。
  • 在屏幕宽度为 375px 的设备上,1rpx 等于 0.5px。

2. 样式导入#

我们可以将样式等文件单独抽离出来成一个文件,当需要使用的时候再进行导入。可以在根目录下创建一个common文件夹,用于存储这些文件,这样只有在使用到的时候这些文件才会被打包进去,从而节省空间。

─common
└─css 存储css文件
└─scss 存储scss文件
└─js 存储js文件

样式引入:

<style lang="scss">
@import "@/common/css/my.css";
</style>

scss:

在根目录中的uni.scss文件中,定义了一系列的常用样式变量。同样的,我们可以在里面导入我们自定义的变量来使用

@import "@/common/scss/my.scss";

4.2 pages.json 页面路由#

1. globalStyle#

用于设置应用的状态栏、导航条、标题、窗口背景色等。

常用属性:

属性类型默认值描述
navigationBarBackgroundColorHexColor#F8F8F8导航栏背景颜色(同状态栏背景色)
navigationBarTextStyleStringblack导航栏标题颜色及状态栏前景颜色,仅支持 black/white
navigationBarTitleTextString导航栏标题文字内容
navigationStyleStringdefault导航栏样式,仅支持 default/custom。custom即取消默认的原生导航栏
enablePullDownRefreshBooleanfalse是否开启下拉刷新,详见页面生命周期
onReachBottomDistanceNumber50页面上拉触底事件触发时距页面底部距离,单位只支持px,详见页面生命周期

2. tabBar#

如果应用是一个多 tab 应用,可以通过 tabBar 配置项指定一级导航栏,以及 tab 切换时显示的对应页。

常用属性:

属性类型必填默认值描述
colorHexColortab 上的文字默认颜色
selectedColorHexColortab 上的文字选中时的颜色
backgroundColorHexColortab 的背景色
listArraytab 的列表,详见 list 属性说明,最少2个、最多5个 tab
fontSizeString10px文字默认大小

其中 list 接收一个数组,数组中的每个项都是一个对象,其属性值如下:

属性类型必填说明
pagePathString页面路径,必须在 pages 中先定义
textStringtab 上按钮文字,在 App 和 H5 平台为非必填。例如中间可放一个没有文字的+号图标
iconPathString图片路径,icon 大小限制为40kb,建议尺寸为 81px * 81px,当 position 为 top 时,此参数无效,不支持网络图片,不支持字体图标
selectedIconPathString选中时的图片路径,icon 大小限制为40kb,建议尺寸为 81px * 81px ,当 position 为 top 时,此参数无效

4.3 交互反馈#

1. showToast#

uni.showToast(OBJECT)用于显示消息提示框。

参数类型必填说明
titleString提示的内容,长度与 icon 取值有关。
iconString图标,有效值详见下方说明,默认:success。
imageString自定义图标的本地路径(app端暂不支持gif)
maskBoolean是否显示透明蒙层,防止触摸穿透,默认:false
durationNumber提示的延迟时间,单位毫秒,默认:1500

使用示例:

<template>
<view class="">
<button @click="onClick()">弹出提示框</button>
</view>
</template>
<script setup>
function onClick() {
uni.showToast({
title: 'Hello World',
icon: 'success'
})
}
</script>

image-20250101162445989

2. showLoading#

uni.showLoading(OBJECT)用于显示 loading 提示框, 需主动调用 uni.hideLoading 才能关闭提示框。

参数类型必填说明
titleString提示的文字内容,显示在loading的下方
maskBoolean是否显示透明蒙层,防止触摸穿透,默认:false
successFunction接口调用成功的回调函数
failFunction接口调用失败的回调函数
completeFunction接口调用结束的回调函数(调用成功、失败都会执行)

使用示例:

<template>
<view class="">
<button @click="onClick()">弹出提示框</button>
</view>
</template>
<script setup>
function onClick() {
uni.showLoading({
title: '加载中'
})
}
</script>

image-20250101163400062

3. showModal#

uni.showModal(OBJECT)显示模态弹窗,可以只有一个确定按钮,也可以同时有确定和取消按钮。

参数类型必填说明
titleString提示的标题
contentString提示的内容
showCancelBoolean是否显示取消按钮,默认为 true
cancelTextString取消按钮的文字,默认为”取消”
cancelColorHexColor取消按钮的文字颜色,默认为”#000000”
confirmTextString确定按钮的文字,默认为”确定”
confirmColorHexColor确定按钮的文字颜色,H5平台默认为”#007aff”,微信小程序平台默认为”#576B95”,百度小程序平台默认为”#3c76ff”
editableBoolean是否显示输入框
placeholderTextString显示输入框时的提示文本
successFunction接口调用成功的回调函数
failFunction接口调用失败的回调函数
completeFunction接口调用结束的回调函数(调用成功、失败都会执行)

使用示例:

<template>
<view class="">
<button @click="onClick()">弹出提示框</button>
</view>
</template>
<script setup>
function onClick() {
uni.showModal({
title: '提示',
content: '这是一个模态弹窗',
showCancel: true,
cancelText: '取消',
confirmText: '确定',
success: function(res) {
if (res.confirm) {
console.log('用户点击确定');
} else if (res.cancel) {
console.log('用户点击取消');
}
}
})
}
</script>

image-20250101162654571

4. showActionSheet#

uni.showActionSheet(OBJECT)用于生成从底部向上弹出操作菜单。

OBJECT参数说明

参数类型必填说明
titleString菜单标题
itemListArray<String>按钮的文字数组
successFunction接口调用成功的回调函数,详见返回参数说明
failFunction接口调用失败的回调函数
completeFunction接口调用结束的回调函数(调用成功、失败都会执行)

使用示例:

<template>
<view class="">
<button @click="onClick()">弹出提示框</button>
<text>您选择了:{{selected}}</text>
</view>
</template>
<script setup>
let list = ["学士", "硕士", "博士"]
let selected = ref("")
function onClick() {
uni.showActionSheet({
itemList: list,
success: function(res) {
console.log('选中了第' + (res.tapIndex + 1) + '个按钮');
console.log(list[res.tapIndex]);
selected.value = list[res.tapIndex]
}
});
}
</script>

image-20250101164727515

4.4 设置导航条#

uni.setNavigationBarTitle(OBJECT)动态设置当前页面的标题。

参数类型必填说明
titleString页面标题
successFunction接口调用成功的回调函数
failFunction接口调用失败的回调函数
completeFunction接口调用结束的回调函数(调用成功、失败都会执行)

使用示例:

uni.setNavigationBarTitle({
title: '新的标题'
});

4.5 设置TabBar#

1. tabBar文本#

uni.setTabBarBadge(OBJECT)为 tabBar 某一项的右上角添加文本。

参数类型必填说明
indexNumbertabBar的哪一项,从左边算起
textString显示的文本,不超过 3 个半角字符
successFunction接口调用成功的回调函数
failFunction接口调用失败的回调函数
completeFunction接口调用结束的回调函数(调用成功、失败都会执行)

使用示例:

uni.setTabBarBadge({
index: 0,
text: '1'
})

uni.removeTabBarBadge(OBJECT)移除 tabBar 某一项右上角的文本。

参数类型必填说明
indexNumbertabBar的哪一项,从左边算起
successFunction接口调用成功的回调函数
failFunction接口调用失败的回调函数
completeFunction接口调用结束的回调函数(调用成功、失败都会执行)

2. tabBar红点#

uni.showTabBarRedDot(OBJECT)显示 tabBar 某一项的右上角的红点。

参数类型必填说明
indexNumbertabBar的哪一项,从左边算起
successFunction接口调用成功的回调函数
failFunction接口调用失败的回调函数
completeFunction接口调用结束的回调函数(调用成功、失败都会执行)

uni.hideTabBarRedDot(OBJECT)隐藏 tabBar 某一项的右上角的红点。

参数类型必填说明
indexNumbertabBar的哪一项,从左边算起
successFunction接口调用成功的回调函数
failFunction接口调用失败的回调函数
completeFunction接口调用结束的回调函数(调用成功、失败都会执行)

4.6 下拉刷新#

在 js 中定义 onPullDownRefresh 处理函数(和onLoad等生命周期函数同级),监听该页面用户下拉刷新事件。

  • 需要在 pages.json 里,找到的当前页面的pages节点,并在 style 选项中开启 enablePullDownRefresh。
  • 当处理完数据刷新后,uni.stopPullDownRefresh 可以停止当前页面的下拉刷新。

uni.startPullDownRefresh(OBJECT):开始下拉刷新,调用后触发下拉刷新动画,效果与用户手动下拉刷新一致。

参数名类型必填说明
successFunction接口调用成功的回调
failFunction接口调用失败的回调函数
completeFunction接口调用结束的回调函数(调用成功、失败都会执行)

4.7 跳转和路由#

  • uni.navigateTo(OBJECT):保留当前页面,跳转到应用内的某个页面,使用uni.navigateBack可以返回到原页面。
  • uni.redirectTo(OBJECT):关闭当前页面,跳转到应用内的某个页面。
  • uni.reLaunch(OBJECT):关闭所有页面,打开到应用内的某个页面。
  • uni.switchTab(OBJECT):跳转到 tabBar 页面,并关闭其他所有非 tabBar 页面。

4.8 数据缓存#

1. uni.setStorageuni.getStorage#

  • uni.setStorage: 用于将数据存储到本地缓存中。
  • uni.getStorage: 用于从本地缓存中获取数据。
// 存储数据
uni.setStorage({
key: 'key_name',
data: 'value',
success: function () {
console.log('存储成功');
}
});
// 获取数据
uni.getStorage({
key: 'key_name',
success: function (res) {
console.log('获取到的数据:', res.data);
}
});

2. uni.setStorageSyncuni.getStorageSync#

  • uni.setStorageSync: 同步方式将数据存储到本地缓存中。
  • uni.getStorageSync: 同步方式从本地缓存中获取数据。

注意事项:

  • 存储的数据可以是任意类型,但最终会以字符串形式存储。
  • 存储的数据大小有限制,通常为 10MB 左右,具体限制因平台而异。
  • 存储的数据在应用卸载后会被清除。
  • 存储的数据是明文存储的,因此不建议存储敏感信息(如密码、token 等)。
// 存储数据
try {
uni.setStorageSync('key_name', 'value');
console.log('存储成功');
} catch (e) {
console.error('存储失败:', e);
}
// 获取数据
try {
const value = uni.getStorageSync('key_name');
console.log('获取到的数据:', value);
} catch (e) {
console.error('获取失败:', e);
}

3. uni.removeStorageuni.removeStorageSync#

  • uni.removeStorage: 异步方式从本地缓存中移除指定 key 的数据。
  • uni.removeStorageSync: 同步方式从本地缓存中移除指定 key 的数据。
// 异步移除数据
uni.removeStorage({
key: 'key_name',
success: function () {
console.log('移除成功');
}
});
// 同步移除数据
try {
uni.removeStorageSync('key_name');
console.log('移除成功');
} catch (e) {
console.error('移除失败:', e);
}

4. uni.clearStorageuni.clearStorageSync#

  • uni.clearStorage: 异步方式清除本地缓存中的所有数据。
  • uni.clearStorageSync: 同步方式清除本地缓存中的所有数据。
// 异步清除所有缓存
uni.clearStorage({
success: function () {
console.log('清除成功');
}
});
// 同步清除所有缓存
try {
uni.clearStorageSync();
console.log('清除成功');
} catch (e) {
console.error('清除失败:', e);
}

5. uni.getStorageInfouni.getStorageInfoSync#

  • uni.getStorageInfo: 异步方式获取当前缓存的相关信息。
  • uni.getStorageInfoSync: 同步方式获取当前缓存的相关信息。
// 异步获取缓存信息
uni.getStorageInfo({
success: function (res) {
console.log('缓存信息:', res);
}
});
// 同步获取缓存信息
try {
const info = uni.getStorageInfoSync();
console.log('缓存信息:', info);
} catch (e) {
console.error('获取缓存信息失败:', e);
}

4.9 网络请求#

1. request#

uni.request 是 UniApp 中用于发起网络请求的 API

参数说明

参数名类型必填说明
urlstring请求地址,必须是完整的 HTTP/HTTPS 地址。
methodstring请求方法,默认为 GET,支持 GETPOSTPUTDELETE 等。
dataobject请求参数,可以是对象或字符串。
headerobject请求头,可以设置 Content-TypeAuthorization 等。
timeoutnumber超时时间,单位为毫秒,默认为 60000(60 秒)。
dataTypestring返回数据的格式,默认为 json,支持 jsontext 等。
responseTypestring响应数据类型,默认为 text,支持 textarraybuffer 等。
successfunction请求成功的回调函数,参数为响应结果。
failfunction请求失败的回调函数,参数为错误信息。
completefunction请求完成的回调函数(无论成功或失败都会执行)。

返回值

uni.requestsuccess 回调函数会返回一个响应对象,包含以下字段:

字段名类型说明
dataany响应数据,类型由 dataTyperesponseType 决定。
statusCodenumberHTTP 状态码,如 200、404 等。
headerobject响应头。
cookiesarray响应中的 cookies(仅 H5 平台支持)。

使用示例

uni.request({
url: 'https://example.com/api/data', // 请求地址
method: 'GET', // 请求方法,默认为 GET
data: { // 请求参数
key1: 'value1',
key2: 'value2'
},
header: { // 请求头
'Content-Type': 'application/json'
},
success: (res) => { // 请求成功回调
console.log('请求成功:', res.data);
},
fail: (err) => { // 请求失败回调
console.error('请求失败:', err);
},
complete: () => { // 请求完成回调(无论成功或失败)
console.log('请求完成');
}
});

2. Promise#

Promise 用来表示一个尚未完成的异步操作,后续结果通过 thencatchawait 处理

三种状态:

  • Pending: 初始状态,表示异步操作尚未完成。
  • Fulfilled: 操作成功完成。
  • Rejected: 操作失败。

在 uni-app 中,可以用 Promise 包装网络请求:

function request(url, method = 'GET', data = {}) {
return new Promise((resolve, reject) => {
uni.request({
url,
method,
data,
success: (res) => resolve(res.data),
fail: (err) => reject(err)
});
});
}
// 使用
request('https://example.com/api/data')
.then(data => console.log('请求成功:', data))
.catch(err => console.error('请求失败:', err));

使用 async/await 简化 Promise

async/await 建立在 Promise 之上。相比连续回调,它更容易看清请求与异常处理的先后顺序。

使用示例:

async function get() {
try {
const res = await uni.request({
url: "https://jsonplaceholder.typicode.com/posts"
});
console.log(res);
} catch (e) {
console.log(e)
}
}
get()

5. 图片选择、预览与保存#

图片相关 API 看起来不难,真正容易踩坑的是路径。uni.chooseImage 返回的是临时路径,只保证在本次应用运行期间可用;保存到应用沙箱、上传服务器和写入系统相册是三件不同的事。

微信小程序从基础库 2.21.0 开始不再维护 wx.chooseImage,新项目应根据目标平台评估 uni.chooseMedia

5.1 选择图片#

下面限制用户只选择一张压缩图片。成功后得到的 tempFilePaths 是路径数组,即使 count 为 1 也要取第一项。

function chooseOneImage() {
uni.chooseImage({
count: 1,
sizeType: ['compressed'],
sourceType: ['album', 'camera'],
success: (result) => {
const filePath = result.tempFilePaths[0];
console.log('临时图片路径:', filePath);
},
fail: (error) => {
console.error('选择图片失败:', error);
}
});
}

用户取消选择也可能进入 fail 回调,界面上不必一律提示“操作失败”。我一般先记录 errMsg,再根据目标平台区分主动取消和权限问题。

5.2 预览图片#

uni.previewImage 接收一个 URL 数组,current 表示打开预览时先显示哪一张。路径可以来自本地临时文件,也可以是网络地址。

function previewImages(urls, current = 0) {
uni.previewImage({
urls,
current,
loop: true,
fail: (error) => {
console.error('预览图片失败:', error);
}
});
}

不同平台对指示器、长按菜单和循环预览的支持并不完全一致。依赖这些选项前,应在实际发布的平台上测试,而不是只看 H5 模拟结果。

5.3 保存到系统相册#

uni.saveImageToPhotosAlbum 接受临时文件路径或持久文件路径,不支持直接传入网络地址。选择图片后,可以把返回的临时路径直接交给它。

function chooseAndSaveImage() {
uni.chooseImage({
count: 1,
success: (chooseResult) => {
const filePath = chooseResult.tempFilePaths[0];
uni.saveImageToPhotosAlbum({
filePath,
success: () => {
uni.showToast({
title: '已保存到相册',
icon: 'success'
});
},
fail: (error) => {
console.error('保存图片失败:', error);
uni.showToast({
title: '保存失败',
icon: 'none'
});
}
});
}
});
}

网络图片需要先下载,下载成功后再保存临时文件:

function saveRemoteImage(url) {
uni.downloadFile({
url,
success: (downloadResult) => {
if (downloadResult.statusCode !== 200) {
uni.showToast({
title: '图片下载失败',
icon: 'none'
});
return;
}
uni.saveImageToPhotosAlbum({
filePath: downloadResult.tempFilePath,
success: () => {
uni.showToast({
title: '已保存到相册',
icon: 'success'
});
},
fail: (error) => {
console.error('保存图片失败:', error);
}
});
},
fail: (error) => {
console.error('下载图片失败:', error);
}
});
}

这个 API 在 H5 端不可用,因为浏览器不允许网页直接写入系统相册。App 和小程序还会受到相册权限、隐私协议与平台审核规则的限制,实际项目需要在 fail 回调里给出对应提示。

uni.saveFile 也带一个“保存”,但它保存到应用沙箱,用户不会在系统相册里看到文件。需要下次启动继续读取临时文件时用它,需要让用户在相册中找到图片时才用 uni.saveImageToPhotosAlbum

6. 参考资料#

uni-app快速上手
https://mj3622.github.io/posts/学习笔记/前端/uni-app/
作者
Minjer
发布于
2024-12-29
许可协议
CC BY-NC-SA 4.0