uniapp 基础(三)
目录
1.uniapp 如何实现表单验证?
内置组件
自定义函数
第三方验证库
实时表单验证
结合UI组件库
后端验证的必要性
2.不同平台的导航栏差异
导航栏基础配置
条件编译
JS控制导航栏
复杂导航栏处理
微信小程序胶囊按钮适配
自定义导航栏组件
状态栏高度适配
注意事项
3.小程序的自定义 tabBar
配置 manifest.json 文件
创建自定义 tabBar 组件
实现 tabBar 逻辑
同步页面路径与选中状态
处理页面生命周期
4.uniapp 状态管理 方法
全局变量
Vuex 状态管理
Pinia 替代方案
本地存储与持久化
Vuex 推荐场景
5.跨平台分享功能
平台差异处理
自定义分享按钮
分享到更多平台
注意事项
6.小程序图片上传
uni.chooseImage选择图片
上传图片到服务器
多图上传处理
图片压缩与预览
注意事项
7.UniApp使用 WebSocket
WebSocket 基础配置
监听 WebSocket 事件
发送消息
关闭 WebSocket 连接
断线重连机制
注意事项
示例
8.UniApp 如何支持 PWA
配置 manifest.json
修改项目配置
生成 Service Worker
构建项目
9.UniApp 中如何处理用户授权
检查权限状态
请求权限
处理拒绝授权后的引导
获取用户位置
兼容多端差异
注意事项
10. UniApp 的插件机制,如何集成一个地图插件
UniApp 插件机制概述
集成地图插件的步骤
选择地图插件
配置插件
调用插件功能
注意事项
1.uniapp 如何实现表单验证?
内置组件
uniapp的<form>
组件和<input>
组件支持基础的表单验证。可以设置required
属性进行非空验证,或通过type
属性限制输入类型(如email
、number
)。
<form @submit="handleSubmit"><input v-model="formData.username" required placeholder="请输入用户名" /><input v-model="formData.password" type="password" required placeholder="请输入密码" /><button form-type="submit">提交</button>
</form>
自定义函数
通过JavaScript编写验证逻辑,在提交表单时触发验证函数。例如验证手机号格式:
第三方验证库
async-validator
可以简化复杂验证规则的实现
import Schema from 'async-validator';const rules = {username: { type: 'string', required: true, message: '用户名不能为空' },email: { type: 'email', message: '邮箱格式不正确' }
};const validator = new Schema(rules);methods: {validateForm() {validator.validate(this.formData, (errors) => {if (errors) {uni.showToast({ title: errors[0].message, icon: 'none' });} else {// 提交表单}});}
}
实时表单验证
通过监听输入变化实现实时验证,提升用户体验
<input v-model="formData.email" @input="validateEmail" placeholder="邮箱" />
methods: {validateEmail() {const reg = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;if (!reg.test(this.formData.email)) {this.errorMsg = '邮箱格式不正确';} else {this.errorMsg = '';}}
}
结合UI组件库
使用uView
或uni-ui
等组件库提供的表单验证功能,可以快速实现带错误提示的验证。以uView
为例:
<u-form :model="formData" :rules="rules" ref="uForm"><u-form-item label="用户名" prop="username"><u-input v-model="formData.username" /></u-form-item>
</u-form>
data() {return {rules: {username: [{ required: true, message: '请输入用户名', trigger: 'blur' }]}};
},
methods: {submit() {this.$refs.uForm.validate(valid => {if (valid) {// 验证通过}});}
}
后端验证的必要性
前端验证无法替代后端验证。在提交数据到服务器后,必须进行后端验证以确保数据安全性。前端验证仅用于提升用户体验和减少无效请求。
2.不同平台的导航栏差异
导航栏基础配置
在
pages.json
中配置全局导航栏样式,通过globalStyle
统一基础属性,例如背景色、文字颜色。针对特定页面,在style
节点中覆盖全局配置。
"globalStyle": {"navigationBarTextStyle": "black","navigationBarTitleText": "默认标题","navigationBarBackgroundColor": "#FFFFFF"
},
"pages": [{"path": "page/index","style": {"navigationBarTitleText": "自定义标题"}
}]
条件编译
在
pages.json
或组件中,通过#ifdef
和#endif
区分不同平台代码块。
"style": {"navigationBarTitleText": "通用标题","#ifdef H5": {"navigationStyle": "custom" // H5隐藏默认导航栏},"#ifdef APP-PLUS": {"titleNView": { // APP专用配置"buttons": [{"text": "按钮","fontSize": "16px"}]}}
}
JS控制导航栏
通过
uni.setNavigationBarTitle
或uni.setNavigationBarColor
动态修改导航栏属性。注意:部分API在小程序不兼容
// 修改标题
uni.setNavigationBarTitle({title: '新标题'
});// 修改背景色(仅支持HEX)
uni.setNavigationBarColor({frontColor: '#000000',backgroundColor: '#FF0000'
});
复杂导航栏处理
在
titleNView
配置复杂导航栏,支持按钮、搜索框等组件。需在pages.json
中声明:
"titleNView": {"searchInput": {"align": "center","backgroundColor": "#F5F5F5","borderRadius": "4px"},"buttons": [{"text": "\ue534","fontSrc": "/static/iconfont.ttf","fontSize": "22px"}]
}
微信小程序胶囊按钮适配
通过
uni.getMenuButtonBoundingClientRect()
获取胶囊按钮位置信息,动态计算偏移量
const menuInfo = uni.getMenuButtonBoundingClientRect();
console.log(menuInfo.width, menuInfo.top); // 示例:自定义导航栏高度计算
const statusBarHeight = uni.getSystemInfoSync().statusBarHeight;
const navHeight = menuInfo.top + menuInfo.height + 8;
自定义导航栏组件
创建跨平台导航栏组件,通过
slot
接收内容,内部处理平台差异:
<template><view class="custom-nav"><!-- 安卓/iOS返回按钮 --><view v-if="showBack" @click="handleBack"><text class="icon-back"></text></view><slot></slot></view>
</template><script>
export default {computed: {showBack() {return uni.getSystemInfoSync().platform !== 'h5';}},methods: {handleBack() {uni.navigateBack();}}
}
</script>
状态栏高度适配
使用
uni.getSystemInfoSync()
获取状态栏高度,确保内容不被遮挡:
const systemInfo = uni.getSystemInfoSync();
const statusBarHeight = systemInfo.statusBarHeight || 0;
const navBarHeight = systemInfo.platform === 'android' ? 48 : 44;
const totalHeight = statusBarHeight + navBarHeight;
注意事项
- 微信小程序自定义导航栏需在
pages.json
设置"navigationStyle": "custom"
- APP端动态修改导航栏按钮需使用
uni.setNavigationBarButton
- H5端隐藏导航栏可能影响浏览器原生返回功能
- 颜色值需使用6位HEX格式(如
#RRGGBB
),不支持缩写或RGB
3.小程序的自定义 tabBar
配置 manifest.json 文件
在项目的 manifest.json
文件中,明确声明需要使用自定义 tabBar。找到对应的小程序配置项(如微信小程序),添加以下配置:
"mp-weixin": {"tabBar": {"custom": true}
}
创建自定义 tabBar 组件
在项目根目录下创建 custom-tab-bar
文件夹,内部结构需符合小程序规范。通常包含以下文件:
index.vue
(或index
目录下的组件文件)- 相关样式和逻辑文件
实现 tabBar 逻辑
在 custom-tab-bar/index.vue
中,通过 Vue 语法实现交互逻辑。例如:
<template><div class="custom-tab-bar"><div v-for="(item, index) in list" :key="index" @click="switchTab(index)">{{ item.text }}</div></div>
</template><script>
export default {data() {return {list: [{ text: "首页", pagePath: "/pages/index/index" },{ text: "分类", pagePath: "/pages/category/index" }]};},methods: {switchTab(index) {uni.switchTab({ url: this.list[index].pagePath });}}
};
</script>
同步页面路径与选中状态
在 pages.json
中配置原生 tabBar 的路径(仅作参考,实际渲染由自定义组件控制):
"tabBar": {"list": [{ "pagePath": "pages/index/index" },{ "pagePath": "pages/category/index" }]
}
处理页面生命周期
在页面 onShow
生命周期中更新 tabBar 选中状态:
onShow() {if (typeof this.getTabBar === 'function' && this.getTabBar()) {this.getTabBar().setData({ selected: 0 }); // 根据实际索引调整}
}
4.uniapp 状态管理 方法
全局变量
在 App.vue
中定义全局变量,通过 getApp().globalData
访问。适合简单数据共享,但不支持响应式更新。
// App.vue
export default {globalData: { count: 0 }
}
// 页面中使用
const app = getApp()
console.log(app.globalData.count)
Vuex 状态管理
Vuex 是 Vue 生态的集中式状态管理工具,适合复杂应用。UniApp 支持 Vuex,需在项目根目录创建 store
文件夹并配置。
// store/index.js
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)export default new Vuex.Store({state: { count: 0 },mutations: { increment(state) { state.count++ } }
})
// 页面中使用
import { mapState, mapMutations } from 'vuex'
export default {computed: { ...mapState(['count']) },methods: { ...mapMutations(['increment']) }
}
Pinia 替代方案
Pinia 是 Vuex 的轻量级替代,支持 Composition API,TypeScript 友好。UniApp 可通过插件引入。
// stores/counter.js
import { defineStore } from 'pinia'
export const useCounterStore = defineStore('counter', {state: () => ({ count: 0 }),actions: { increment() { this.count++ } }
})
// 页面中使用
import { useCounterStore } from '@/stores/counter'
export default {setup() {const counter = useCounterStore()return { counter }}
}
本地存储与持久化
结合 uni.setStorageSync
实现状态持久化,适合需长期保存的数据。
// 存储状态
uni.setStorageSync('userToken', 'abc123')
// 读取状态
const token = uni.getStorageSync('userToken')
Vuex 推荐场景
- 推荐使用:跨多个页面共享复杂状态,需严格的同步更新机制。
- 不推荐场景:简单应用或仅需父子组件通信时,优先使用 Props/Emit 或全局变量。
Vuex 会增加项目复杂度,建议根据实际需求评估。对于小型项目,Pinia 或全局变量可能更高效。
5.跨平台分享功能
UniApp提供了统一的API,支持微信、QQ、微博等主流社交平台的分享功能。
使用uni.share
API
UniApp内置的uni.share
API可以轻松实现多平台分享。该API支持微信、QQ、新浪微博等平台。
uni.share({provider: "weixin", // 分享服务提供商,可选值:weixin、qq、sinaweibo等scene: "WXSceneSession", // 分享到聊天界面(WXSceneSession)或朋友圈(WXSceneTimeline)type: 0, // 分享类型,0为图文,1为纯文字,2为图片,5为小程序title: "分享标题",summary: "分享内容摘要",href: "https://example.com", // 分享链接imageUrl: "https://example.com/image.png", // 分享图片success: function(res) {console.log("分享成功");},fail: function(err) {console.log("分享失败", err);}
});
平台差异处理
不同平台对分享的支持程度不同,需根据平台特性调整参数。例如,微信支持分享到朋友圈和聊天界面,而QQ仅支持分享到聊天界面。
// 判断平台并设置不同参数
let provider = "";
let scene = "";// #ifdef MP-WEIXIN
provider = "weixin";
scene = "WXSceneSession"; // 或 WXSceneTimeline
// #endif// #ifdef MP-QQ
provider = "qq";
// #endifuni.share({provider: provider,scene: scene,// 其他参数
});
自定义分享按钮
某些平台(如微信小程序)要求分享必须通过按钮触发,不能直接调用API。需要在页面中添加<button>
组件并设置open-type="share"
。
<button open-type="share" type="warn">点击分享</button>
在页面的onShareAppMessage
生命周期中定义分享内容:
onShareAppMessage() {return {title: "自定义分享标题",path: "/pages/index/index",imageUrl: "/static/logo.png"};
}
分享到更多平台
如果需要分享到更多平台(如微博、支付宝等),可以通过条件编译或第三方插件扩展功能。
// #ifdef APP-PLUS
// 使用原生扩展或第三方SDK
const share = uni.requireNativePlugin("ShareModule");
share.shareToWeibo({text: "分享到微博",image: "/static/logo.png"
});
// #endif
注意事项
- 分享功能在某些平台(如微信)需要配置合法域名。
- 部分平台要求应用审核通过后才能使用分享功能。
- 图片链接需为HTTPS协议,且某些平台对图片大小有限制。
6.小程序图片上传
uni.chooseImage选择图片
调用uni.chooseImage
接口选择本地图片,支持多选和压缩。
uni.chooseImage({count: 3, // 最多选择3张sizeType: ['compressed'], // 压缩图success: (res) => {const tempFilePaths = res.tempFilePaths // 临时文件路径数组}
})
上传图片到服务器
获取临时路径后,通过uni.uploadFile
上传。需注意小程序端需配置合法域名:
uni.uploadFile({url: 'https://example.com/upload',filePath: tempFilePaths[0],name: 'file',formData: { user: 'test' },success: (uploadRes) => {console.log(uploadRes.data);}
})
多图上传处理
递归上传方法 采用递归方式依次上传多张图片,确保顺序执行:
function uploadFiles(files, index = 0) {if (index >= files.length) returnuni.uploadFile({url: 'https://example.com/upload',filePath: files[index],success: () => {uploadFiles(files, index + 1)}})
}
图片压缩与预览
使用条件编译处理平台差异 通过#ifdef
区分平台,H5端可用canvas压缩:
// #ifdef H5
compressImage(path) {// H5端压缩逻辑
}
// #endif
预览大图接口 调用uni.previewImage
实现预览功能:
uni.previewImage({current: 0, // 当前显示索引urls: imageList // 图片URL数组
})
注意事项
域名白名单配置 小程序需在后台配置request
和uploadFile
的合法域名,开发阶段可勾选不校验域名。
临时路径有效期 小程序端获取的临时路径仅在本次启动有效,不可直接存储。需上传成功后保存服务器返回的永久URL。
类型限制示例 通过extension
参数限制文件类型:
uni.chooseImage({extension: ['jpg', 'png'] // 只允许选择jpg/png
})
7.UniApp使用 WebSocket
WebSocket 基础配置
在 UniApp 中使用 WebSocket 需要通过 uni.connectSocket
方法建立连接。
const socketTask = uni.connectSocket({url: 'wss://your-websocket-server.com',success: () => {console.log('WebSocket 连接成功');},fail: (err) => {console.error('WebSocket 连接失败', err);}
});
监听 WebSocket 事件
WebSocket 支持多个事件监听,包括连接打开、接收消息、错误和关闭事件。通过 uni.onSocketOpen
、uni.onSocketMessage
等方法实现:
uni.onSocketOpen((res) => {console.log('WebSocket 连接已打开');
});uni.onSocketMessage((res) => {console.log('收到服务器内容:', res.data);
});uni.onSocketError((err) => {console.error('WebSocket 错误:', err);
});uni.onSocketClose((res) => {console.log('WebSocket 连接已关闭');
});
发送消息
通过 uni.sendSocketMessage
方法向服务器发送消息:
uni.sendSocketMessage({data: JSON.stringify({ message: 'Hello WebSocket' }),success: () => {console.log('消息发送成功');},fail: (err) => {console.error('消息发送失败', err);}
});
关闭 WebSocket 连接
通过 uni.closeSocket
方法主动关闭连接:
uni.closeSocket({success: () => {console.log('WebSocket 连接已关闭');}
});
断线重连机制
为实现稳定性,可以加入断线重连的逻辑。以下是一个简单的实现:
let reconnectAttempts = 0;
const maxReconnectAttempts = 5;function connectWebSocket() {const socketTask = uni.connectSocket({url: 'wss://your-websocket-server.com',success: () => {reconnectAttempts = 0;},fail: () => {if (reconnectAttempts < maxReconnectAttempts) {setTimeout(connectWebSocket, 3000);reconnectAttempts++;}}});
}uni.onSocketClose(() => {if (reconnectAttempts < maxReconnectAttempts) {setTimeout(connectWebSocket, 3000);reconnectAttempts++;}
});
注意事项
- 协议支持:确保服务器支持 WebSocket 协议(
ws://
或wss://
)。 - 多平台兼容性:部分小程序平台对 WebSocket 的实现可能有差异,需测试目标平台。
- 心跳机制:长时间连接建议加入心跳包防止超时断开。
- 全局管理:建议将 WebSocket 逻辑封装为单独模块以便复用。
示例
class WebSocketManager {constructor(url) {this.url = url;this.socketTask = null;this.reconnectAttempts = 0;this.maxReconnectAttempts = 5;}connect() {this.socketTask = uni.connectSocket({url: this.url,success: () => {this.reconnectAttempts = 0;},fail: () => this.handleReconnect()});this.setupEventListeners();}setupEventListeners() {uni.onSocketOpen(() => {console.log('WebSocket connected');});uni.onSocketMessage((res) => {console.log('Received:', res.data);});uni.onSocketClose(() => {this.handleReconnect();});}handleReconnect() {if (this.reconnectAttempts < this.maxReconnectAttempts) {setTimeout(() => this.connect(), 3000);this.reconnectAttempts++;}}send(data) {uni.sendSocketMessage({data: JSON.stringify(data)});}close() {uni.closeSocket();}
}// 使用示例
const ws = new WebSocketManager('wss://your-websocket-server.com');
ws.connect();
8.UniApp 如何支持 PWA
UniApp 通过特定的配置和插件可以将应用打包为 PWA(Progressive Web App),
配置 manifest.json
在项目根目录创建 manifest.json
文件,内容参考以下示例:
{"name": "My PWA App","short_name": "PWA App","start_url": ".","display": "standalone","background_color": "#ffffff","theme_color": "#4DBA87","icons": [{"src": "static/icons/icon-192x192.png","sizes": "192x192","type": "image/png"},{"src": "static/icons/icon-512x512.png","sizes": "512x512","type": "image/png"}]
}
修改项目配置
在 manifest.json
同目录下创建 vue.config.js
,配置 PWA 插件:
module.exports = {pwa: {name: 'My PWA App',themeColor: '#4DBA87',msTileColor: '#000000',appleMobileWebAppCapable: 'yes',appleMobileWebAppStatusBarStyle: 'black',manifestPath: 'manifest.json',workboxPluginMode: 'GenerateSW',workboxOptions: {skipWaiting: true,clientsClaim: true}}
}
生成 Service Worker
UniApp 使用 Workbox 生成 Service Worker。确保项目中已安装 workbox-webpack-plugin
:
npm install workbox-webpack-plugin --save-dev
构建项目
运行以下命令生成 PWA 所需的文件:
npm run build
构建完成后,dist
目录会包含 sw.js
和 manifest.json
等 PWA 必需文件。
部署注意事项
- 必须通过 HTTPS 协议访问 PWA 应用。
- 确保服务器正确配置
Content-Type
为application/json
处理manifest.json
。 - 测试 PWA 功能可使用 Chrome 的 Lighthouse 工具。
9.UniApp 中如何处理用户授权
检查权限状态
在 UniApp 中,可以使用 uni.getSetting
检查用户是否已授权某项权限。该方法返回用户的授权状态,可通过 authSetting
对象判断具体权限是否开启。
uni.getSetting({success(res) {const hasLocationPermission = res.authSetting['scope.userLocation']if (hasLocationPermission) {console.log('用户已授权位置权限')} else {console.log('用户未授权位置权限')}}
})
请求权限
如果用户未授权,通过 uni.authorize
发起权限请求。需在 manifest.json
中声明所需权限(如 "requiredPrivateInfos": ["getLocation"]
)。
uni.authorize({scope: 'scope.userLocation',success() {console.log('授权成功')},fail() {console.log('授权失败')}
})
处理拒绝授权后的引导
用户可能首次拒绝授权,需通过 uni.openSetting
引导用户手动开启权限
uni.showModal({title: '提示',content: '需要位置权限才能继续使用,是否去设置开启?',success(res) {if (res.confirm) {uni.openSetting({success(res) {console.log('用户已跳转至设置页')}})}}
})
获取用户位置
授权成功后,调用 uni.getLocation
获取具体位置信息。需注意高精度模式(isHighAccuracy
)可能增加耗电量。
uni.getLocation({type: 'wgs84',success(res) {console.log('经度:', res.longitude, '纬度:', res.latitude)},fail(err) {console.error('获取位置失败:', err)}
})
兼容多端差异
不同平台(微信小程序、H5、App)的权限机制可能不同:
- 微信小程序:需通过
button
组件的open-type="openSetting"
触发跳转设置页。 - H5:依赖浏览器 API(如
navigator.geolocation
),需处理 HTTPS 环境限制。 - App:需在原生配置文件中声明权限(如 Android 的
AndroidManifest.xml
)。
注意事项
- 频繁调用授权弹窗可能被平台限制,建议合理设计触发逻辑。
- 部分平台(如 iOS)要求提供权限使用目的描述,需在配置文件中补充隐私说明。
- 用户拒绝后,部分平台(如微信小程序)需等待 24 小时才能再次触发授权弹窗。
10. UniApp 的插件机制,如何集成一个地图插件
UniApp 插件机制概述
UniApp 的插件机制允许开发者扩展原生功能,通过集成原生插件实现跨平台能力(如调用设备硬件、地图、支付等)。插件分为两种类型:
- 原生插件:基于 Android/iOS 原生代码开发,需通过 UniApp 的插件市场或手动集成。
- JS 插件:纯前端实现,基于 JavaScript 的通用功能扩展。
插件通过 uni.requireNativePlugin
或直接引入 JS 模块的方式调用。
集成地图插件的步骤
选择地图插件
UniApp 官方推荐的高德地图或腾讯地图插件,可通过插件市场搜索并下载。以高德地图为例:
配置插件
-
引入插件包
下载插件后,将插件包(如amap-uni-plugin
)放入项目根目录的nativeplugins
文件夹。 -
配置 manifest.json
在manifest.json
的App原生插件配置
中添加插件:"app-plus": {"plugins": {"amap": {"version": "x.x.x", // 插件版本"provider": "dcloud.amap"}} }
-
配置平台密钥
在高德开放平台申请 AppKey,并填入manifest.json
:"app-plus": {"distribute": {"android": {"amap": {"appkey": "你的高德Android Key"}},"ios": {"amap": {"appkey": "你的高德iOS Key"}}} }
调用插件功能
在页面中通过原生插件 API 调用地图功能:
const amap = uni.requireNativePlugin('amap');
amap.init(() => {console.log('地图初始化成功');
});
// 打开地图页面
amap.openMap({latitude: 39.90469,longitude: 116.40717,name: '北京市'
});
注意事项
- 平台差异:Android 和 iOS 需分别配置密钥和权限。
- 调试:真机调试时需确保原生插件已正确打包到安装包中。
- 更新插件:插件版本需与 UniApp SDK 兼容,定期检查更新。