uni-app
是一个使用 Vue.js 开发所有前端应用的框架,开发者编写一套代码,
可发布到iOS、Android、Web(响应式)、以及各种小程序
(微信/支付宝/百度/头条/飞书/QQ/快手/钉钉/淘宝)、快应用等多个平台
官网链接:https://uniapp.dcloud.net.cn/
安装
使用 HBuilderX 编辑器进行安装
官网下载地址:https://www.dcloud.io/hbuilderx.html
HBuilderX 内置相关环境,开箱即用,无需配置nodejs;
H是HTML的首字母,Builder是构造者,X是HBuilder的下一代版本。
简称HX
。 HX
是轻如编辑器、强如IDE的合体版本
教程
B站相关视频教程 – 详细:https://www.bilibili.com/video/BV1mT411K7nW
B站相关视频教程 – 入门:https://www.bilibili.com/video/BV1BJ411W7pX
新建项目目录
HBuilderX 可以很迅速的创建 uniAPP 项目的基本结构。通过 文件 -> 新建 -> 项目 创建对应的 uniAPP项目
选择默认模板,等待 HBuilderX 配置 uniAPP 项目结构。选择对应模板
结构介绍
项目创建后,自动生成默认文件目录结构,各自负责对应的功能
开发规范
为了实现多端兼容,综合考虑编译速度、运行性能等因素,uni-app 约定了如下开发规范:
- 页面文件遵循 Vue单文件组件 (SFC) 规范
- 组件标签靠近小程序规范
- 接口能力 (JS API) 靠近微信小程序规范,但需将前缀 wx 替换为 uni
- 数据绑定及时间处理同 Vue.js 规范,同时补充了 APP 及页面的生命周期
- 为兼容多端运行,建议使用 flex 布局进行开发
需要提前安装 微信小程序工具,并在微信小程序工具的安全设置中打开端口
通过绑定微信小程序的安装目录,即可在 运行 中选择微信小程序的封装操作
手机开启开发者模式并开启USB调试。并在 HBuilderX 程序中下载真机调试插件即可选择连接操作
连接成功后,即可在真机状态下打包安卓APP并安装在手机上进行调试
该内容可查阅官方参考文档:https://uniapp.dcloud.net.cn/collocation/pages.html#globalstyle
介绍
在根目录的 pages.json 文件中,globalStyle属性 用于设置应用的状态栏、导航条、标题、窗口背景色等
参数介绍
属性 | 类型 | 默认值 | 描述 | 平台差异说明 |
---|---|---|---|---|
navigationBarBackgroundColor | HexColor | #F7F7F7 | 导航栏背景颜色(同状态栏背景色) | APP与H5为#F7F7F7,小程序平台请参考相应小程序文档 |
navigationBarTextStyle | String | white | 导航栏标题颜色及状态栏前景颜色,仅支持 black/white | 支付宝小程序不支持,请使用 my.setNavigationBar |
navigationBarTitleText | String | 导航栏标题文字内容 | ||
navigationStyle | String | default | 导航栏样式,仅支持 default/custom。custom即取消默认的原生导航栏,需看使用注意 | 微信小程序 7.0+、百度小程序、H5、App(2.0.3+) |
backgroundColor | HexColor | #ffffff | 下拉显示出来的窗口的背景色 | 微信小程序 |
backgroundTextStyle | String | dark | 下拉 loading 的样式,仅支持 dark / light | 微信小程序 |
enablePullDownRefresh | Boolean | false | 是否开启下拉刷新,详见页面生命周期。 | |
onReachBottomDistance | Number | 50 | 页面上拉触底事件触发时距页面底部距离,单位只支持px,详见页面生命周期 | |
backgroundColorTop | HexColor | #ffffff | 顶部窗口的背景色(bounce回弹区域) | 仅 iOS 平台 |
backgroundColorBottom | HexColor | #ffffff | 底部窗口的背景色(bounce回弹区域) | 仅 iOS 平台 |
titleImage | String | 导航栏图片地址(替换当前文字标题),支付宝小程序内必须使用https的图片链接地址 | 支付宝小程序、H5、APP | |
transparentTitle | String | none | 导航栏整体(前景、背景)透明设置。支持 always 一直透明 / auto 滑动自适应 / none 不透明 | 支付宝小程序、H5、APP |
titlePenetrate | String | NO | 导航栏点击穿透 | 支付宝小程序、H5 |
pageOrientation | String | portrait | 横屏配置,屏幕旋转设置,仅支持 auto / portrait / landscape 详见 响应显示区域变化 | App 2.4.7+、微信小程序、QQ小程序 |
animationType | String | pop-in | 窗口显示的动画效果,详见:窗口动画 | App |
animationDuration | Number | 300 | 窗口显示动画的持续时间,单位为 ms | App |
app-plus | Object | 设置编译到 App 平台的特定样式,配置项参考下方 app-plus | App | |
h5 | Object | 设置编译到 H5 平台的特定样式,配置项参考下方 H5 | H5 | |
mp-alipay | Object | 设置编译到 mp-alipay 平台的特定样式,配置项参考下方 MP-ALIPAY | 支付宝小程序 | |
mp-weixin | Object | 设置编译到 mp-weixin 平台的特定样式 | 微信小程序 | |
mp-baidu | Object | 设置编译到 mp-baidu 平台的特定样式 | 百度小程序 | |
mp-toutiao | Object | 设置编译到 mp-toutiao 平台的特定样式 | 字节跳动小程序 | |
mp-lark | Object | 设置编译到 mp-lark 平台的特定样式 | 飞书小程序 | |
mp-qq | Object | 设置编译到 mp-qq 平台的特定样式 | QQ小程序 | |
mp-kuaishou | Object | 设置编译到 mp-kuaishou 平台的特定样式 | 快手小程序 | |
mp-jd | Object | 设置编译到 mp-jd 平台的特定样式 | 京东小程序 | |
usingComponents | Object | 引用小程序组件,参考 小程序组件 | ||
renderingMode | String | 同层渲染,webrtc(实时音视频) 无法正常时尝试配置 seperated 强制关掉同层 | 微信小程序 | |
leftWindow | Boolean | true | 当存在 leftWindow 时,默认是否显示 leftWindow | H5 |
topWindow | Boolean | true | 当存在 topWindow 时,默认是否显示 topWindow | H5 |
rightWindow | Boolean | true | 当存在 rightWindow 时,默认是否显示 rightWindow | H5 |
rpxCalcMaxDeviceWidth | Number | 960 | rpx 计算所支持的最大设备宽度,单位 px | App(vue2 且不含 nvue)、H5(2.8.12+) |
rpxCalcBaseDeviceWidth | Number | 375 | rpx 计算使用的基准设备宽度,设备实际宽度超出 rpx 计算所支持的最大设备宽度时将按基准宽度计算,单位 px | App(vue2 且不含 nvue)、H5(2.8.12+) |
rpxCalcIncludeWidth | Number | 750 | rpx 计算特殊处理的值,始终按实际的设备宽度计算,单位 rpx | App(vue2 且不含 nvue)、H5(2.8.12+) |
dynamicRpx | Boolean | false | 动态 rpx,屏幕大小变化会重新渲染 rpx | App-nvue(vue3 固定值为 true) 3.2.13+ |
maxWidth | Number | 单位px,当浏览器可见区域宽度大于maxWidth时,两侧留白,当小于等于maxWidth时,页面铺满;不同页面支持配置不同的maxWidth;maxWidth = leftWindow(可选)+page(页面主体)+rightWindow(可选) | H5(2.9.9+) |
案例演示
{
"pages": [
// pages数组中第一项表示应用启动页,
// 参考 https://uniapp.dcloud.io/collocation/pages
{
"path": "pages/index/index"
}
],
"globalStyle": {
// 导航栏标题颜色 白色
"navigationBarTextStyle": "white",
// 导航栏标题文字内容 初号机
"navigationBarTitleText": "初号机",
// 导航栏背景色(同状态栏背景色) #66ccff
"navigationBarBackgroundColor": "#66CCFF",
// 下拉框背景色 #66cc
"backgroundColor": "#66CC",
// 是否开启下拉刷新 是
"enablePullDownRefresh": true,
// 下拉加载样式 亮色
"backgroundTextStyle":"light"
},
"uniIdRouter": {}
}
效果展示
该内容可查阅官方参考文档:https://uniapp.dcloud.net.cn/collocation/pages.html#style
介绍
pages.json 的 pages 属性可用于设置每个页面的状态栏、导航条、标题、窗口背景色等,
该配置项会覆盖 globalStyle 中相同的配置项
参数介绍
属性 | 类型 | 默认值 | 描述 | 平台差异说明 |
---|---|---|---|---|
navigationBarBackgroundColor | HexColor | #000000 | 导航栏背景颜色(同状态栏背景色),如”#000000″ | |
navigationBarTextStyle | String | white | 导航栏标题颜色及状态栏前景颜色,仅支持 black/white | |
navigationBarTitleText | String | 导航栏标题文字内容 | ||
navigationBarShadow | Object | 导航栏阴影,配置参考下方 导航栏阴影 | ||
navigationStyle | String | default | 导航栏样式,仅支持 default/custom。custom即取消默认的原生导航栏,需看使用注意 | 微信小程序 7.0+、百度小程序、H5、App(2.0.3+) |
disableScroll | Boolean | false | 设置为 true 则页面整体不能上下滚动(bounce效果),只在页面配置中有效,在globalStyle中设置无效 | 微信小程序(iOS)、百度小程序(iOS) |
backgroundColor | HexColor | #ffffff | 窗口的背景色 | 微信小程序、百度小程序、字节跳动小程序、飞书小程序、京东小程序 |
backgroundTextStyle | String | dark | 下拉 loading 的样式,仅支持 dark/light | |
enablePullDownRefresh | Boolean | false | 是否开启下拉刷新,详见页面生命周期。 | |
onReachBottomDistance | Number | 50 | 页面上拉触底事件触发时距页面底部距离,单位只支持px,详见页面生命周期 | |
backgroundColorTop | HexColor | #ffffff | 顶部窗口的背景色(bounce回弹区域) | 仅 iOS 平台 |
backgroundColorBottom | HexColor | #ffffff | 底部窗口的背景色(bounce回弹区域) | 仅 iOS 平台 |
disableSwipeBack | Boolean | false | 是否禁用滑动返回 | App-iOS(3.4.0+) |
titleImage | String | 导航栏图片地址(替换当前文字标题),支付宝小程序内必须使用https的图片链接地址 | 支付宝小程序、H5、App | |
transparentTitle | String | none | 导航栏透明设置。支持 always 一直透明 / auto 滑动自适应 / none 不透明 | 支付宝小程序、H5、APP |
titlePenetrate | String | NO | 导航栏点击穿透 | 支付宝小程序、H5 |
app-plus | Object | 设置编译到 App 平台的特定样式,配置项参考下方 app-plus | App | |
h5 | Object | 设置编译到 H5 平台的特定样式,配置项参考下方 H5 | H5 | |
mp-alipay | Object | 设置编译到 mp-alipay 平台的特定样式,配置项参考下方 MP-ALIPAY | 支付宝小程序 | |
mp-weixin | Object | 设置编译到 mp-weixin 平台的特定样式 | 微信小程序 | |
mp-baidu | Object | 设置编译到 mp-baidu 平台的特定样式 | 百度小程序 | |
mp-toutiao | Object | 设置编译到 mp-toutiao 平台的特定样式 | 字节跳动小程序 | |
mp-lark | Object | 设置编译到 mp-lark 平台的特定样式 | 飞书小程序 | |
mp-qq | Object | 设置编译到 mp-qq 平台的特定样式 | QQ小程序 | |
mp-kuaishou | Object | 设置编译到 mp-kuaishou 平台的特定样式 | 快手小程序 | |
mp-jd | Object | 设置编译到 mp-jd 平台的特定样式 | 京东小程序 | |
usingComponents | Object | 引用小程序组件,参考 小程序组件 | App、微信小程序、支付宝小程序、百度小程序、京东小程序 | |
leftWindow | Boolean | true | 当存在 leftWindow时,当前页面是否显示 leftWindow | H5 |
topWindow | Boolean | true | 当存在 topWindow 时,当前页面是否显示 topWindow | H5 |
rightWindow | Boolean | true | 当存在 rightWindow时,当前页面是否显示 rightWindow | H5 |
maxWidth | Number | 单位px,当浏览器可见区域宽度大于maxWidth时,两侧留白,当小于等于maxWidth时,页面铺满;不同页面支持配置不同的maxWidth;maxWidth = leftWindow(可选)+page(页面主体)+rightWindow(可选) | H5(2.9.9+) |
案例演示
1. 创建子组件 建议使用 view 标签,而不是 div ,因为有兼容问题
<template>
<view>
Hello-uni
</view>
</template>
<script>
export default {}
</script>
<style>
</style>
2. pages 中使用 path 引用 子组件路径,并使用 style 声明组件样式
【注意组件放置的顺序,第一项为应用启动项】
{
"pages": [
//pages数组中第一项表示应用启动页
{
"path": "pages/message/message",
"style": {
"navigationBarTitleText": "信息页",
"navigationBarBackgroundColor": "#007AFF"
}
},
{
"path": "pages/index/index"
}
],
"globalStyle": {
//...
},
"uniIdRouter": {}
}
效果展示
该内容可查阅官方参考文档:https://uniapp.dcloud.net.cn/collocation/pages.html#h5
介绍
配置编译到 H5 平台时的特定样式
参数介绍
属性 | 类型 | 描述 |
---|---|---|
titleNView | Object | 导航栏 |
pullToRefresh | Object | 下拉刷新 |
导航栏
属性 | 类型 | 默认值 | 描述 | 最低版本 |
---|---|---|---|---|
backgroundColor | String | #F7F7F7 | 背景颜色,颜色值格式为”#RRGGBB”。 | |
buttons | Array | 自定义按钮,参考 buttons | ||
titleColor | String | #000000 | 标题文字颜色 | |
titleText | String | 标题文字内容 | ||
titleSize | String | 标题文字字体大小 | ||
type | String | default | 导航栏样式。”default”-默认样式;”transparent”-透明渐变。 | |
searchInput | Object | 导航栏上的搜索框样式,详见:searchInput | 1.6.5 |
案例演示
{
"pages": [
{
"path": "pages/message/message",
"style": {
// ...
"h5": {
"pullToRefresh": {
"color": "red"
}
}
}
},
{
"path": "pages/index/index"
}
],
"globalStyle": {},
"uniIdRouter": {}
}
效果展示
由于 HbuilderX
默认创建的是 vue2
的框架,且不支持 TS
语法的提示。故应采用 vscode
开发并构建。
使用 npx 创建
npx degit dcloudio/uni-preset-vue#vite-ts 项目名称
# 如果下载失败 可以尝试清除缓存 和更新 npm 版本
npm cache clean --force
npm install -g npm@latest
效果展示
安装项目依赖包
cd 项目目录
npm install
运行项目
package.json 包中会根据参数运行编译指定平台的代码(如下图所示)
若要以运行微信小程序项目运行,例如可使用:npm run dev:wp-weixin
npm run dev:mp-weixin
运行后,微信开发者工具找到对应包文件位置 项目根目录 \dist\dev\mp-weixin
同步打开,即可同步调试
问题解决
部署完成后,tsconfig.json
文件会提示如下报错,修改配置项即可
Option 'importsNotUsedAsValues' is deprecated and will stop functioning in TypeScript 5.5. Specify compilerOption '"ignoreDeprecations": "5.0"' to silence this error.
Use 'verbatimModuleSyntax' instead.
可以在tsconfig.json文件中”compilerOptions”配置项内添加”ignoreDeprecations”: “5.0” 修复
"compilerOptions": {
"ignoreDeprecations": "5.0"
},
或者,使用该教程提供的策略: https://blog.csdn.net/anech/article/details/143733836
插件安装
要在 vscode
上开发 uniapp-vue3
项目,可以安装几款插件去辅助开发,提高效率
uni-create-view
上面这是一款可以右键新建目录并在 package.json
注册对应路由的包,
注意需要在插件扩展配置中将配置改为 vue3
uni-helper
uniapp 的专用配置项的代码提示工具
uniapp小程序扩展
为 uniapp 相关api提供文档及文档链接
安装类型声明文件
pnpm i -D @types/wechat-miniprogram @uni-helper/uni-app-types
配置 tsconfig.json
// tsconfig.json
{
"extends": "@vue/tsconfig/tsconfig.json",
"compilerOptions": {
"ignoreDeprecations": "5.0",
"sourceMap": true,
"baseUrl": ".",
"paths": {
"@/*": ["./src/*"]
},
"lib": ["esnext","dom"],
"types": [
"@dcloudio/types", // uni-app API 类型
"miniprogram-api-typings", // 原生微信小程序类型
"@uni-helper/uni-app-types" // uni-app 组件类型
]
},
// vue 编译器类型,校验标签类型
"vueCompilerOptions": {
"nativeTags": ["block","component","template","slot"],
},
"include": ["src/**/*.ts","src/**/*.d.ts","src/**/*.tsx","src/**/*.vue"]
}
另外后续设置其他操作,可以查看其他文档 ↓
相关文档来源:https://huaweicloud.csdn.net/654cad4b8c4ad05cd82aaf8d.html
实际上,uni-app 的大部分功能实现与 Vue 相似,其中夹杂了一些 微信小程序 的 api。这让我怎么写笔记…
写详细吧,这笔记就特别的冗余,写简单吧;好像笔记的效益不大
那不如直接让学 uniApp 的小朋友直接先学 微信小程序 和 Vue 可能更适合;
因此,我就写一些uniApp比较特别的内容吧
唯一遗憾大概就是没有二级路由的设定,做子级嵌套的内容切换会有点麻烦吧,或许有提供第三方包?
安全区域的顶部和底部间隔
当使用 uniapp
开发手机端的自定义导航栏时,为了适配刘海屏等机型。需要去做安全区域适配
使用 uni.getSystemInfoSync().safeAreaInsets
去获取屏幕边界到安全区域的位置,并设置导航栏与手机端顶部安全区域的合适间隔,实现适配
<script setup lang="ts">
// 获取屏幕边界到安全区域的位置
const { safeAreaInsets } = uni.getSystemInfoSync()
</script>
<template>
<view class="navbar" :style="{ paddingTop: safeAreaInsets?.top + 'px' }">
<!-- ... -->
</view>
</template>
动态设置 scroll-view 组件高度
如若需要组件设置占满高度,为了兼容所有机型高度,使用 uni.getSystemInfoSync()
获取设备高度
关于 uni.getSystemInfoSync()
相关文档
<script>
export default {
data() {
return {
wh: 0
};
},
// 页面加载完成后
onLoad() {
// 获取设备信息
const info = uni.getSystemInfoSync();
// 为当前 可用高度对应变量进行赋值
this.wh = info.windowHeight;
}
}
</script>
当需要全屏展示一组图片列表时,可以使用 uni.previewImage({current:string,urls:string[]})
方法
<script setup lang="ts">
const imageList = ["xxx.jpg","xxx.jpg","..."]
// 点击图片时业务
const onTapImage = (url: string) => {
uni.previewImage({
current: url,
urls: imageList
})
}
</script>
<template>
<swiper circular @change="onChange">
<swiper-item v-for="src in imageList" :key="src">
<image @tap="onTapImage(src)" mode="aspectFill" :src="src" />
</swiper-item>
</swiper>
</template>
效果展示
点击轮播图,打开大图预览
uniapp
的代码在编译在不同平台的时候,都会有一些差异的内容。详情和部分解决方案可以看 官方文档
小程序不支持 * 选择器
为避免在编译小程序项目时报错,应避免使用 *
选择器,而使用并集选择器去选择对应标签
解决不同平台定位时底部位置不同的问题
由于每个平台的视口底部都不是同一个位置,使用 uniapp
内置提供的 --windows-bottom
去差异获取每个平台的最底部位置
.n {
/* .. */
top: var(--windows-top);
bottom: var(--windows-bottom);
}
声明式跳转
在Uni-app中,可以使用声明式导航(Declarative Navigation)来实现页面的跳转。通过在模板中绑定特定的事件或属性,触发相应的导航操作;
下面是使用 <navigator>
标签进行页面跳转
<navigator url="/pages/about">跳转到关于页面</navigator>
编程式导航
通过绑定事件,并触发事件后使用 uni.navigateTo() 的方式跳转;
<button @click="redirectToAbout">跳转到关于页面</button>
methods: {
redirectToAbout() {
uni.navigateTo({
url: '/pages/about'
})
}
}
若是需要跳转到已经声明在 TabBar 的页面。为了同时切换底部导航栏选项。需要使用 uni.switchTab() 方式进行跳转。
uni.switchTab({
url: '/pages/tabbar/home'
})
关闭当前页跳转
使用 uni.redirectTo() 跳转时,会在 页面栈 中关闭当前页进行跳转。通常用于阻止用户表单多次提交使用
uniApp的事件监听写法与 vue 类似,详细请看 vue教程
相关资料来自 CSDN
- prevent:阻止默认行为
- stop:阻止事件冒泡
- capture:使用事件捕获模式
- self:只有当事件是从事件源本身触发时才触发回调函数 (不兼容 android端)
- once:只触发一次回调函数
- passive:提高页面滚动的流畅度
<!-- 阻止默认行为 -->
<button @click.prevent="handleClick">点击我</button>
<!-- 阻止事件冒泡 -->
<button @click.stop="handleClick">点击我</button>
<!-- 使用事件捕获模式 -->
<button @click.capture="handleClick">点击我</button>
<!-- 只有当事件是从事件源本身触发时才触发回调函数 -->
<button @click.self="handleClick">点击我</button>
<!-- 只触发一次回调函数 -->
<button @click.once="handleClick">点击我</button>
<!-- 提高页面滚动的流畅度 -->
<button @click.passive="handleClick">点击我</button>
HTML中 表单组件通常为 input 并选择 type 的类型来实现 复选框、单选框、下拉框;在这有差异
button 组件的内容比较丰富,可自行文档查询 相关文档
<button type="primary">按钮</button>
size 按钮尺寸
设置预设的按钮的大小样式
值 | 说明 |
---|---|
default | 默认大小 |
mini | 小尺寸 |
type 有效值
设置预设的按钮的外观样式
值 | 说明 |
---|---|
primary | 默认样式 |
default | 白色 |
warn | 红色 |
form-type 有效值
实现表单按钮事件
值 | 说明 |
---|---|
submit | 提交表单 |
reset | 重置表单 |
代码演示
checkbox 是通过由 checkbox-group 包裹 checkbox 实现复选框效果 可自行文档查询 相关文档
<template>
<checkbox-group>
<label>
<checkbox value="cb" checked="true" />选中
</label>
<label>
<checkbox value="cb" />未选中
</label>
</checkbox-group>
</template>
效果展示
checkbox-group 参数
属性名 | 类型 | 说明 |
---|---|---|
@change | EventHandle | <checkbox-group> 中选中项发生改变是触发 change 事件,detail = {value:[选中的checkbox的value的数组]} |
checkbox 参数
属性名 | 类型 | 默认值 | 说明 |
---|---|---|---|
value | String | <checkbox> 标识,选中时触发 <checkbox-group> 的 change 事件,并携带 <checkbox> 的 value。 | |
disabled | Boolean | false | 是否禁用 |
checked | Boolean | false | 当前是否选中,可用来设置默认选中 |
color | Color | checkbox的颜色,同css的color |
- checkbox的默认颜色,在不同平台不一样。微信小程序、360小程序是绿色的,字节跳动小程序为红色,
其他平台是蓝色的。更改颜色使用color属性。 - 如需调节 checkbox 大小,可通过 css 的 scale 方法调节,如缩小到70% style=”transform:scale(0.7)”
组件放置在 uniApp 的根目录的 component 文件夹内,在 HBuilder 软件中对 component 文件夹右键 新建组件,即可创建组件
创建组件后,引用的名称就为组件创建的文件夹的名称,可以驼峰命名,也可以用 – 命名
<smm-title></smm-title>
父级直接在 组件标签上 定义变量 并 传值,即可传到子级同名下的 props 对象中创建的变量中
<smm-lyricBar name='你好'></smm-lyricBar>
子级通过 props 接收对应参数
export default {
// 也可以用数组的简化方式 props:["name"]
props: {
name: {
// 限制传入的类型
type: String,
// 默认值
default: ""
}
}
}
子级组件通过 $emit 向外主动发起事件并传参,父级通过对组件绑定事件来触发事件处理函数并接收参数
<template>
<view @click="cilckFn"></view>
</template>
<script>
export default {
methods: {
cilckFn() {
this.$emit('click', 1);
}
}
}
</script>
父级通过给组件绑定对应事件,即可获取子组件发送的数据
<template>
<smm-title @click="cilckFn"></smm-title>
</template>
<script>
export default {
methods: {
cilckFn(e) {
console.log(e); // 1
}
}
}
</script>
【组件接收父级的参数实际上是单向数据流,因此即使子级改变了传入的参数;也不改变对父级的数据】
因此当我们想主动改变 props 对象中对应变量的值时,系统会抛出一个报错
为了简化 流程,父级界面通过对组件传参的变量使用 .sync 修饰符,这相当父级组件直接为该变量添加并绑定了
updata:变量名 的方法;并可以由组件的 this.$emit(‘updata:变量名’,新变量的值) 直接修改父级的变量
<template>
<smm-title :name.sync="str"></smm-title>
</template>
<script>
export default {
data() {
return {
str: 'hello'
}
}
}
</script>
子级组件通过 this.$emit(‘updata:xxx‘,xxx); 可直接修改对应的 props 值
<script>
export default {
props: {
name: {
type: String,
default: ''
}
},
methods: {
changeStr() {
// 父级通过 .sync 修饰的变量 可由子级通过此方式修改
this.$emit('updata:name', 'word');
}
}
}
</script>
【默认在组件上使用 click 等原生事件是无效的,一般是由组件绑定对应事件并由 $emit 向外提供】
例如:若是 需要触发组件的单击事件,旧方式是 组件需要绑定 cilck 并使用 $emit 暴露出去
<template>
<view @click="cilckFn"></view>
</template>
<script>
export default {
methods: {
cilckFn() {
this.$emit('click');
}
}
}
</script>
如若需要直接对事件触发原生事件的需求,父级界面可在对组件进行的 事件监听 加上 .native 修饰符
<template>
<!-- 直接对组件调用原生的 单击 事件 -->
<smm-title @click.native="cilckFn"></smm-title>
</template>
创建拦截器文件 (http.t
s / http.js
),并使用 uni.addInterceptor
设置请求拦截器、基地址等操作
const baseURL = 'http接口地址'
const httpInterceptor = {
// 拦截前触发
invoke(options: UniApp.RequestOptions) {
// 如果链接中没有地址
if (!options.url.startsWith('http')) {
options.url = baseURL + options.url
}
// 修改请求超时 默认60000毫秒
options.timeout = 10000
// 设置请求头
options.header = {
...options.header,
'source-client': 'app',
}
// ...
// 如有 token
if (token) {
// 添加 token
options.header.Authorization = token
}
},
}
uni.addInterceptor('request', httpInterceptor)
uni.addInterceptor('uploadFile', httpInterceptor)
由于 uniapp
的 uni.request
的拦截器并没有 axios
完善,响应拦截器需要自己封装 uni.request
函数代替
// 响应拦截器
export const http = (options) => {
return new Promise((resolve, reject) => {
uni.request({
...options,
// 响应成功
success(res) {
// 数据是否返回成功
if (res.statusCode >= 200 && res.statusCode < 300) {
resolve(res.data)
}
// token 是否失效
else if (res.statusCode === 401) {
// ...
// 失败回传
reject(res)
} else {
uni.showToast({
title: (res.data).msg || "请求错误",
icon: "none"
})
reject(res)
}
},
// 响应失败
fail(err) {
uni.showToast({
title: "网络错误,换个网络试试",
icon: "none"
})
reject(err)
}
})
})
}
在页面中引用该文件,即可实现拦截器效果
<script setup lang="ts">
import { http } from '@/utils/http'
// ...
http({
// ...
})
</script>
<template>
</template>
<style lang="scss">
</style>
uniapp 支持资源上传功能,默认使用 formData 的方式向服务器发起上传文件的请求。自带了如下选择工具:
- 图片选择器 uni.chooseImage()
- 视频选择器 uni.chooseVideo()
通过选择器选择本地的资源路径,再通过 uni.uploadFile() 方法将其上传到服务器
手机端如若需要使用该方法,请确认已授权软件读取文件的权限
使用 图片选择控件 uni.chooseImage 和 文件上传 uni.uploadFile 实现图片的上传功能
微信小程序从基础库 2.21.0 开始, wx.chooseImage 停止维护,请使用 uni.chooseMedia 代替
uni.chooseImage({
count: 1, // 最多可以选择的图片张数
sizeType: ['original', 'compressed'], // 可以指定是原图还是压缩图,默认二者都有
sourceType: ['album', 'camera'], // 可以指定来源是相册还是相机,默认二者都有
success: function (res) {
var tempFilePaths = res.tempFilePaths;
// 上传文件
uni.uploadFile({
url: 'your/upload/url',
filePath: tempFilePaths[0],
name: 'file',
formData: {
// 添加其他的 FormData 数据
key1: value1,
key2: value2,
// ...
},
success: function (res) {
console.log('上传成功', res);
uni.showToast({
title: '上传成功',
icon: 'success',
duration: 2000
});
},
fail: function (res) {
console.log('上传失败', res);
uni.showToast({
title: '上传失败',
icon: 'none',
duration: 2000
});
}
});
}
});
使用 图片选择控件 uni.choosVideo 和 文件上传 uni.uploadFile 实现图片的上传功能
uni.chooseVideo({
sourceType: ['album', 'camera'], // 可以指定来源是相册还是相机,默认二者都有
compressed: true, // 是否压缩视频,默认为 true
maxDuration: 60, // 拍摄最长拍摄时间,单位秒,默认为 60
camera: 'back', // 指定使用前置或后置摄像头,默认为后置摄像头
success: function (res) {
var tempFilePath = res.tempFilePath;
// 上传文件
uni.uploadFile({
url: 'your/upload/url',
filePath: tempFilePath,
name: 'file',
formData: {
// 添加其他的 FormData 数据
key1: value1,
key2: value2,
// ...
},
success: function (res) {
console.log('上传成功', res);
uni.showToast({
title: '上传成功',
icon: 'success',
duration: 2000
});
},
fail: function (res) {
console.log('上传失败', res);
uni.showToast({
title: '上传失败',
icon: 'none',
duration: 2000
});
}
});
}
});
除了 uni.chooseImage()
和 uni.chooseVideo()
方法,uniapp 还提供了其他选择方法,可以根据不同的需求选择合适的方法,例如:
- uni.chooseFile():选择文件,可以选择任意类型的文件,包括图片、视频、文档等。
- uni.chooseMedia():拍摄或从手机相册中选择图片或视频,选择视频或者图片
这些方法都有各自的参数和返回值,具体使用方法可以参考 uniapp 的官方文档。根据具体的需求,选择合适的方法来实现相应的功能
使用 uni.downloadFile() 方法 下载文件资源到本地,客户端直接发起一个 HTTP GET 请求,返回文件的本地临时路径
// 下载直链视频
uni.downloadFile({
url: 'https://example.com/my-video.mp4',
success: function (res) {
if (res.statusCode === 200) {
uni.showToast({
title: '视频下载成功',
icon: 'success'
});
}
},
fail: function (error) {
uni.showToast({
title: '视频下载失败',
icon: 'none'
});
}
});
通过该方法获取临时文件存放路径后,一般再通过 uni.saveFile 方法保存文件到本地
该方法不支持 H5,且 saveFile 会把临时文件移动,因此调用成功后传入的 tempFilePath 将不可用
uni.chooseImage({
success: function (res) {
var tempFilePaths = res.tempFilePaths;
uni.saveFile({
tempFilePath: tempFilePaths[0],
success: function (res) {
// 文件保存的所在目录
let savedFilePath = res.savedFilePath;
}
});
}
});
当然,如果是视频或者图片,一般使用的是 uni.saveImageToPhotosAlbum() 保存图片到相册 或 uni.saveVideoToPhotosAlbum() 保存视频到相册
通过 uni.saveImageToPhotosAlbum() 和 uni.saveVideoToPhotosAlbum() 保存视频或图片到相册
参数介绍
参数名 | 类型 | 必填 | 说明 |
---|---|---|---|
filePath | String | 是 | 视频文件路径,可以是临时文件路径也可以是永久文件路径 |
success | Function | 否 | 接口调用成功的回调函数 |
fail | Function | 否 | 接口调用失败的回调函数 |
complete | Function | 否 | 接口调用结束的回调函数(调用成功、失败都会执行) |
案例演示
// 下载并保存视频
uni.downloadFile({
url: 'https://example.com/my-video.mp4',
success: function (res) {
if (res.statusCode === 200) {
// 保存视频到相册
uni.saveVideoToPhotosAlbum({
filePath: res.tempFilePath,
success: function () {
uni.showToast({
title: '视频保存成功',
icon: 'success'
});
},
fail: function (error) {
uni.showToast({
title: '视频保存失败',
icon: 'none'
});
}
});
}
},
fail: function (error) {
uni.showToast({
title: '视频下载失败',
icon: 'none'
});
}
});
每个平台有自己的一些特性,即使是API在每个平台不一定都是支持的,因此会存在一些无法跨平台的情况
- 大量写 if else,会造成代码执行性能低下和管理混乱。
- 编译到不同的工程后二次修改,会让后续升级变的很麻烦
因此,推荐使用条件编译来对不同平台写特定的代码样式
使用语法介绍
条件编译是用特殊的注释作为标记,在编译时根据这些特殊的注释,将注释里面的代码编译到不同平台
**写法:**以 #ifdef 或 #ifndef 加 %PLATFORM% 开头,以 #endif 结尾。
- #ifdef:if defined 仅在某平台存在
- #ifndef:if not defined 除了某平台均存在
- %PLATFORM%:平台名称
条件编译写法 | 说明 |
---|---|
#ifdef APP-PLUS 需条件编译的代码 #endif | 仅出现在 App 平台下的代码 |
#ifndef H5 需条件编译的代码 #endif | 除了 H5 平台,其它平台均存在的代码 |
#ifdef H5 || MP-WEIXIN 需条件编译的代码 #endif | 在 H5 平台或微信小程序平台存在的代码(这里只有||,不可能出现&&,因为没有交集) |
Vuex 之前已经写了相关文档,若需要详细了解请直接查阅
uniapp 官方提供了 getApp().globalData.data 的方法去实现全局共享,但功能不多因此不加以介绍。详情请看文档说明:
说明
uniapp 内置了全局共享数据 vuex 模块,将极大简化并方便了项目的全局共享状态的模块开发
一般搭配本地存储,在 vuex 的操作方法若执行 uni.setStorageSync(); 即可实现永久存值
配置模块
在项目的根目录中新建 store 目录,创建 index.js 文件
import Vue from 'vue';
import Vuex from 'vuex';
Vue.use(Vuex);
const store = new Vuex.Store({
state: {
// 定义状态
sayhi:'hello'
}
},
mutations: {
// 定义方法
changeData(state, payload){
state.sayhi = payload;
}
}
})
export default store;
导入模块
在根目录下的 main.js 中,引用 vuex 并其配置的全局共享数据
import store from './store/index.js'
// #ifndef VUE3
import Vue from 'vue'
import App from './App'
Vue.prototype.$store = store;
Vue.config.productionTip = false
App.mpType = 'app'
const app = new Vue({
...App
})
app.$mount()
// #endif
// #ifdef VUE3
import { createSSRApp } from 'vue'
import App from './App.vue'
export function createApp() {
const app = createSSRApp(App)
return {
app
}
}
// #endif
获取状态
通过使用 vuex 提供的方法,或者直接使用 this.$store.state 来获取 vuex 中 state 存储的状态
<template>
<div>
<p>{{ count }}</p>
</div>
</template>
<script>
export default {
computed: {
count() {
return this.$store.state.sayhi
}
}
}
</script>
修改状态
使用 mapMutations
辅助函数将 vuex
中的 mutations 映射到当前组件的 methods
中,并在需要修改 state 值的地方调用该方法即可
<template>
<div>
<p>{{ count }}</p>
<button @click="increment">Increment</button>
</div>
</template>
<script>
import { mapMutations } from 'vuex'
export default {
computed: {
count() {
return this.$store.state.sayhi
}
},
methods: {
...mapMutations([
'changeData'
])
}
}
</script>
在组件的 methods
中定义需要修改 state 值的方法,并在方法中通过 this.$store.commit
调用对应的 mutation
methods: {
increment() {
this.$store.commit('changeData')
}
}
uniapp 支持多语言切换,其基础组件和API,内置如下语言:
- 中文简体
- 中文繁体
- 英语
- 法语
- 西班牙语
关于配置多语言,可参考 uniapp 官方文档
uni-app的国际化,即多语言,分为应用部分和框架部分
- 应用部分,即开发者自己的代码里涉及的界面部分
- 框架部分,即uni-app内置组件和API涉及界面的部分
框架部分会自动获取当前设备或软件配置的语言环境并进行切换,如若有除上述语中的多语言需求,请自行配置
在 locale 文件夹中新建 uni-app.对应地区语言代码.json ,并进行对应语种配置 相关教程
{
"common": {
"uni.app.quit": "再按一次退出应用",
"uni.async.error": "连接服务器超时,点击屏幕重试",
"uni.showActionSheet.cancel": "取消",
"uni.showToast.unpaired": "请注意 showToast 与 hideToast 必须配对使用",
"uni.showLoading.unpaired": "请注意 showLoading 与 hideLoading 必须配对使用",
"uni.showModal.cancel": "取消",
"uni.showModal.confirm": "确定",
"uni.chooseImage.cancel": "取消",
"uni.chooseImage.sourceType.album": "从相册选择",
"uni.chooseImage.sourceType.camera": "拍摄",
"uni.chooseVideo.cancel": "取消",
"uni.chooseVideo.sourceType.album": "从相册选择",
"uni.chooseVideo.sourceType.camera": "拍摄",
"uni.previewImage.cancel": "取消",
"uni.previewImage.button.save": "保存图像",
"uni.previewImage.save.success": "保存图像到相册成功",
"uni.previewImage.save.fail": "保存图像到相册失败",
"uni.setClipboardData.success": "内容已复制",
"uni.scanCode.title": "扫码",
"uni.scanCode.album": "相册",
"uni.scanCode.fail": "识别失败",
"uni.scanCode.flash.on": "轻触照亮",
"uni.scanCode.flash.off": "轻触关闭",
"uni.startSoterAuthentication.authContent": "指纹识别中...",
"uni.picker.done": "完成",
"uni.picker.cancel": "取消",
"uni.video.danmu": "弹幕",
"uni.video.volume": "音量",
"uni.button.feedback.title": "问题反馈",
"uni.button.feedback.send": "发送"
},
"ios": {},
"android": {}
}
{
"common": {
"uni.app.quit": "もう一度押すと、アプリケーションが終了します",
"uni.async.error": "サーバーへの接続がタイムアウトしました。画面をクリックして再試行してください",
"uni.showActionSheet.cancel": "キャンセル",
"uni.showToast.unpaired": "使用するには、showToastとhideToastをペアにする必要があることに注意してください",
"uni.showLoading.unpaired": "使用するには、showLoadingとhideLoadingをペアにする必要があることに注意してください",
"uni.showModal.cancel": "キャンセル",
"uni.showModal.confirm": "OK",
"uni.chooseImage.cancel": "キャンセル",
"uni.chooseImage.sourceType.album": "アルバムから選択",
"uni.chooseImage.sourceType.camera": "カメラ",
"uni.chooseVideo.cancel": "キャンセル",
"uni.chooseVideo.sourceType.album": "アルバムから選択",
"uni.chooseVideo.sourceType.camera": "カメラ",
"uni.previewImage.cancel": "キャンセル",
"uni.previewImage.button.save": "画像を保存",
"uni.previewImage.save.success": "画像をアルバムに正常に保存します",
"uni.previewImage.save.fail": "画像をアルバムに保存できませんでした",
"uni.setClipboardData.success": "コンテンツがコピーされました",
"uni.scanCode.title": "スキャンコード",
"uni.scanCode.album": "アルバム",
"uni.scanCode.fail": "認識に失敗しました",
"uni.scanCode.flash.on": "タッチして点灯",
"uni.scanCode.flash.off": "タップして閉じる",
"uni.startSoterAuthentication.authContent": "指紋認識...",
"uni.picker.done": "完了",
"uni.picker.cancel": "キャンセル",
"uni.video.danmu": "「弾幕」",
"uni.video.volume": "ボリューム",
"uni.button.feedback.title": "質問のフィードバック",
"uni.button.feedback.send": "送信"
},
"ios": {},
"android": {}
}
创建多语言 json 文件
建议在根目录中新建 locale 文件夹,在里面配置多语言模块
创建俩个 json 文件,对应不同语言环境下显示的内容
{
"login.login": "login",
"message.changeLang.title": "prompt",
"message.changeLang.content": "Do you want to switch languages?",
"tabBar.index": "index",
"tabBar.live": "live",
"tabBar.song": "song",
"tabBar.my": "my"
}
{
"login.login": "登录",
"message.changeLang.title": "提示",
"message.changeLang.content": "是否要切换语言?",
"tabBar.index": "首页",
"tabBar.live": "直播",
"tabBar.song": "歌曲",
"tabBar.my": "我的"
}
创建入口文件 并引入 json 文件
通过入口 index.js 文件,引入所有 文本数据 并向外暴露
import en from './en.json'
import zhHans from './zh-Hans.json'
export default {
en,
'zh-Hans': zhHans
}
在项目中挂载多语言模块
import messages from './locale/index'
let i18nConfig = {
locale: uni.getLocale(),
messages
}
// vue2 配置方案
// #ifndef VUE3
import Vue from 'vue'
import App from './App'
// 引入 VueI18n 库
import VueI18n from 'vue-i18n'
// 在 Vue 中使用 VueI18n
Vue.use(VueI18n)
// 创建一个 VueI18n 实例,传入 i18nConfig 配置
const i18n = new VueI18n(i18nConfig)
// ...
const app = new Vue({
i18n,
...App
})
app.$mount()
// #endif
// vue3 配置方案
// #ifdef VUE3
import { createSSRApp } from 'vue'
import { createI18n } from 'vue-i18n'
import App from './App.vue'
const i18n = createI18n(i18nConfig)
export function createApp() {
const app = createSSRApp(App)
app.use(i18n)
return {
app
}
}
// #endif
使用多语言模块
通过 uni.setLocale(lang) 并 that.$i18n.locale = lang; 进行当前语言的切换,注意this 应指向 Vue
在界面上 通过 {{ $t( ‘text’ ) }} 引入多语言文本
<template>
<view class="list-item">
<button>{{$t('login.login')}}</button>
</template>
在 js 中 通过 this.$t( ‘text’ ) 引入多语言文本
that = this
// ....
data() {
return {
langList: [{
text: '简体中文',
code: 'zh-Hans'
}, {
text: 'English',
code: 'en'
}],
nowLang: ''
};
},
methods: {
changeLanguage(c) {
const lang = c.code;
uni.showModal({
title: this.$t('message.changeLang.title'),
content: this.$t('message.changeLang.content')
success(e) {
if (e.confirm) {
// 设置当前语言环境
uni.setLocale(lang);
// 在当前 VueI18n 实例中切换语言
that.$i18n.locale = lang;
}
}
})
}
}
在 .json 文件中使用 %text% 引用多语言文本
"tabBar": {
"selectedColor": "rgba(249,94,0,1)",
"list": [
{
"text": "%tabBar.index%",
// ..
},
{
"text": "%tabBar.live%",
// ..
},
{
"text": "%tabBar.song%",
// ..
},
{
"text": "%tabBar.my%",
// ..
}
]
},
效果展示
调用第三方样式组件并实现自动导入 节约开发成本
uni-ui 是 uniapp
官方推出。使用安全放心,并且支持多端兼容开发
下载包
pnpm i @dcloudio/uni-ui
# uni-ui 使用了 sass
npm install sass --save-dev
配置 easycom
easycom
是 uniapp
特有的配置,在 pages.json
设置后就可以完成组件的自动导入
{
// 组件自动导入
"easycom": {
"autoscan": true,
"custom": {
// uni-ui 规则如下配置 // [!code ++]
"^uni-(.*)": "@dcloudio/uni-ui/lib/uni-$1/uni-$1.vue" // [!code ++]
}
},
"pages": [
// …省略
]
}
安装类型声明文件
使组件代码的调用有类型提示
pnpm i -D @uni-helper/uni-ui-types
配置类型声明文件
如有报错请将 vscode
的 Vue - Official
插件降级到 2.0.12
{
"compilerOptions": {
// ...
"types": [
"@dcloudio/types", // uni-app API 类型
"miniprogram-api-typings", // 原生微信小程序类型
"@uni-helper/uni-app-types", // uni-app 组件类型
"@uni-helper/uni-ui-types" // uni-ui 组件类型 // [!code ++]
]
},
// vue 编译器类型,校验标签类型
"vueCompilerOptions": {
"nativeTags": ["block", "component", "template", "slot"]
}
}
pinia
是 vue3
项目中使用的全局数据共享依赖
安装pinia和 持久化 插件
# 强制安装 pinia
# 如果安装出现报错比如说:Pinia 依赖的
# @vue/composition-api 要求 Vue 的版本在 “>= 2.5 < 2.7” 范围内,
# 但实际上你的项目使用的是 Vue 3。
# 对于@vue/composition-api是Vue2版本提供组合式API的插件,
# Vue3不需要所以碰到这个问题直接强制安装pinia
npm install pinia@2.2.0 --force
npm install pinia-plugin-persistedstate
还有一种方式是直接升级 vue
版本,再安装 pinia
npm install vue@^3.5.11
配置 Pinia
建议是配置完 pinia
相关内容后,再导入到 mian.ts
入口
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'
import { createSSRApp } from "vue";
import App from "./App.vue";
import pinia from "./stores";
export function createApp() {
const app = createSSRApp(App);
app.use(pinia);
return {
app,
};
}
创建 pinia
数据模块
为了方便每个模块独立管理,可以在创建的 src/stores
下新建 modules
文件夹,存放 pinia
数据
import { defineStore } from "pinia";
import { ref } from "vue";
type UserInfo = {
name: string
}
export const useUserInfoStore = defineStore('userInfo', () => {
const userInfo = ref<UserInfo>()
const setUserInfo = (val: UserInfo) => {
userInfo.value = val
}
const clearUserInfo = () => {
userInfo.value = undefined
}
return { userInfo, setUserInfo, clearUserInfo }
}, {
// 修正本地化存放的操作 为兼容多平台的存储策略
persist: {
storage: {
getItem(key) {
return uni.getStorageSync(key)
},
setItem(key, value) {
uni.setStorageSync(key, value)
},
},
}
})
pinia
的入口文件直接导入进来即可
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/user'
使用 pinia
数据
<script setup lang="ts">
import { ref } from 'vue'
import { useUserInfoStore } from '@/stores';
const userInfoStore = useUserInfoStore()
const login = () => {
userInfoStore.setUserInfo({ name: '屎猫猫' })
}
const clear = () => {
userInfoStore.clearUserInfo()
}
</script>
<template>
{{ userInfoStore.userInfo }}
<button @tap="login">登录</button>
<button @tap="clear">清除</button>
</template>
效果展示