12 KiB
12 KiB
UniApp + H5混合开发微信小程序 技术实施方案(UniApp端)
你希望通过UniApp开发微信小程序,以web-view嵌套H5承载核心业务展示,同时由UniApp原生层负责登录、退出、支付等核心能力,以下是UniApp端的详细技术实施方案,明确需要开发的核心模块、功能和关键逻辑。
一、整体架构与职责边界
核心分工
| 模块/能力 | UniApp(小程序端)负责 | H5端负责 |
|---|---|---|
| 用户身份 | 微信登录、token管理、用户信息存储、退出登录逻辑 | 接收并使用用户信息,触发退出登录指令 |
| 支付 | 调起微信支付、支付结果处理、支付状态同步 | 发起支付请求、展示支付结果 |
| 页面容器 | 提供tabBar框架、web-view容器、页面跳转控制 | 承载table栏、业务内容展示、内部页面跳转 |
| 通信 | 接收H5消息、主动向H5推送数据、处理通信异常 | 向UniApp发送指令、接收UniApp数据、展示状态反馈 |
二、UniApp端核心开发内容
1. 项目基础配置
(1)全局配置(pages.json)
配置tabBar、页面路由、web-view权限等核心配置:
{
"pages": [
// 登录页
"pages/login/login",
// tabBar主页面(嵌套web-view)
"pages/index/index",
// 其他tabBar页面(如需)
"pages/my/my"
],
"tabBar": {
"color": "#666666",
"selectedColor": "#007AFF",
"backgroundColor": "#ffffff",
"borderStyle": "black",
"list": [
{
"pagePath": "pages/index/index",
"text": "首页",
"iconPath": "static/tabbar/home.png",
"selectedIconPath": "static/tabbar/home-active.png"
},
{
"pagePath": "pages/my/my",
"text": "我的",
"iconPath": "static/tabbar/my.png",
"selectedIconPath": "static/tabbar/my-active.png"
}
]
},
"permission": {
"scope.userLocation": {
"desc": "你的位置信息将用于小程序定位"
}
},
"webview": {
"webviewParameter": {
"allowUniversalAccessFromFileURLs": true,
"allowFileAccess": true
}
}
}
(2)微信小程序配置(manifest.json)
开启微信小程序相关权限,配置AppID等:
{
"mp-weixin": {
"appid": "你的微信小程序AppID",
"setting": {
"urlCheck": false, // 允许访问非域名白名单的H5(开发阶段,正式需配置白名单)
"es6": true,
"postcss": true
},
"usingComponents": true,
"permission": {
"scope.userInfo": {
"desc": "获取你的用户信息用于登录"
}
}
}
}
2. 核心页面开发
(1)登录页(pages/login/login.vue)
负责微信授权登录、获取用户信息、token存储:
<template>
<view class="login-container">
<button open-type="getUserProfile" @click="wxLogin" type="primary">微信快捷登录</button>
</view>
</template>
<script>
export default {
methods: {
async wxLogin() {
try {
// 1. 获取微信登录code
const loginRes = await uni.login({ provider: 'weixin' });
if (loginRes.code) {
// 2. 调用后端接口,换取用户token和信息
const userRes = await uni.request({
url: '你的后端登录接口', // 如:https://api.xxx.com/wx/login
method: 'POST',
data: {
code: loginRes.code,
appid: '你的小程序AppID'
}
});
if (userRes.data.code === 200) {
// 3. 存储用户信息和token(全局可用)
const { token, userInfo } = userRes.data.data;
uni.setStorageSync('wx_token', token);
uni.setStorageSync('user_info', userInfo);
// 4. 登录成功跳转到首页
uni.switchTab({ url: '/pages/index/index' });
uni.showToast({ title: '登录成功', icon: 'success' });
} else {
uni.showToast({ title: userRes.data.msg, icon: 'none' });
}
}
} catch (err) {
uni.showToast({ title: '登录失败,请重试', icon: 'none' });
console.error('登录异常:', err);
}
}
}
};
</script>
<style scoped>
.login-container {
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
}
button {
width: 80%;
height: 80rpx;
font-size: 32rpx;
}
</style>
(2)tabBar首页(pages/index/index.vue)
核心容器页,嵌套web-view、处理与H5的通信:
<template>
<view class="webview-container">
<!-- web-view组件:嵌套H5页面,绑定通信事件 -->
<web-view
:src="h5Url"
@message="handleH5Message"
id="main-webview"
></web-view>
</view>
</template>
<script>
export default {
data() {
return {
h5Url: '你的H5网页地址', // 如:https://h5.xxx.com/home
userToken: '',
userInfo: {}
};
},
onShow() {
// 页面显示时校验登录态,无登录态则跳转登录页
this.checkLoginStatus();
// 拼接用户信息到H5链接(初始化传参)
this.initH5Url();
},
methods: {
// 1. 校验登录状态
checkLoginStatus() {
this.userToken = uni.getStorageSync('wx_token');
this.userInfo = uni.getStorageSync('user_info');
if (!this.userToken) {
uni.redirectTo({ url: '/pages/login/login' });
}
},
// 2. 初始化H5链接(携带用户信息)
initH5Url() {
if (this.userToken) {
// 拼接token、用户ID等关键信息到H5链接
this.h5Url = `${this.h5Url}?token=${this.userToken}&userId=${this.userInfo.id}&nickName=${encodeURIComponent(this.userInfo.nickName)}`;
}
},
// 3. 接收H5发送的消息(核心通信逻辑)
handleH5Message(e) {
// H5发送的消息格式建议:{ type: '指令类型', data: '指令参数' }
const [message] = e.detail.data;
if (!message) return;
switch (message.type) {
// H5触发支付
case 'triggerPay':
this.handlePay(message.data);
break;
// H5触发退出登录
case 'triggerLogout':
this.handleLogout();
break;
// H5请求重新获取用户信息
case 'getUserInfo':
this.sendUserInfoToH5();
break;
default:
console.log('未知指令类型:', message.type);
}
},
// 4. 处理支付逻辑(UniApp原生调起微信支付)
async handlePay(payParams) {
try {
// 4.1 调用后端获取微信支付参数(如prepay_id)
const payRes = await uni.request({
url: '你的后端支付接口', // 如:https://api.xxx.com/pay/create
method: 'POST',
data: {
token: this.userToken,
...payParams // H5传递的订单ID、金额等参数
}
});
if (payRes.data.code !== 200) {
uni.showToast({ title: payRes.data.msg, icon: 'none' });
// 通知H5支付失败
this.sendMsgToH5({ type: 'payFail', data: payRes.data.msg });
return;
}
// 4.2 调起微信支付
const wxPayParams = payRes.data.data;
uni.requestPayment({
timeStamp: wxPayParams.timeStamp,
nonceStr: wxPayParams.nonceStr,
package: wxPayParams.package,
signType: 'MD5',
paySign: wxPayParams.paySign,
// 支付成功回调
success: () => {
uni.showToast({ title: '支付成功', icon: 'success' });
this.sendMsgToH5({ type: 'paySuccess', data: '支付成功' });
},
// 支付失败/取消回调
fail: (err) => {
uni.showToast({ title: '支付失败', icon: 'none' });
this.sendMsgToH5({ type: 'payFail', data: err.errMsg });
}
});
} catch (err) {
console.error('支付异常:', err);
this.sendMsgToH5({ type: 'payFail', data: '支付接口异常' });
}
},
// 5. 处理退出登录
handleLogout() {
// 清除本地存储的登录态
uni.removeStorageSync('wx_token');
uni.removeStorageSync('user_info');
// 通知H5退出成功
this.sendMsgToH5({ type: 'logoutSuccess' });
// 跳转登录页
uni.redirectTo({ url: '/pages/login/login' });
},
// 6. 主动向H5发送消息
sendMsgToH5(msg) {
// 创建web-view上下文,指定ID(与页面中web-view的id一致)
const webViewCtx = uni.createWebViewContext('main-webview');
// 发送消息(H5需监听message事件接收)
webViewCtx.postMessage({ data: msg });
},
// 7. 主动向H5推送用户信息
sendUserInfoToH5() {
this.sendMsgToH5({
type: 'userInfo',
data: {
token: this.userToken,
userId: this.userInfo.id,
nickName: this.userInfo.nickName,
avatarUrl: this.userInfo.avatarUrl
}
});
}
}
};
</script>
<style scoped>
.webview-container {
width: 100%;
height: 100vh;
}
</style>
(3)“我的”页面(pages/my/my.vue)
可选,如需原生展示用户信息、退出按钮等:
<template>
<view class="my-container">
<view class="user-info">
<image :src="userInfo.avatarUrl" class="avatar"></image>
<text class="nickname">{{ userInfo.nickName }}</text>
</view>
<button @click="handleLogout" type="warn">退出登录</button>
</view>
</template>
<script>
export default {
data() {
return {
userInfo: {}
};
},
onShow() {
this.userInfo = uni.getStorageSync('user_info');
},
methods: {
handleLogout() {
uni.removeStorageSync('wx_token');
uni.removeStorageSync('user_info');
uni.redirectTo({ url: '/pages/login/login' });
}
}
};
</script>
<style scoped>
.my-container {
padding: 40rpx;
}
.user-info {
display: flex;
align-items: center;
margin-bottom: 60rpx;
}
.avatar {
width: 120rpx;
height: 120rpx;
border-radius: 50%;
margin-right: 30rpx;
}
.nickname {
font-size: 36rpx;
font-weight: bold;
}
button {
width: 100%;
height: 80rpx;
}
</style>
3. 全局工具类(可选)
封装通信、登录校验等通用方法,便于复用:
// common/utils.js
/**
* 向H5发送消息
* @param {String} webViewId web-view的ID
* @param {Object} msg 消息内容
*/
export const sendMsgToH5 = (webViewId, msg) => {
const webViewCtx = uni.createWebViewContext(webViewId);
webViewCtx.postMessage({ data: msg });
};
/**
* 校验登录态
* @returns {Boolean} 是否已登录
*/
export const checkLogin = () => {
const token = uni.getStorageSync('wx_token');
if (!token) {
uni.redirectTo({ url: '/pages/login/login' });
return false;
}
return true;
};
4. 关键注意事项
- 域名白名单配置:微信小程序后台需配置H5域名、后端接口域名的白名单,否则无法访问;
- 通信格式统一:UniApp与H5的通信消息需约定固定格式(如
{type: 'xxx', data: 'xxx'}),避免解析异常; - 支付权限:确保小程序已开通微信支付权限,后端支付接口需正确对接微信支付商户平台;
- 登录态同步:H5页面刷新时,需重新从UniApp获取登录态(可通过URL参数或postMessage);
- 兼容性:web-view组件在微信小程序中部分JS API有限制,需避免在H5中调用小程序专属API(如支付、登录)。
总结
UniApp端核心开发内容可归纳为3个关键点:
- 基础层:配置tabBar、页面路由、微信小程序权限,搭建整体框架;
- 核心能力层:开发登录页实现微信授权登录,在web-view容器页处理与H5的通信,封装支付、退出登录等核心逻辑;
- 保障层:统一通信格式、配置域名白名单、处理登录态同步,确保混合开发模式稳定运行。