4.1 Vue Router 与页面导航
本章目标: 创建两个页面,使用 URL 参数,并知道刷新深层路径为何可能返回 404。
安装与注册
在 vue-lab 运行:
npm install vue-router@4
src/router/index.ts
import { createRouter, createWebHistory } from 'vue-router';
export const router = createRouter({
history: createWebHistory(import.meta.env.BASE_URL),
routes: [
{ path: '/', name: 'tasks', component: () => import('../views/TaskListView.vue') },
{ path: '/tasks/:id', name: 'task-detail', component: () => import('../views/TaskDetailView.vue') },
{ path: '/:pathMatch(.*)*', component: () => import('../views/NotFoundView.vue') },
],
});
动态 import 为页面提供按需加载。文件都要创建,不能只配置不存在的页面路径。
src/main.ts
import { createApp } from 'vue';
import App from './App.vue';
import { router } from './router';
createApp(App).use(router).mount('#app');
src/App.vue
<template>
<main>
<nav><RouterLink to="/">任务列表</RouterLink></nav>
<RouterView />
</main>
</template>
创建页面
src/views/TaskListView.vue
<template>
<section>
<h1>任务列表</h1>
<RouterLink :to="{ name: 'task-detail', params: { id: 1 } }">
查看任务 1
</RouterLink>
</section>
</template>
src/views/TaskDetailView.vue
<script setup lang="ts">
import { computed } from 'vue';
import { useRoute } from 'vue-router';
const route = useRoute();
const taskId = computed(() => Number(route.params.id));
const validId = computed(() => Number.isSafeInteger(taskId.value) && taskId.value > 0);
</script>
<template>
<section>
<h1>任务详情</h1>
<p v-if="validId">当前任务 ID:{{ taskId }}</p>
<p v-else role="alert">任务 ID 无效</p>
</section>
</template>
src/views/NotFoundView.vue
<template>
<section><h1>页面不存在</h1><RouterLink to="/">返回列表</RouterLink></section>
</template>
路由参数来自 URL,通常是字符串或字符串数组,不能直接当作 number DTO。先转换并校验,再请求 API。
route 与 router
useRoute() 读取当前地址的信息;useRouter() 执行导航:
组件中返回列表的片段
import { useRouter } from 'vue-router';
const router = useRouter();
function goBackToList() {
return router.push({ name: 'tasks' });
}
push 加入历史记录,replace 替换当前记录。搜索条件可以放 query,例如 /?status=pending,便于分享和刷新后恢复。
同一个组件可能被复用
从 /tasks/1 跳到 /tasks/2 时,Vue Router 可能复用详情组件,onMounted 不会再次执行。需要重新加载数据时观察参数:
loadTask 已定义时的片段
watch(() => route.params.id, id => {
const value = Number(id);
if (Number.isSafeInteger(value) && value > 0) void loadTask(value);
}, { immediate: true });
导入 watch,并确保 loadTask 内部处理失败与请求竞争,不能只忽略 Promise。
History 模式与服务器
createWebHistory 生成正常路径,生产服务器必须把不对应静态文件、也不属于 /api 的前端路径回退到 index.html。createWebHashHistory 使用 /#/tasks/1,配置更简单,但 URL 形式不同。
区分站点
以上是 Vue SPA 的回退需求。Docusaurus 为文档生成独立 HTML 页面,部署方式不同,详见部署指南。
练习
添加 /about 页面和导航链接,再输入一个不存在的地址验证 NotFound。将详情 ID 改为 abc,确认页面给出无效参数提示。
检查答案
新增 AboutView 并在通配路由之前注册 /about。有效详情 ID 必须是正的安全整数;把错误文本转成 NaN 后继续调用 API 不算完成校验。