Skip to content

Vue Router 与 Pinia

一、Vue Router:页面导航

Vue 是单页应用(SPA),页面切换由前端路由控制,不再刷新浏览器。

路由配置(商城全部页面)

ts
// src/router/index.ts
import { createRouter, createWebHistory } from 'vue-router'

const router = createRouter({
  history: createWebHistory(),
  routes: [
    { path: '/', redirect: '/home' },
    { path: '/home', name: 'Home', component: () => import('@/views/HomeView.vue') },
    { path: '/product/:id', name: 'ProductDetail',
      component: () => import('@/views/ProductDetailView.vue') },
    { path: '/cart', name: 'Cart', component: () => import('@/views/CartView.vue') },
    { path: '/order/list', name: 'OrderList', component: () => import('@/views/OrderListView.vue') },
    { path: '/login', name: 'Login', component: () => import('@/views/LoginView.vue') },
    { path: '/register', name: 'Register', component: () => import('@/views/RegisterView.vue') },
  ],
})

export default router

路由跳转

vue
<template>
  <!-- 声明式跳转 -->
  <RouterLink to="/cart">购物车</RouterLink>
</template>
ts
import { useRouter } from 'vue-router'
const router = useRouter()

// 编程式跳转
router.push('/cart')
router.push({ name: 'ProductDetail', params: { id: 3 } })

动态路由参数

商品详情页用 :id 接收路由参数:

ts
import { useRoute } from 'vue-router'
const route = useRoute()

const id = route.params.id      // 从 URL /product/3 取到 3
const productId = Number(id)

路由守卫:登录校验

未登录访问购物车/订单,跳回登录页。这是企业项目最常见的守卫:

ts
router.beforeEach((to) => {
  const token = localStorage.getItem('token')
  const needAuth = ['/cart', '/order/list'].includes(to.path)
  if (needAuth && !token) {
    return { path: '/login', query: { redirect: to.fullPath } }
  }
})

二、Pinia:全局状态管理

多个组件共享的状态(比如登录用户、购物车数量),放到 Pinia 统一管理。

定义 Store

ts
// src/stores/user.ts
import { defineStore } from 'pinia'
import { ref, computed } from 'vue'

export const useUserStore = defineStore('user', () => {
  const token = ref(localStorage.getItem('token') || '')
  const nickname = ref(localStorage.getItem('nickname') || '')

  const isLogin = computed(() => !!token.value)

  function setLogin(t: string, name: string) {
    token.value = t
    nickname.value = name
    localStorage.setItem('token', t)
    localStorage.setItem('nickname', name)
  }

  function logout() {
    token.value = ''
    nickname.value = ''
    localStorage.removeItem('token')
    localStorage.removeItem('nickname')
  }

  return { token, nickname, isLogin, setLogin, logout }
})

在组件里使用

ts
import { useUserStore } from '@/stores/user'
const userStore = useUserStore()

if (!userStore.isLogin) {
  router.push('/login')
}

userStore.logout()

购物车数量 Badge

ts
// src/stores/cart.ts
import { defineStore } from 'pinia'
import { ref, computed } from 'vue'

export const useCartStore = defineStore('cart', () => {
  const count = ref(0)
  const cartCount = computed(() => count.value)

  function fetchCartCount() {
    // 调用后端 /api/cart,取出条目总数
  }
  return { count, cartCount, fetchCartCount }
})

顶栏展示:<el-badge :value="cartStore.cartCount">购物车</el-badge>

三、登录态与路由的配合

完整的登录流程:

1. 用户在 /login 输入账号密码
2. 调用 POST /api/auth/login → 后端返回 token
3. userStore.setLogin(token, nickname)
4. router.push(redirect || '/home')
5. 之后所有请求带 Authorization 头(axios 拦截器统一加)
6. 访问受保护路由 → 守卫检查 token → 通过才放行

安全提醒

token 存 localStorage 在本教程足够。生产项目更推荐 httpOnly Cookie(防 XSS),第 6 部分 JWT 章节会讨论权衡。

四、本章验收

  • [ ] 通过顶栏能跳转首页/购物车/订单/登录
  • [ ] 未登录访问 /cart 被重定向到 /login
  • [ ] 登录后 userStore.isLogin 为 true,顶栏显示昵称
  • [ ] 购物车 Badge 能显示后端返回的数量

基于 MIT 协议发布,可自由学习与修改