主题
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 能显示后端返回的数量