第一次用UniApp同时对接微信和支付宝小程序登录时,我盯着控制台里时而出现时而消失的用户信息百思不得其解——同样的代码在微信能跑,到支付宝就报错。经过三个项目的迭代验证,终于总结出这套真正可落地的跨平台登录方案。本文将带你从零构建完整的登录模块,包含这些关键知识点:
在开始编码前,需要确保开发环境正确配置。打开HBuilderX创建新项目时,选择"uni-app"模板,在manifest.json中勾选微信小程序和支付宝小程序支持。关键配置项如下:
json复制// manifest.json 部分配置
"mp-weixin": {
"appid": "你的微信小程序ID",
"requiredPrivateInfos": ["getUserInfo"]
},
"mp-alipay": {
"appid": "你的支付宝小程序ID",
"requestAli": {
"scopes": ["auth_base"]
}
}
提示:支付宝小程序需要额外在开放平台申请"获取会员信息"功能权限,否则调用API时会返回"无权限"错误
安装必要依赖:
bash复制npm install @escook/request-miniprogram -S # 推荐使用封装好的请求库
npm install js-base64 -S # 用于解码加密数据
微信的授权体系分为三个层级:
| 授权级别 | 获取内容 | 是否需要弹窗 | 对应API |
|---|---|---|---|
| 静默授权 | code/openid | 否 | uni.login |
| 用户信息 | 昵称/头像 | 是 | getUserProfile |
| 手机号 | 加密手机号 | 是 | getPhoneNumber |
典型代码结构:
javascript复制// 微信端登录逻辑
async function wechatLogin() {
const [loginErr, loginRes] = await uni.login()
if (loginErr) return handleError(loginErr)
const [userErr, userRes] = await uni.getUserProfile({
desc: '用于完善会员资料'
})
return {
code: loginRes.code,
userInfo: userRes.userInfo
}
}
支付宝的授权方式截然不同:
javascript复制// 支付宝专用授权组件
<button
open-type="getAuthorize"
scope="userInfo"
@getAuthorize="handleAuthSuccess"
@error="handleAuthError">
授权登录
</button>
// 授权成功后获取信息
function handleAuthSuccess() {
my.getUserInfo({
success: (res) => {
console.log('用户信息:', res)
}
})
}
关键差异点:
auth_base(静默)和auth_user(显式)两种scope采用工厂模式封装平台差异:
javascript复制// loginFactory.js
export default {
createLoginHandler(platform) {
switch(platform) {
case 'weixin':
return new WechatLogin()
case 'alipay':
return new AlipayLogin()
default:
throw new Error('不支持的平台')
}
}
}
class WechatLogin {
async login() { /* 微信实现 */ }
}
class AlipayLogin {
async login() { /* 支付宝实现 */ }
}
UniApp的条件编译是处理平台差异的利器:
javascript复制// 统一登录入口
function unifiedLogin() {
// #ifdef MP-WEIXIN
return wechatLogin()
// #endif
// #ifdef MP-ALIPAY
return alipayLogin()
// #endif
}
推荐的文件组织方式:
code复制/src
/modules
login
wechat.js
alipay.js
index.js # 统一出口
完整的登录时序图:
code/authCodeopenidjavascript复制// 安全存储方案
const storage = {
setToken(token) {
uni.setStorageSync('auth_token', token)
uni.setStorageSync('token_expire', Date.now() + 7200000)
},
checkValid() {
return uni.getStorageSync('token_expire') > Date.now()
}
}
后端接口需要包含这些安全措施:
建议的HTTP拦截器配置:
javascript复制// request拦截器示例
uni.addInterceptor('request', {
invoke(args) {
if (needAuth(args.url) && !storage.checkValid()) {
return reLogin().then(() => uni.request(args))
}
args.header.Authorization = `Bearer ${getToken()}`
}
})
推荐使用Vuex维护全局状态:
javascript复制// store/modules/user.js
export default {
state: {
userInfo: null,
isLoggedIn: false
},
mutations: {
updateUser(state, payload) {
state.userInfo = payload
state.isLoggedIn = !!payload
}
},
actions: {
async login({ commit }) {
const userData = await unifiedLogin()
commit('updateUser', userData)
}
}
}
完善的错误处理流程:
javascript复制async function handleLogin() {
try {
await this.$store.dispatch('user/login')
} catch (error) {
const errorMap = {
1001: '微信服务不可用',
1002: '用户取消授权',
1003: '支付宝环境异常'
}
uni.showToast({
title: errorMap[error.code] || '登录失败',
icon: 'none'
})
if (error.code === 1002) {
this.showAlternativeLogin = true
}
}
}
合理利用本地缓存提升体验:
javascript复制// 用户信息缓存方案
function getUserInfo() {
const cacheKey = 'cached_user_info'
const cached = uni.getStorageSync(cacheKey)
if (cached) return Promise.resolve(cached)
return fetchUserInfo().then(res => {
uni.setStorage({ key: cacheKey, data: res })
return res
})
}
当主要登录方式不可用时:
javascript复制// 备用登录通道
function fallbackLogin() {
return new Promise((resolve) => {
uni.showModal({
title: '授权失败',
content: '是否使用手机号快捷登录?',
success(res) {
if (res.confirm) {
startPhoneLogin()
}
}
})
})
}
常见问题处理指南:
| 问题现象 | 可能原因 | 解决方案 |
|---|---|---|
| 微信返回"微信用户" | 未调用getUserProfile | 使用按钮触发授权 |
| 支付宝报无效授权 | scopes配置错误 | 检查manifest配置 |
| 获取不到openid | code已过期 | 重新获取code |
| 跨域问题 | 域名未配置 | 检查小程序后台配置 |
调试技巧:
javascript复制// 开启详细日志
uni.setEnableDebug({
enableDebug: true
})
// 查看当前运行环境
console.log('运行平台:', uni.getSystemInfoSync().platform)
记得在项目根目录创建debug.log文件实时查看完整日志。最近在电商项目中遇到一个典型案例:支付宝在iOS设备上偶尔会丢失授权状态,最终发现是页面跳转时序问题,通过添加setTimeout延迟100ms解决。