UniApp + Vue3 实现 Token 登录认证系统:完整教程及源码分享
本文详细介绍如何使用 UniApp + Vue3 + SCSS 开发一个完整的用户登录认证系统。包含完整源码,一步步教你实现用户登录、Token 验证、自动登出等功能。
一、项目介绍
1.1 技术栈
- 框架:
UniApp+Vue3 - 开发语言:
JavaScript - 样式处理:
SCSS - 状态管理:
Composition API - 网络请求:
uni.request+Promise封装 - 数据存储:
uni.setStorageSync
1.2 功能特点
- 基于 Token 的用户认证
- 表单验证和错误处理
- 自动登出和会话管理
- 跨平台兼容(H5/小程序/App)
- 响应式布局设计
二、项目结构
├── pages/ # 页面文件夹
│ ├── login/ # 登录模块
│ │ └── login.vue # 登录页面
│ └── index/ # 首页模块
│ └── index.vue # 首页(需登录访问)
├── utils/ # 工具文件夹
│ ├── request.js # 请求封装
│ └── api.js # 接口管理
└── static/ # 静态资源
└── logo.png # 项目logo
三、详细开发流程
1. 工具层开发 (/utils)
1.1 请求封装 (/utils/request.js)
首先创建请求工具,用于统一处理所有HTTP请求:
const BASE_URL = process.env.NODE_ENV === 'development'
? 'http://a1/api/20250201' // 开发环境
: 'https://xiyueta.com/api/20250201' // 生产环境
// 构建url参数
const buildUrl = (url, params) => {
let queryString = ''
if (params) {
queryString = Object.keys(params)
.map(key => `${encodeURIComponent(key)}=${encodeURIComponent(params[key])}`)
.join('&')
}
return queryString ? `${url}?${queryString}` : url
}
// 请求函数
export const request = (options = {}) => {
const token = uni.getStorageSync('token')
let url = BASE_URL + options.url
let requestParams = { ...options.data }
if (options.method === 'GET' && options.data) {
url = buildUrl(url, options.data)
delete options.data
}
return new Promise((resolve, reject) => {
// 添加请求日志
console.log('发送请求:', {
url: url,
method: options.method,
data: options.data
})
uni.request({
url: url,
method: options.method || 'GET',
data: options.method === 'POST' ? requestParams : undefined,
header: {
'content-type': 'application/x-www-form-urlencoded'
},
success: (res) => {
// 添加响应日志
console.log('请求响应:', res)
if (res.statusCode === 200) {
resolve(res.data)
} else {
reject(new Error(`请求失败: ${res.statusCode}`))
}
},
fail: (err) => {
console.error('请求失败:', err)
reject(new Error(`请求错误: ${err.errMsg}`))
}
})
})
}
为什么这样设计:
1. 分环境配置BASE_URL,方便开发和生产环境切换
2. buildUrl函数处理GET请求参数,确保参数正确编码
3. 统一的请求格式和错误处理,减少重复代码
4. Promise封装支持async/await语法
1.2 接口管理 (/utils/api.js)
创建统一的接口管理文件:
import { request } from './request'
export const api = {
// 登录接口
login: (params) => {
return request({
url: '/test/post_login.asp',
method: 'POST',
data: params
})
},
// token验证接口
checkToken: (params) => {
return request({
url: '/test/check_token.asp',
method: 'GET',
data: {
token: params.token,
json: params.json || 1
}
})
}
}
为什么这样设计:
1. 统一管理所有接口,便于维护
2. 接口复用,避免重复编写请求代码
3. 可以统一处理接口参数
4. 便于后期接口变更
2. 页面开发
2.1 登录页面 (/pages/login/login.vue)
登录页面是用户进入系统的入口:
<template>
<view class="login-container">
<view class="login-box">
<text class="login-title">用户登录</text>
<view class="form-box">
<view class="input-item">
<input
v-model="username"
type="text"
placeholder="请输入用户名"
placeholder-class="placeholder"
/>
</view>
<view class="input-item">
<input
v-model="password"
type="password"
placeholder="请输入密码"
placeholder-class="placeholder"
/>
</view>
<button
class="login-btn"
:loading="loading"
@tap="handleLogin"
>
登录
</button>
</view>
</view>
</view>
</template>
<script setup>
import { ref } from 'vue'
import { api } from '@/utils/api.js'
const username = ref('')
const password = ref('')
const loading = ref(false)
const handleLogin = async () => {
if (!username.value || !password.value) {
uni.showToast({
title: '请输入用户名和密码',
icon: 'none'
})
return
}
try {
loading.value = true
const res = await api.login({
username: username.value,
password: password.value
})
if (res.status === 'y') {
uni.setStorageSync('token', res.data.token)
uni.showToast({
title: '登录成功',
icon: 'success'
})
console.log('准备跳转到首页')
setTimeout(() => {
console.log('开始执行跳转')
uni.navigateTo({
url: '/pages/index/index'
})
}, 1500)
} else {
uni.showToast({
title: res.info || '登录失败',
icon: 'none'
})
}
} catch (error) {
uni.showToast({
title: '登录失败,请稍后重试',
icon: 'none'
})
console.error('登录错误:', error)
} finally {
loading.value = false
}
}
</script>
<style lang="scss" scoped>
.login-container {
min-height: 100vh;
background-color: #fff;
padding: 40rpx;
display: flex;
align-items: center;
justify-content: center;
.login-box {
width: 100%;
padding: 0 40rpx;
.login-title {
display: block;
font-size: 44rpx;
font-weight: 600;
text-align: center;
color: #333;
margin-bottom: 60rpx;
}
.form-box {
.input-item {
margin-bottom: 30rpx;
input {
width: 100%;
height: 88rpx;
background: #f5f5f5;
border-radius: 12rpx;
padding: 0 30rpx;
font-size: 28rpx;
color: #333;
box-sizing: border-box;
&:focus {
background: #fff;
box-shadow: 0 0 0 2rpx rgba(0, 122, 255, 0.2);
}
}
}
.placeholder {
color: #999;
}
.login-btn {
width: 100%;
height: 88rpx;
line-height: 88rpx;
background: #007AFF;
color: #fff;
font-size: 32rpx;
border-radius: 12rpx;
margin-top: 20rpx;
&:active {
opacity: 0.8;
}
}
}
}
}
</style>
为什么这样设计:
1. 使用组合式API (setup),代码更简洁清晰
2. 响应式数据管理用户输入
3. loading状态提升用户体验
4. 统一的错误提示
5. SCSS嵌套语法提高样式可维护性
2.2 首页开发 (/pages/index/index.vue)
首页是登录后的主页面:
<template>
<view class="container">
<image class="logo" src="/static/logo.png"></image>
<view class="text-area">
<text class="title">{{title}}</text>
</view>
<!-- 退出登录按钮 -->
<button class="logout-btn" @tap="handleLogout">退出登录</button>
</view>
</template>
<script setup>
import { ref, onMounted } from 'vue'
import { api } from '@/utils/api.js'
const title = ref('Hello')
// 跳转到登录页
const redirectToLogin = () => {
uni.removeStorageSync('token')
uni.reLaunch({
url: '/pages/login/login'
})
}
// 检查token
const checkToken = async () => {
console.log('开始检查token')
const token = uni.getStorageSync('token')
console.log('当前token:', token)
if (!token) {
uni.showToast({
title: '请先登录',
icon: 'none'
})
redirectToLogin()
return
}
try {
const res = await api.checkToken({
token: token,
json: 1
})
if (res.status !== 'y') {
uni.showToast({
title: 'token已失效,请重新登录',
icon: 'none'
})
redirectToLogin()
}
} catch (error) {
console.error('token检测错误:', error)
uni.showToast({
title: 'token验证失败,请重新登录',
icon: 'none'
})
redirectToLogin()
}
}
// 退出登录
const handleLogout = () => {
uni.showModal({
title: '提示',
content: '确定要退出登录吗?',
success: (res) => {
if (res.confirm) {
uni.removeStorageSync('token')
uni.showToast({
title: '已退出登录',
icon: 'success'
})
setTimeout(() => {
uni.reLaunch({
url: '/pages/login/login'
})
}, 1500)
}
}
})
}
// 页面加载时检查token
onMounted(() => {
console.log('页面加载,执行onMounted')
checkToken()
})
// 页面显示时也检查token
const onShow = () => {
console.log('页面显示,执行onShow')
checkToken()
}
defineExpose({
onShow
})
</script>
<style lang="scss" scoped>
.container {
min-height: 100vh;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 40rpx;
.logo {
height: 200rpx;
width: 200rpx;
margin-bottom: 50rpx;
}
.text-area {
display: flex;
justify-content: center;
.title {
font-size: 36rpx;
color: #8f8f94;
}
}
.logout-btn {
width: 90%;
height: 88rpx;
line-height: 88rpx;
background: #ff4d4f;
color: #fff;
font-size: 32rpx;
border-radius: 12rpx;
margin-top: 60rpx;
&:active {
opacity: 0.8;
}
}
}
</style>
为什么这样设计:
1. onMounted钩子验证token,确保访问权限
2. onShow钩子处理页面重新显示时的token验证
3. 统一的退出登录流程
4. 清晰的错误提示和自动跳转
四、开发要点解析
1. 登录流程
const handleLogin = async () => {
// 1. 表单验证
if (!username.value || !password.value) {
uni.showToast({ title: '请输入用户名和密码' })
return
}
// 2. 发起登录请求
try {
loading.value = true
const res = await api.login({
username: username.value,
password: password.value
})
// 3. 处理登录响应
if (res.status === 'y') {
// 保存token
uni.setStorageSync('token', res.data.token)
// 跳转首页
uni.navigateTo({ url: '/pages/index/index' })
}
} catch (error) {
// 错误处理
}
}
2. Token验证
const checkToken = async () => {
const token = uni.getStorageSync('token')
try {
const res = await api.checkToken({
token: token,
json: 1
})
if (res.status !== 'y') {
// token无效,跳转登录页
redirectToLogin()
}
} catch (error) {
// 错误处理
}
}
五、性能优化建议
1. 请求优化
- 合理使用 loading 状态
- 防抖处理
- 请求缓存
2. 页面优化
- 及时销毁事件监听
- 合理使用生命周期钩子
- 避免不必要的token检查
六、常见问题解答
6.1 登录失败问题
Q:为什么登录请求发送成功但无法登录?
A:常见原因包括:
1. 接口地址配置错误
2. 请求参数格式不正确
3. 后端返回的状态码判断有误
6.2 Token验证问题
Q:为什么Token验证总是失败?
A:可能的原因:
1. Token存储时机不对
2. Token格式不正确
3. 验证接口配置有误
七、进阶优化建议
1. 安全性增强
- 添加请求加密
- 实现Token自动刷新
- 增加登录失败次数限制
2. 用户体验优化
- 添加记住密码功能
- 实现自动登录
- 优化页面过渡效果
AI 提示词参考
如果您希望使用 AI 生成类似的代码或项目,可以使用以下提示词:
请帮我用 uni-app + Vue3 开发一个登录系统,包含以下功能:
1. 用户名、密码输入框
2. 登录接口对接
3. Token存储和验证
4. 自动登出功能
5. 完整的错误处理
总结
本项目展示了如何使用现代化的技术栈实现一个完整的登录系统。通过:
- 组合式 API 提高代码可维护性
- Promise 封装统一处理请求
- Token 机制确保安全性
- SCSS 实现响应式布局
希望这个教程能帮助您快速上手 UniApp + Vue3 的登录系统开发。
---
关于作者
如果本指南对您有所帮助,欢迎交流和探讨技术问题。
📌 QQ: 313801120
📖 更多文章: www.xiyueta.com/
希望能一起成长,共同探索更多开发技巧!
参考资料
#uni-app #Vue3 #前端开发 #登录系统