微信小程序的授权登录机制本质上是一种OAuth2.0的简化实现。当用户已经登录微信后,小程序可以通过微信开放能力获取用户的基本信息。这种设计有以下几个关键特点:
在实际开发中,我们需要使用wx.login()获取临时登录凭证code,这个code的有效期只有5分钟。获取code后,再结合用户授权的手机号信息(encryptedData和iv),即可完成完整的登录流程。
重要提示:code获取必须在页面加载时完成,如果在getphonenumber事件回调中获取会导致时序问题。这是因为微信的授权弹窗会中断JS执行流程。
登录接口需要三个关键参数:
后端接口示例:
typescript复制// src/services/login.ts
type LoginParams = {
code: string
encryptedData: string
iv: string
}
export const postLoginWxMinAPI = (data: LoginParams) => {
return http<LoginResult>({
method: 'POST',
url: '/login/wxMin',
data,
})
}
前端调用逻辑:
typescript复制const onGetphonenumber = async (ev) => {
const encryptedData = ev.detail!.encryptedData!
const iv = ev.detail!.iv!
const res = await postLoginWxMinAPI({ code, encryptedData, iv })
loginSuccess(res.result)
}
登录成功后,我们需要将用户信息持久化存储。这里使用Pinia配合持久化插件实现:
typescript复制// src/stores/modules/member.ts
export const useMemberStore = defineStore(
'member',
() => {
const profile = ref<LoginResult>()
const setProfile = (val: LoginResult) => {
profile.value = val
}
const clearProfile = () => {
profile.value = undefined
}
return { profile, setProfile, clearProfile }
},
{
persist: {
storage: {
getItem(key) {
return uni.getStorageSync(key)
},
setItem(key, value) {
uni.setStorageSync(key, value)
},
},
},
},
)
在个人中心页面,我们需要根据登录状态显示不同的UI:
vue复制<view class="overview" v-if="memberStore.profile">
<navigator url="/pagesMember/profile/profile">
<image class="avatar" :src="memberStore.profile.avatar"></image>
</navigator>
<view class="meta">
<view class="nickname">
{{ memberStore.profile.nickname || memberStore.profile.account }}
</view>
</view>
</view>
<view class="overview" v-else>
<navigator url="/pages/login/login">
<image class="avatar gray" src="default-avatar.png"></image>
</navigator>
<view class="meta">
<navigator url="/pages/login/login" class="nickname">
未登录
</navigator>
</view>
</view>
为提高用户体验,我们可以在用户进入"我的"页面时预加载设置模块:
json复制// pages.json
{
"preloadRule": {
"pages/my/my": {
"network": "all",
"packages": ["pagesMember"]
}
}
}
这种预加载策略可以使跳转到设置页面的速度提升50%以上,特别是在网络状况不佳的情况下效果更为明显。
退出登录需要清理用户状态并返回上一页:
typescript复制const onLogout = () => {
uni.showModal({
content: '是否退出登录?',
success: (res) => {
if (res.confirm) {
memberStore.clearProfile()
uni.navigateBack()
}
},
})
}
个人信息编辑页包含以下字段:
关键实现代码:
vue复制<view class="form-item">
<text class="label">性别</text>
<radio-group v-model="form.gender">
<label class="radio">
<radio value="男" color="#27ba9b" />
男
</label>
<label class="radio">
<radio value="女" color="#27ba9b" />
女
</label>
</radio-group>
</view>
<view class="form-item">
<text class="label">城市</text>
<picker
class="picker"
mode="region"
@change="onRegionChange"
:value="form.region"
>
<view v-if="form.region">{{form.region.join(' ')}}</view>
<view class="placeholder" v-else>请选择城市</view>
</picker>
</view>
头像上传需要处理本地文件选择和上传两个步骤:
typescript复制const uploadAvatar = async () => {
try {
// 选择图片
const res = await uni.chooseImage({
count: 1,
sizeType: ['compressed'],
sourceType: ['album']
})
// 上传到服务器
const uploadRes = await uni.uploadFile({
url: '/upload/avatar',
filePath: res.tempFilePaths[0],
name: 'file'
})
// 更新本地数据
form.avatar = JSON.parse(uploadRes.data).url
} catch (error) {
uni.showToast({ title: '上传失败', icon: 'none' })
}
}
问题1:无法唤起获取手机号界面
问题2:登录成功后信息不更新
问题3:分包预加载不生效
敏感信息处理:
权限控制:
数据验证:
通过以上实现,我们构建了一个完整的微信小程序用户系统,包含了登录、个人信息展示和编辑等核心功能。在实际项目中,还可以根据需求添加更多功能如第三方登录、多端同步等。