1-引入router基本逻辑
- 新增frontend-vue2\src\router\index.js:
js
import Vue from "vue";
import Router from "vue-router";
// 引入我们要展示的页面组件(先建两个示例页面)
import Login from "@/views/Login.vue";
import Dashboard from "@/views/Dashboard.vue";
// 告诉 Vue 使用 vue-router
Vue.use(Router);
// 路由表:定义页面路径与对应组件
const routes = [
{
path: "/login",
name: "Login",
component: Login,
},
{
path: "/",
name: "Dashboard",
component: Dashboard,
meta: { requiresAuth: true }, // meta 信息,用来标记需要登录才能访问
},
];
// 创建 Router 实例
const router = new Router({
mode: "history", // 使用 history 模式,URL 没有 #,更好看
routes,
});
// 全局路由守卫:每次路由跳转都会执行
// to:跳转的目标信息
// from:跳转前的信息
// next:放行
router.beforeEach((to, from, next) => {
// 判断目标路由是否需要登录
const requiresAuth = to.matched.some((record) => record.meta.requiresAuth);
// 这里先写死一个假登录状态,下一步我们再用 Vuex 来判断
const isLoggedIn = false; // TODO: 下一步会替换成从 Vuex 读取 token
if (requiresAuth && !isLoggedIn) {
// 没登录想访问受保护页面,跳转到 /login
console.log("目标页面" + to.fullPath + "需要登录");
next({ path: "/login", query: { redirect: to.fullPath } });
} else {
// 其他情况直接放行
next();
}
});
export default router;
- 主页面引入路由标签:
<router-view></router-view>
html
<template>
<div id="app">
<router-view></router-view>
</div>
</template>
- 修改frontend-vue2\src\main.js
js
import Vue from "vue";
import App from "./App.vue";
// 路由组件
import router from "./router";
Vue.config.productionTip = false;
new Vue({
router,// 新增router
render: (h) => h(App),
}).$mount("#app");
作用:
- 实现了基础的页面路由管理
- 建立全局路由守卫,没有token的就跳登录,简单权限控制
- 为后续集成Vuex管理用户登录状态预留了扩展点
2-引入Vuex基本逻辑
- 新增frontend-vue2\src\store\index.js,PS:login action后续修改为接口调用
js
import Vue from "vue";
import Vuex from "vuex";
Vue.use(Vuex);
export default new Vuex.Store({
state: {
// 全局状态:存储用户 token
token: null,
},
// mutations:修改state的唯一入口
mutations: {
// 设置 token(登录时调用)
setToken(state, token) {
state.token = token;
},
// 清除 token(退出时调用)
clearToken(state) {
state.token = null;
},
},
// actions:处理异步逻辑
actions: {
// 登录:这里模拟一个接口调用
login({ commit }, { username, password }) {
return new Promise((resolve, reject) => {
// 假装调了后端接口
if (username === 'admin' && password === '123456') {
const fakeToken = 'fake-jwt-token'
commit('setToken', fakeToken) // 调用 mutation 修改 state
resolve(fakeToken)
} else {
reject(new Error('用户名或密码错误'))
}
})
},
// 退出登录
logout({ commit }) {
commit("clearToken");
},
},
// getters:数据的派生计算
getters: {
// 获取登录状态:是否有 token
isLoggedIn: (state) => !!state.token,
},
});
- 修改上方的frontend-vue2\src\router\index.js代码,新增Vuex支持:
js
import Vue from "vue";
import Router from "vue-router";
import store from "@/store"; // 引入 Vuex
// 引入我们要展示的页面组件(先建两个示例页面)
import Login from "@/views/Login.vue";
import Dashboard from "@/views/Dashboard.vue";
// 告诉 Vue 使用 vue-router
Vue.use(Router);
// 路由表:定义页面路径与对应组件
const routes = [
{
path: "/login",
name: "Login",
component: Login,
},
{
path: "/",
name: "Dashboard",
component: Dashboard,
meta: { requiresAuth: true }, // meta 信息,用来标记需要登录才能访问
},
];
// 创建 Router 实例
const router = new Router({
mode: "history", // 使用 history 模式,URL 没有 #,更好看
routes,
});
// 全局路由守卫:每次路由跳转都会执行
// to:跳转的目标信息
// from:跳转前的信息
// next:放行
router.beforeEach((to, from, next) => {
// 判断目标路由是否需要登录
const requiresAuth = to.matched.some((record) => record.meta.requiresAuth);
// 登录状态:从Vuex 获取是否有token来判断
const isLoggedIn = store.getters.isLoggedIn;
if (requiresAuth && !isLoggedIn) {
// 没登录想访问受保护页面,跳转到 /login
console.log("目标页面" + to.fullPath + "需要登录");
next({ path: "/login", query: { redirect: to.fullPath } });
} else {
// 其他情况直接放行
next();
}
});
export default router;
- 修改frontend-vue2\src\main.js
js
import Vue from "vue";
import App from "./App.vue";
// 路由组件
import router from "./router";
// Vuex
import store from "./store";
Vue.config.productionTip = false;
new Vue({
router,// router
store, // 把 store 注入全局
render: (h) => h(App),
}).$mount("#app");
作用:
- 使用Vuex存储全局状态
- 调用login action,验证成功后自动更新token并标记为已登录
- 调用logout action,清除token并标记为未登录
- 通过getters.isLoggedIn可以随时获取当前登录状态
3-引入axios基本逻辑
- 新增frontend-vue2\src\api\auth.js
js
import request from "./request";
// 登录接口
export function loginApi(data) {
return request({
url: "/login",
method: "post",
data,
});
}
// 获取当前用户信息
export function getUserInfo() {
return request({
url: "/user/info",
method: "get",
});
}
- 新增frontend-vue2\src\api\request.js
js
// axios 封装
import axios from "axios";
import store from "@/store";
import router from "@/router";
// 创建 axios 实例
const service = axios.create({
baseURL: "http://localhost:8080/api", // 后端接口的统一前缀
timeout: 5000, // 请求超时时间
});
// 请求拦截器:每次请求都带上 token
service.interceptors.request.use(
(config) => {
const token = store.state.token;
if (token) {
config.headers["Authorization"] = `Bearer ${token}`;
}
return config;
},
(error) => {
return Promise.reject(error);
}
);
// 响应拦截器:统一处理错误
service.interceptors.response.use(
(response) => {
return response.data;
},
(error) => {
if (error.response && error.response.status === 401) {
// 401 未授权,说明 token 过期或无效
store.dispatch("logout");
router.push("/login");
}
return Promise.reject(error);
}
);
export default service;
- 修改上方的frontend-vue2\src\store\index.js代码,将login action部分改为axios接口访问
js
import Vue from "vue";
import Vuex from "vuex";
import { loginApi } from "@/api/auth";
Vue.use(Vuex);
export default new Vuex.Store({
state: {
// 全局状态:存储用户 token
token: null,
},
// mutations:修改state的唯一入口
mutations: {
// 设置 token(登录时调用)
setToken(state, token) {
state.token = token;
},
// 清除 token(退出时调用)
clearToken(state) {
state.token = null;
},
},
// actions:处理异步逻辑(像 Java Service 层)
actions: {
async login({ commit }, { username, password }) {
try {
const res = await loginApi({ username, password });
commit("setToken", res.token); // 后端返回的 token
} catch (err) {
throw err;
}
},
// 退出登录
logout({ commit }) {
commit("clearToken");
},
},
// getters:数据的派生计算
getters: {
// 获取登录状态:是否有 token
isLoggedIn: (state) => !!state.token,
},
});
作用:
- 使用axios实现接口访问
- 请求拦截器:在每次请求发送前,从 Vuex 的 store.state.token 中获取 token,添加进Authorization请求头
- 响应拦截器:对响应数据进行统一处理
4-页面代码补充
frontend-vue2\src\views\Dashboard.vue
html
<template>
<div class="dashboard">
<h2>欢迎来到首页</h2>
<p>这是登录后才能访问的页面</p>
<button @click="logout">退出登录</button>
</div>
</template>
<script>
export default {
name: "Dashboard",
methods: {
logout() {
this.$store.dispatch('logout')
this.$router.push('/login')
}
}
}
</script>
frontend-vue2\src\views\Login.vue
html
<template>
<div class="login">
<h2>登录页面</h2>
<form @submit.prevent="handleLogin">
<div>
<label>用户名:</label>
<input v-model="username" />
</div>
<div>
<label>密码:</label>
<input type="password" v-model="password" />
</div>
<button type="submit">登录</button>
</form>
<p v-if="errorMsg" style="color: red">{{ errorMsg }}</p>
</div>
</template>
<script>
export default {
name: "Login",
data() {
return {
username: "",
password: "",
errorMsg: "",
};
},
methods: {
async handleLogin() {
try {
// 调用 Vuex 的 login action
await this.$store.dispatch("login", {
username: this.username,
password: this.password,
});
// 登录成功后跳转到首页
this.$router.push("/");
} catch (err) {
this.errorMsg = err.message;
}
},
},
};
</script>