基于UniApp与Vue3的持久化登录功能实现策略
AI 概述
需求分析核心实现思路1. 数据持久化存储2. 应用生命周期管理3. 自动跳转逻辑技术实现详解一、Store 状态管理(stores/user.ts)1.2 Token 管理方法1.3 用户信息管理1.4 恢复登录状态方法1.5 Store 初始化二、App.vue 应用级处理2.1 全局变量定义2.2 onLaunch 处理逻辑2.3 onShow 处理逻辑三、登录页兜...
目录
文章目录隐藏
在移动应用开发领域,用户体验始终占据着核心地位。当用户将应用切换至后台,后续再次启动应用时,若仍需重新执行登录操作,这必然会对用户体验造成显著的负面影响。本文将深入剖析在基于 UniApp 与 Vue3 框架的项目中,怎样达成持久化登录功能,以此保障用户从后台返回应用时,能够维持原有的登录状态。

需求分析
核心需求:
- 用户登录成功后,将登录信息持久化保存;
- 应用退出到后台后,重新打开时自动恢复登录状态;
- 已登录用户打开应用时,自动跳转到首页,而不是停留在登录页。
技术要点:
- 数据持久化存储(本地存储);
- uni-app 应用生命周期(onLaunch、onShow);
- 状态恢复和自动跳转逻辑。
核心实现思路
1. 数据持久化存储
使用 UniApp 的 uni.setStorageSync API 将登录相关信息保存到本地存储:
- Token:用户身份凭证;
- 用户信息:用户基本信息;
- 其他相关数据:如房屋信息、配置信息等。
2. 应用生命周期管理
利用 UniApp 的生命周期钩子实现自动恢复:
- onLaunch:应用首次启动时恢复状态。
- onShow:应用从后台恢复时恢复状态。
- onHide:应用进入后台时无需额外操作。
3. 自动跳转逻辑
检测到已登录且当前在登录页时,自动跳转到首页,并防止重复跳转。
技术实现详解
一、Store 状态管理(stores/user.ts)
import { defineStore } from "pinia";
import { ref } from "vue";
export const useUserStore = defineStore("user", () => {
const token = ref('')
const userInfo = ref<any>(null)
const verifiedHouse = ref<any>(null)
// ... 其他代码
})
1.2 Token 管理方法
// 从本地存储获取 token
const getToken = () => {
try {
const localToken = uni.getStorageSync('user_token')
if (localToken) {
token.value = localToken
}
return token.value
} catch (error) {
console.error('获取 token 失败:', error)
return ''
}
}
// 设置 token 并保存到本地存储
const setToken = (val: string) => {
token.value = val
try {
uni.setStorageSync('user_token', val)
} catch (error) {
console.error('保存 token 失败:', error)
}
}
// 清除 token 和本地存储
const clearToken = () => {
token.value = ''
try {
uni.removeStorageSync('user_token')
} catch (error) {
console.error('清除 token 失败:', error)
}
}
要点说明:
setToken()时同步保存到本地存储getToken()时优先从本地存储读取,确保数据持久化- 清除 token 时同时清除本地存储
1.3 用户信息管理
// 设置用户信息
const setUserInfo = (info: any) => {
userInfo.value = info;
try {
uni.setStorageSync('user_info', info);
} catch (error) {
console.error('保存用户信息失败:', error)
}
}
// 清除用户信息
const clearUserInfo = () => {
userInfo.value = null;
try {
uni.removeStorageSync('user_info');
} catch (error) {
console.error('清除用户信息失败:', error)
}
}
1.4 恢复登录状态方法
// 恢复登录状态(从本地存储恢复所有登录相关的信息)
const restoreLoginState = () => {
// 恢复 token
getToken()
// 恢复用户信息
try {
const localUserInfo = uni.getStorageSync('user_info')
if (localUserInfo) {
userInfo.value = localUserInfo
}
} catch (error) {
console.error('恢复用户信息失败:', error)
}
// 恢复房屋信息
try {
const localHouseInfo = uni.getStorageSync('verifiedHouse')
if (localHouseInfo) {
verifiedHouse.value = localHouseInfo
}
} catch (error) {
console.error('恢复信息失败:', error)
}
}
核心作用:
- 统一恢复所有登录相关的状态;
- 在应用启动和后台恢复时调用;
- 确保内存中的状态与本地存储同步。
1.5 Store 初始化
// 初始化时自动从本地存储恢复数据
getToken()
try {
const localUserInfo = uni.getStorageSync('user_info')
if (localUserInfo) {
userInfo.value = localUserInfo
}
} catch (error) {
console.error('获取用户信息失败:', error)
}
return {
token, getToken, setToken, clearToken,
userInfo, setUserInfo, clearUserInfo,
verifiedHouse, setHouseInfo, getHouseInfo, clearHouseInfo,
restoreLoginState
}
二、App.vue 应用级处理
2.1 全局变量定义
<script setup lang="ts">
import { onLaunch, onShow, onHide } from '@dcloudio/uni-app'
import { nextTick } from 'vue'
import { checkPermission } from '@/common/permission'
import { useUserStore } from '@/stores/user'
// 防止重复跳转的标志位
let isNavigating = false
// 记录是否是首次启动
let isFirstLaunch = true
设计说明:
isNavigating:防止并发跳转导致的路由冲突。isFirstLaunch:区分首次启动和后台恢复,避免重复处理。
2.2 onLaunch 处理逻辑
onLaunch(() => {
// 1. 恢复登录状态
const userStore = useUserStore()
userStore.restoreLoginState()
// 2. 延迟检查并跳转(确保页面已加载)
setTimeout(() => {
try {
const pages = getCurrentPages()
const currentPage = pages[pages.length - 1]
if (currentPage) {
const currentRoute = '/' + currentPage.route
const currentToken = userStore.getToken() || uni.getStorageSync('user_token')
// 如果已登录且当前在登录页,跳转到首页
if (currentToken && currentRoute === '/pages/login/index' && !isNavigating) {
isNavigating = true
// 使用 nextTick 确保在路由拦截器注册之后执行
nextTick(() => {
uni.reLaunch({
url: '/pages/Home/HomeIndex',
success: () => {
isNavigating = false
},
fail: () => {
isNavigating = false
}
})
})
}
}
} catch (error) {
// 静默处理错误
}
}, 300)
// 3. 添加路由拦截器
uni.addInterceptor('navigateTo', {
invoke(e: any) {
return checkPermission(e.url)
}
})
uni.addInterceptor('redirectTo', {
invoke(e: any) {
return checkPermission(e.url)
}
})
uni.addInterceptor('reLaunch', {
invoke(e: any) {
return checkPermission(e.url)
}
})
uni.addInterceptor('switchTab', {
invoke(e: any) {
return checkPermission(e.url)
}
})
})
关键点解析:
- 延迟检查:使用
setTimeout(300ms)确保页面栈已初始化完成。 - nextTick 使用:确保路由拦截器注册完成后再执行跳转。
- 条件判断:
- 检查是否有 token;
- 检查当前是否在登录页;
- 检查是否正在跳转中(
!isNavigating)。
- 标志位管理:跳转前后设置和重置
isNavigating。
2.3 onShow 处理逻辑
onShow(() => {
// 1. 恢复登录状态
const userStore = useUserStore()
userStore.restoreLoginState()
// 2. 首次启动时跳过(onLaunch 已处理)
if (isFirstLaunch) {
isFirstLaunch = false
return
}
// 3. 后台恢复时检查跳转
setTimeout(() => {
try {
const pages = getCurrentPages()
const currentPage = pages[pages.length - 1]
if (currentPage) {
const currentRoute = '/' + currentPage.route
const currentToken = userStore.getToken() || uni.getStorageSync('user_token')
// 如果已登录且当前在登录页,跳转到首页
if (currentToken && currentRoute === '/pages/login/index' && !isNavigating) {
isNavigating = true
uni.reLaunch({
url: '/pages/Home/HomeIndex',
success: () => {
isNavigating = false
},
fail: () => {
isNavigating = false
}
})
}
}
} catch (error) {
// 静默处理错误
}
}, 300)
})
onHide(() => {
// 应用进入后台时,token 已经保存在本地存储中,无需额外操作
})
设计说明:
- 首次启动时
onShow也会触发,但此时onLaunch已处理跳转,需要跳过 - 只在真正的后台恢复时(非首次启动)才执行跳转检查
- 使用相同的延迟和检查逻辑,保持一致性
三、登录页兜底处理(pages/login/index.vue)
onMounted(async () => {
const systemInfo = uni.getSystemInfoSync()
statusBarHeight.value = systemInfo.statusBarHeight
// 检查登录状态,如果已登录则跳转到首页
// 注意:App.vue 已经处理了自动跳转逻辑,这里只做兜底检查
const tokenFromStore = store.getToken()
const tokenFromStorage = uni.getStorageSync('user_token')
const token = tokenFromStore || tokenFromStorage
// 延迟检查,给 App.vue 的跳转逻辑一个执行机会
// 如果 App.vue 没有跳转,这里作为兜底
if (token) {
setTimeout(() => {
const pages = getCurrentPages()
const currentPage = pages[pages.length - 1]
if (currentPage) {
const currentRoute = '/' + currentPage.route
// 如果还在登录页,说明 App.vue 没有跳转,这里执行跳转
if (currentRoute === '/pages/login/index') {
uni.reLaunch({
url: '/pages/Home/HomeIndex'
})
return
}
}
}, 500)
return
}
// 未登录,继续登录流程
await initAliSDK()
})
设计说明:
- 延迟更长(500ms):给 App.vue 的跳转逻辑(300ms)足够的执行时间;
- 兜底机制:确保即使 App.vue 的跳转失败,登录页也能处理;
- 双重检查:从 Store 和本地存储两个来源获取 token,提高可靠性。
完整流程图
┌─────────────────────────────────────────────────────────┐
│ 用户登录成功 │
└──────────────────┬──────────────────────────────────────┘
│
↓
┌─────────────────────────────────────────────────────────┐
│ 保存 token 到本地存储 (uni.setStorageSync) │
│ - user_token: token │
│ - user_info: 用户信息 │
│ - verifiedHouse: 房屋信息 │
└──────────────────┬──────────────────────────────────────┘
│
↓
┌─────────────────────────────────────────────────────────┐
│ 用户退出应用到后台 │
└──────────────────┬──────────────────────────────────────┘
│
↓
┌─────────────────────────────────────────────────────────┐
│ 用户重新打开应用 │
└──────────────────┬──────────────────────────────────────┘
│
↓
┌──────────┴──────────┐
│ │
↓ ↓
┌──────────────┐ ┌──────────────┐
│ onLaunch │ │ onShow │
│ 触发 │ │ 触发 │
└──────┬───────┘ └──────┬────────┘
│ │
↓ ↓
┌──────────────┐ ┌──────────────┐
│restoreLogin │ │restoreLogin │
│State() │ │State() │
└──────┬───────┘ └──────┬────────┘
│ │
↓ ↓
┌──────────────┐ ┌──────────────┐
│ 延迟 300ms │ │首次启动? │
│ 检查跳转 │ │是 → 跳过 │
└──────┬───────┘ │否 → 延迟 300ms │
│ │ 检查跳转 │
↓ └──────┬────────┘
┌──────────────┐ │
│检查条件: │ ↓
│1. 有 token? │ ┌──────────────┐
│2. 在登录页? │ │检查条件: │
│3. 未在跳转? │ │1. 有 token? │
└──────┬───────┘ │2. 在登录页? │
│ │3. 未在跳转? │
↓ └──────┬───────┘
┌──────────────┐ │
│是 → 跳转首页 │ ↓
│否 → 保持现状 │ ┌──────────────┐
└──────────────┘ │是 → 跳转首页 │
│否 → 保持现状 │
└───────────────┘
│
↓
┌───────────────────────────┐
│ 登录页 onMounted (兜底) │
│ 延迟 500ms 检查 │
│ 仍在登录页?→ 执行跳转 │
└───────────────────────────┘
关键注意事项
1. 时序问题
问题: 页面栈可能在应用启动时还未完全初始化
解决方案:
- 使用
setTimeout延迟检查(300ms); - 确保
getCurrentPages()能正确获取当前页面。
setTimeout(() => {
const pages = getCurrentPages()
// 此时页面栈已初始化完成
}, 300)
2. 避免重复跳转
问题: onLaunch 和 onShow 可能同时触发跳转,导致冲突
解决方案:
- 使用
isNavigating标志位防止并发跳转; - 首次启动时
onShow跳过处理; - 跳转成功后重置标志位。
let isNavigating = false
if (currentToken && !isNavigating) {
isNavigating = true
uni.reLaunch({
url: '/pages/Home/HomeIndex',
success: () => {
isNavigating = false
}
})
}
3. 路由拦截器时序
问题: 跳转时路由拦截器可能还未注册完成
解决方案:
- 使用 Vue 的
nextTick确保拦截器已注册。
nextTick(() => {
uni.reLaunch({ url: '/pages/Home/HomeIndex' })
})
4. 兜底机制
问题: 某些边缘情况下 App.vue 的跳转可能失效
解决方案:
- 在登录页的
onMounted中添加兜底检查; - 延迟时间(500ms)长于 App.vue(300ms),确保兜底。
5. 错误处理
原则: 登录状态恢复不应该阻塞应用启动。
实现:
- 使用 try-catch 包裹可能出错的操作;
- 本地存储操作失败时使用默认值,不抛错。
try {
const localToken = uni.getStorageSync('user_token')
if (localToken) {
token.value = localToken
}
} catch (error) {
console.error('获取 token 失败:', error)
return ''
}
6. 登录状态监听
// 监听登录状态变化
watch(() => userStore.token, (newToken, oldToken) => {
if (newToken && !oldToken) {
// 登录成功
emit('login-success')
} else if (!newToken && oldToken) {
// 退出登录
emit('logout')
}
})
结语
本文详细介绍了基于 UniApp 与 Vue3 的持久化登录功能的完整方案。核心思路是:
- 数据持久化:使用本地存储保存登录信息;
- 状态恢复:在应用生命周期钩子中恢复状态;
- 自动跳转:检测到已登录时自动跳转;
- 兜底机制:多层级检查确保可靠性。
以上关于基于UniApp与Vue3的持久化登录功能实现策略的文章就介绍到这了,更多相关内容请搜索码云笔记以前的文章或继续浏览下面的相关文章,希望大家以后多多支持码云笔记。
声明:本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若内容造成侵权/违法违规/事实不符,请将相关资料发送至 admin@mybj123.com 进行投诉反馈,一经查实,立即处理!
重要:如软件存在付费、会员、充值等,均属软件开发者或所属公司行为,与本站无关,网友需自行判断
码云笔记 » 基于UniApp与Vue3的持久化登录功能实现策略
如若内容造成侵权/违法违规/事实不符,请将相关资料发送至 admin@mybj123.com 进行投诉反馈,一经查实,立即处理!
重要:如软件存在付费、会员、充值等,均属软件开发者或所属公司行为,与本站无关,网友需自行判断
码云笔记 » 基于UniApp与Vue3的持久化登录功能实现策略

微信
支付宝