Vue3应用中带参数的路由为何无法显示404视图?
问题描述
我正在用Vue 3、TypeScript和TMDB API开发SPA,配置404页面后,访问根URL下的随机路径(如http://localhost:8080/someNonExistingPath)能正常显示404,但访问动态路由的无效参数(如http://localhost:8080/movie/a615656)时,却不会触发404视图。
当前路由配置src/router/index.ts:
import { createRouter, createWebHistory, RouteRecordRaw } from 'vue-router'; import HomeView from '../views/HomeView.vue'; import TopRatedMoviesView from '../views/TopRatedMoviesView.vue'; import MovieDetailsView from '../views/MovieDetailsView.vue'; import ActorDetailsView from '../views/ActorDetailsView.vue'; import NotFoundView from '../views/NotFoundView.vue'; const routes: Array<RouteRecordRaw> = [ { path: '/', name: 'home', component: HomeView }, { path: '/top-rated', name: 'top_rated', component: TopRatedMoviesView }, { path: '/movie/:id', name: 'movie_details', component: MovieDetailsView }, { path: '/actor/:id', name: 'actor_details', component: ActorDetailsView }, { path: '/:pathMatch(.*)*', name: "404", component: NotFoundView }, ] const router = createRouter({ history: createWebHistory(process.env.BASE_URL), routes }) export default router
404视图src/views/NotFoundView.vue代码:
<template> <div class="container d-flex"> <div class="text-center"> <h1 class="mb-3">404 | page not found</h1> <router-link class="btn btn-sm btn-success" to="/"> <strong>Go to the Homepege</strong> </router-link> </div> </div> </template> <script lang="ts"> import { defineComponent } from 'vue'; export default defineComponent({ name: 'NotFoundView', }); </script>
原因分析
路由匹配按配置顺序执行:当访问/movie/a615656时,路由系统会优先匹配/movie/:id规则(:id可匹配任意字符串),加载MovieDetailsView组件,不会走到最后的404路由。只有当路径完全不匹配前面所有规则时,才会触发/:pathMatch(.*)*的404路由。
可靠修复方案
方案1:动态路由组件内校验数据,不存在则跳转404
在MovieDetailsView和ActorDetailsView中,调用TMDB API获取数据后判断是否存在,若返回空或请求失败,通过router.push跳转到404页面。
以MovieDetailsView为例:
<script lang="ts"> import { defineComponent, onMounted } from 'vue'; import { useRoute, useRouter } from 'vue-router'; // 假设你有封装的TMDB API请求函数 import { getMovieDetails } from '@/api/tmdb'; export default defineComponent({ name: 'MovieDetailsView', setup() { const route = useRoute(); const router = useRouter(); onMounted(async () => { try { const movie = await getMovieDetails(route.params.id as string); // 数据为空则跳转404 if (!movie) { router.push({ name: '404' }); return; } // 正常处理电影数据 } catch (error) { // 捕获请求错误,跳转404 router.push({ name: '404' }); } }); return {}; }, }); </script>
方案2:使用路由守卫(beforeEnter)提前校验
在动态路由配置中添加beforeEnter守卫,提前请求API校验参数有效性,无效则跳转到404。
修改路由配置:
// 导入API函数 import { getMovieDetails, getActorDetails } from '@/api/tmdb'; const routes: Array<RouteRecordRaw> = [ // ...其他路由 { path: '/movie/:id', name: 'movie_details', component: MovieDetailsView, beforeEnter: async (to) => { try { const movie = await getMovieDetails(to.params.id as string); if (!movie) return { name: '404' }; } catch (error) { return { name: '404' }; } } }, { path: '/actor/:id', name: 'actor_details', component: ActorDetailsView, beforeEnter: async (to) => { try { const actor = await getActorDetails(to.params.id as string); if (!actor) return { name: '404' }; } catch (error) { return { name: '404' }; } } }, // ...404路由 ]
方案3:限制动态参数格式(辅助优化)
如果TMDB的id是纯数字,可以在路由路径中用正则限制id仅匹配数字,非数字参数会直接走到404路由。注意:该方案仅过滤格式错误的id,无法处理格式正确但不存在的id,需配合前两种方案使用。
修改路由路径:
{ path: '/movie/:id(\\d+)', // 仅匹配数字id name: 'movie_details', component: MovieDetailsView }, { path: '/actor/:id(\\d+)', name: 'actor_details', component: ActorDetailsView },
总结
最可靠的方案是方案1或方案2,能真正校验参数对应的资源是否存在;方案3作为辅助手段,可减少无效API调用。
内容的提问来源于stack exchange,提问作者Razvan Zamfir

