Vue 项目中,注册登录是基础功能,而接口封装、请求拦截器、导航守卫是保障项目可维护性的核心。
一、注册功能:静态布局 + 接口封装 + 用户提示
注册功能是用户体系的入口,需完成静态页面、接口调用、表单校验等步骤。
1. 注册静态布局
使用 Vant UI 快速搭建注册表单(以手机号 + 密码注册为例):
vue
<template>
<van-form @submit="onSubmit">
<van-field
v-model="form.phone"
label="手机号"
placeholder="请输入手机号"
type="tel"
/>
<van-field
v-model="form.password"
label="密码"
placeholder="请输入密码"
type="password"
/>
<van-button type="primary" block native-type="submit">注册</van-button>
</van-form>
</template>
<script>
export default {
data() {
return {
form: { phone: '', password: '' }
};
},
methods: {
onSubmit() {
// 后续实现注册请求
}
}
};
</script>
2. 封装 request 模块(统一请求配置)
基于axios封装请求模块,统一处理请求地址、超时等配置:
javascript
// src/utils/request.js
import axios from 'axios';
// 创建axios实例
const request = axios.create({
baseURL: 'https://api.example.com', // 后端接口地址
timeout: 5000 // 超时时间
});
export default request;
注意:要记得下载axios包(npm i axios)
3. 封装 api 模块(接口统一管理)
将注册接口封装到api目录,便于维护:
javascript
// src/api/user.js
import request from '@/utils/request';
// 注册接口
export function register(data) {
return request({
url: '/auth/register',
method: 'POST',
data
});
}
4. 注册请求:成功 / 失败提示用户
调用封装的接口,结合 Vant 的Toast组件提示用户:
vue
<script>
import { register } from '@/api/user';
import { Toast } from 'vant';
export default {
methods: {
async onSubmit() {
try {
await register(this.form);
Toast.success('注册成功!');
this.$router.push('/login'); // 跳转到登录页
} catch (err) {
Toast.fail(err.response.data.msg || '注册失败');
}
}
}
};
</script>
5. 正则校验 + 响应拦截器统一处理
- 正则校验:在表单提交前验证手机号格式:
javascript
onSubmit() {
const phoneReg = /^1[3-9]\d{9}$/;
if (!phoneReg.test(this.form.phone)) {
Toast.fail('手机号格式错误');
return;
}
// 后续请求逻辑
}
- 响应拦截器:在
request.js中统一处理接口错误:
javascript
// src/utils/request.js
request.interceptors.response.use(
response => response.data, // 直接返回响应体
error => {
// 统一处理错误
Toast.fail(error.response.data.msg || '请求失败');
return Promise.reject(error);
}
);
二、登录功能:接口调用 + Token 存储
登录的核心是获取 Token 并持久化存储,便于后续接口鉴权。
1. 登录功能实现
类似注册流程,调用登录接口并获取 Token:
javascript
// src/api/user.js
export function login(data) {
return request({
url: '/auth/login',
method: 'POST',
data
});
}
vue
// Login.vue
async onSubmit() {
const res = await login(this.form);
// 存储Token
localStorage.setItem('token', res.token);
Toast.success('登录成功');
this.$router.push('/'); // 跳转到首页
}
2. 封装 storage 模块(统一存储操作)
将本地存储封装为工具模块,避免重复代码:
javascript
// src/utils/storage.js
const KEY = 'study-day-1212-token'
// 存储数据
export const setToken = token => localStorage.setItem(KEY, token)
// 获取数据
export const getToken = () => localStorage.getItem(KEY)
// 删除数据
export const delToken = () => localStorage.removeItem(KEY)
注意:KEY是自己定义的一个变量,尽量不要直接使用token,容易和其他重复,建议设置一个不易重复的
使用:
javascript
import { setItem } from '@/utils/storage';
setToken(res.data.token)
三、导航守卫:页面拦截(未登录禁止访问首页)
导航守卫用于控制路由访问权限,比如未登录用户禁止进入首页。
1. 全局前置守卫
在router/index.js中配置全局前置守卫:
javascript
import VueRouter from 'vue-router';
import { getItem } from '@/utils/storage';
import { Toast } from 'vant';
const router = new VueRouter({ routes });
// 全局前置守卫
router.beforeEach((to, from, next) => {
// 不需要登录的页面(如登录、注册)直接放行
const whiteList = ['/login', '/register'];
if (whiteList.includes(to.path)) {
return next();
}
// 验证Token
const token = getItem('token');
if (token) {
next(); // 已登录,放行
} else {
Toast.fail('请先登录');
next('/login'); // 未登录,跳转到登录页
}
});
四、首页开发:组件封装 + 接口请求 + 拦截器优化
首页需实现文章列表展示、分页、数据筛选等功能,结合拦截器统一处理接口鉴权。
1. 首页静态布局 + 封装文章组件
先封装ArticleItem组件,用于渲染单篇文章:
vue
<!-- src/components/ArticleItem.vue -->
<template>
<div class="article-item">
<h3>{{ article.title }}</h3>
<p>{{ article.content }}</p>
<span>{{ article.createTime }}</span>
</div>
</template>
<script>
export default {
props: { article: { type: Object, required: true } }
};
</script>
在首页中使用:
vue
<!-- src/views/Home.vue -->
<template>
<div>
<article-item
v-for="item in articleList"
:key="item.id"
:article="item"
/>
</div>
</template>
<script>
import ArticleItem from '@/components/ArticleItem.vue';
export default {
components: { ArticleItem },
data() {
return { articleList: [] };
}
};
</script>
2. 获取文章列表数据
封装文章列表接口,并在首页调用:
javascript
// src/api/article.js
export function getArticleList(params) {
return request({
url: '/article/list',
method: 'GET',
params // 分页、筛选参数
});
}
vue
// Home.vue
async mounted() {
const res = await getArticleList({ page: 1, size: 10 });
this.articleList = res.list;
}
3. 拦截器统一携带 Token + 处理 401 错误
- 统一携带 Token:在request.js的请求拦截器中添加 Token:
javascript
// src/utils/request.js
import { getItem } from '@/utils/storage';
request.interceptors.request.use(config => {
// 给请求头添加Token
const token = getItem('token');
if (token) {
config.headers.Authorization = `Bearer ${token}`;
}
return config;
});
- 统一处理 401 错误(Token 失效):
javascript
// src/utils/request.js
request.interceptors.response.use(
response => response.data,
error => {
// 401:Token失效
if (error.response.status === 401) {
Toast.fail('登录已过期,请重新登录');
// 清除Token并跳转到登录页
removeItem('token');
router.push('/login');
}
return Promise.reject(error);
}
);
4. 分页功能实现
添加分页参数,实现分页加载:
vue
// Home.vue
data() {
return {
articleList: [],
page: 1, // 当前页
size: 10 // 每页条数
};
},
methods: {
async loadMore() {
const res = await getArticleList({ page: this.page, size: this.size });
this.articleList = [...this.articleList, ...res.list];
this.page++;
}
}
5. 文章列表交互:切换推荐 / 最新 + 跳转详情页
- 切换推荐 / 最新:添加筛选参数,重新请求列表:
vue
<van-tabs v-model="activeTab" @change="fetchArticleList">
<van-tab title="推荐">推荐</van-tab>
<van-tab title="最新">最新</van-tab>
</van-tabs>
<script>
methods: {
async fetchArticleList() {
const type = this.activeTab === 0 ? 'recommend' : 'latest';
const res = await getArticleList({ type, page: 1 });
this.articleList = res.list;
this.page = 2;
}
}
</script>
- 跳转详情页:携带文章 ID 跳转到详情页:
vue
<!-- ArticleItem.vue -->
<div @click="toDetail">
<!-- 文章内容 -->
</div>
<script>
methods: {
toDetail() {
this.$router.push({ path: '/article/detail', query: { id: this.article.id } });
}
}
</script>