第三阶段:Vue 路由与状态管理(第 45 天)(路由与状态管理实战:开发一个带登录权限的单页应用)

带登录权限的单页应用开发实战

本任务将指导您使用 Vue.js、Vue Router 和 Vuex 开发一个带登录权限的单页应用(SPA)。核心目标是实现登录页面、首页、个人中心(需权限访问)和404页面的路由与状态管理。关键点包括:登录流程、权限控制、状态持久化和路由跳转优化。下面我将逐步解释实现过程,确保结构清晰,并提供代码示例。

1. 配置路由规则(区分公开路由和私有路由)

首先,我们需要定义路由规则,区分公开路由(如登录页和404页)和私有路由(如首页和个人中心,需登录才能访问)。使用 Vue Router 的 router/index.js 文件配置路由。

  • 公开路由 :登录页(/login)和404页(/*)。
  • 私有路由 :首页(/)和个人中心(/profile)。
  • 逻辑说明 :私有路由需要权限控制,通过路由元信息(meta)标记哪些路由需要登录。
javascript 复制代码
// router/index.js
import Vue from 'vue';
import VueRouter from 'vue-router';
import Login from '../views/Login.vue';
import Home from '../views/Home.vue';
import Profile from '../views/Profile.vue';
import NotFound from '../views/NotFound.vue';

Vue.use(VueRouter);

const routes = [
  {
    path: '/login',
    name: 'Login',
    component: Login,
    meta: { requiresAuth: false } // 公开路由
  },
  {
    path: '/',
    name: 'Home',
    component: Home,
    meta: { requiresAuth: true } // 私有路由,需登录
  },
  {
    path: '/profile',
    name: 'Profile',
    component: Profile,
    meta: { requiresAuth: true } // 私有路由
  },
  {
    path: '*',
    component: NotFound,
    meta: { requiresAuth: false } // 404页,公开路由
  }
];

const router = new VueRouter({
  mode: 'history',
  routes
});

export default router;
2. Vuex 封装用户模块(处理登录状态)

使用 Vuex 管理用户状态,包括 token 和用户信息。定义 state、mutation 和 action 来处理登录异步请求和状态变更。

  • state:存储 token 和用户信息。
  • mutation:直接修改状态(如设置 token)。
  • action:处理登录逻辑(如调用 API 验证账号密码),并提交 mutation。
  • 逻辑说明:登录成功后,将 token 存储到 state,并持久化状态。
javascript 复制代码
// store/index.js
import Vue from 'vue';
import Vuex from 'vuex';
import createPersistedState from 'vuex-persistedstate'; // 用于状态持久化

Vue.use(Vuex);

export default new Vuex.Store({
  state: {
    token: null, // 存储登录 token
    userInfo: null // 存储用户信息
  },
  mutations: {
    SET_TOKEN(state, token) {
      state.token = token;
    },
    SET_USER_INFO(state, userInfo) {
      state.userInfo = userInfo;
    },
    CLEAR_USER(state) {
      state.token = null;
      state.userInfo = null;
    }
  },
  actions: {
    async login({ commit }, { username, password }) {
      // 模拟 API 调用验证账号密码
      try {
        const response = await fetch('/api/login', {
          method: 'POST',
          body: JSON.stringify({ username, password })
        });
        const data = await response.json();
        if (data.success) {
          commit('SET_TOKEN', data.token);
          commit('SET_USER_INFO', data.userInfo);
          return true; // 登录成功
        }
        return false; // 登录失败
      } catch (error) {
        console.error('登录失败:', error);
        return false;
      }
    },
    logout({ commit }) {
      commit('CLEAR_USER');
    }
  },
  getters: {
    isAuthenticated: state => !!state.token // 检查是否登录
  },
  plugins: [createPersistedState()] // 状态持久化插件,页面刷新后状态不丢失
});
3. 实现全局路由守卫(权限拦截与目标页面缓存)

使用 Vue Router 的全局前置守卫(beforeEach)检查用户登录状态。如果访问私有路由未登录,则拦截并跳转到登录页,同时缓存目标路径以便登录后回跳。

  • 逻辑说明 :在守卫中,检查路由的 meta.requiresAuth 和 Vuex 的登录状态。未登录时,跳转至登录页,并通过 next 函数传递目标路径。
  • 优化:登录成功后,跳转回原目标页面。
javascript 复制代码
// 在 router/index.js 中,添加守卫
router.beforeEach((to, from, next) => {
  const requiresAuth = to.matched.some(record => record.meta.requiresAuth);
  const isAuthenticated = store.getters.isAuthenticated; // 假设 store 已导入

  if (requiresAuth && !isAuthenticated) {
    // 未登录访问私有路由,跳转到登录页,并存储目标路径
    next({
      path: '/login',
      query: { redirect: to.fullPath } // 缓存目标路径
    });
  } else if (to.path === '/login' && isAuthenticated) {
    // 已登录时访问登录页,直接跳转首页
    next('/');
  } else {
    next(); // 允许访问
  }
});
4. 编写登录组件(表单验证 + 调用 Vuex action)

创建登录组件,实现表单验证和登录逻辑。调用 Vuex action 处理登录,成功后根据缓存的目标路径跳转。

  • 表单验证:使用简单验证(如非空检查),实际项目中可用库如 Vuelidate。
  • 逻辑说明:登录成功后,检查是否有重定向路径,并跳转。
vue 复制代码
<!-- views/Login.vue -->
<template>
  <div>
    <form @submit.prevent="handleLogin">
      <input v-model="username" placeholder="用户名" />
      <input v-model="password" type="password" placeholder="密码" />
      <button type="submit">登录</button>
    </form>
    <p v-if="error">{{ error }}</p>
  </div>
</template>

<script>
import { mapActions } from 'vuex';

export default {
  data() {
    return {
      username: '',
      password: '',
      error: ''
    };
  },
  methods: {
    ...mapActions(['login']),
    async handleLogin() {
      if (!this.username || !this.password) {
        this.error = '用户名和密码不能为空';
        return;
      }
      const success = await this.login({
        username: this.username,
        password: this.password
      });
      if (success) {
        // 登录成功,检查是否有重定向路径
        const redirect = this.$route.query.redirect || '/';
        this.$router.push(redirect);
      } else {
        this.error = '登录失败,请检查账号密码';
      }
    }
  }
};
</script>
5. 编写首页和个人中心组件(从 Vuex 获取用户信息)

创建首页和个人中心组件,从 Vuex 获取用户信息并渲染。个人中心需权限访问,已在路由守卫中处理。

  • 首页组件:显示欢迎信息。
  • 个人中心组件:显示用户详情,需登录才能访问。
vue 复制代码
<!-- views/Home.vue -->
<template>
  <div>
    <h1>欢迎来到首页</h1>
    <p v-if="userInfo">你好,{{ userInfo.name }}</p>
  </div>
</template>

<script>
import { mapState } from 'vuex';

export default {
  computed: {
    ...mapState(['userInfo'])
  }
};
</script>
vue 复制代码
<!-- views/Profile.vue -->
<template>
  <div>
    <h1>个人中心</h1>
    <div v-if="userInfo">
      <p>用户名:{{ userInfo.name }}</p>
      <p>邮箱:{{ userInfo.email }}</p>
    </div>
    <button @click="logout">退出登录</button>
  </div>
</template>

<script>
import { mapActions, mapState } from 'vuex';

export default {
  computed: {
    ...mapState(['userInfo'])
  },
  methods: {
    ...mapActions(['logout']),
    logout() {
      this.logout();
      this.$router.push('/login');
    }
  }
};
</script>
总结要点回顾

通过本实战,您已掌握带登录权限的 SPA 开发核心流程:

  • Vue Router 与 Vuex 联动 :路由守卫(beforeEach)结合 Vuex 状态(如 isAuthenticated)实现权限控制。例如,访问私有路由时,检查状态:如果用户未登录(即 \\text{isAuthenticated} = \\text{false} ),则拦截跳转。
  • 权限控制核心逻辑:基于"状态存储 + 路由拦截"的模式。状态存储在 Vuex,路由守卫动态判断权限,确保安全访问。
  • 实战解决方案
    • 状态持久化 :使用插件如 vuex-persistedstate,防止页面刷新后登录状态丢失。
    • 路由跳转优化 :在登录页通过 query 参数缓存目标路径,登录后自动跳转。
    • 其他优化:表单验证、错误处理等提升用户体验。

此方案高效可靠,适用于大多数 SPA 应用。实践中,可扩展更多功能如角色权限或多因素认证。

相关推荐
编程大师哥2 小时前
JavaScript 和 Python 哪个更适合初学者?
开发语言·javascript·python
Irene19912 小时前
Vue 3 中的具名插槽仍然完全支持,Vue 2 的旧语法 Vue 3 中已废弃
vue.js·slot
方方洛2 小时前
技术实践总结:schema-bridgion:json、xml、yaml、toml文件相互转换
xml·前端·typescript·node.js·json
2601_949575862 小时前
Flutter for OpenHarmony二手物品置换App实战 - 自定义组件实现
android·javascript·flutter
object not found2 小时前
基于uniapp开发小程序自定义顶部导航栏状态栏标题栏
前端·javascript·小程序·uni-app
Irene19913 小时前
v-model 在 Vue2 和 Vue3 中的实现对比或异同
vue.js
能源革命3 小时前
Three.js、Unity、Cesium对比分析
开发语言·javascript·unity
We་ct3 小时前
LeetCode 28. 找出字符串中第一个匹配项的下标:两种实现与深度解析
前端·算法·leetcode·typescript
xzl043 小时前
小智服务端chat入口工具调用流程
java·服务器·前端