Vue3如何通过动态路由参数匹配渲染同名页面组件
原写法失效原因
- 构建工具(Vite/Webpack)的动态导入
import()存在静态分析限制:完全靠运行时拼接的路径无法在构建阶段被识别,构建工具无法确定需要打包的文件范围,直接导致模块加载失败 - 路径拼接逻辑错误:访问
/forms/form/FirstPage时location.pathname.slice(1)得到的是forms/form/FirstPage,和pages目录下的文件路径不匹配 - 路由配置初始化阶段读取
location对象无法响应后续路由参数变化,也没有做组件不存在的兜底逻辑,会直接抛出加载错误
可落地方案(推荐使用通用加载组件方式,易维护扩展)
第一步:创建通用动态页面加载组件
新建src/components/DynamicPageLoader.vue,代码如下:
<template> <!-- 动态渲染匹配到的页面组件 --> <component :is="loadedComponent" v-if="loadedComponent" /> <!-- 组件不存在时渲染404 --> <NotFound v-else /> </template> <script setup lang="ts"> import { ref, watch, onMounted } from "vue" import { useRoute } from "vue-router" import NotFound from "./NotFound.vue" // -------------------------- // 注意根据你的构建工具二选一 // -------------------------- // Vite 环境用:预收集pages目录下所有vue组件,自动做懒加载拆分 const pageModules = import.meta.glob("../pages/*.vue") // Webpack/Vue CLI 环境用,注释掉上面的Vite版本,放开下面这段 // const pageContext = require.context("../pages", false, /\.vue$/) // const pageModules = pageContext.keys().reduce((moduleMap, filePath) => { // const componentName = filePath.split("/").pop()?.replace(".vue", "") || "" // moduleMap[componentName] = () => Promise.resolve(pageContext(filePath).default) // return moduleMap // }, {} as Record<string, () => Promise<any>>) const route = useRoute() const loadedComponent = ref<any>(null) const loadTargetPage = async () => { // 从路由路径参数中拿到组件名 const targetComponentName = route.params.id as string // Vite环境用这个路径判断 const targetPath = `../pages/${targetComponentName}.vue` if (pageModules[targetPath]) { // 加载目标组件 const moduleRes = await pageModules[targetPath]() loadedComponent.value = (moduleRes as any).default return } // Webpack环境把上面的判断替换成下面这段 // if (pageModules[targetComponentName]) { // const moduleRes = await pageModules[targetComponentName]() // loadedComponent.value = moduleRes.default // return // } // 匹配不到组件时清空,渲染404 loadedComponent.value = null } // 首次进入加载 onMounted(loadTargetPage) // 监听路由参数变化,切换组件 watch(() => route.params.id, loadTargetPage) </script>
第二步:修改路由配置
编辑src/router/index.ts,替换原来的/forms/form/:id路由规则,删除错误的动态导入逻辑,移除未使用的useRoute导入:
import { createWebHistory, createRouter } from "vue-router" import FirstPage from "../pages/FirstPage.vue" import SecondPage from "../pages/SecondPage.vue" import ThirdPage from "../pages/ThirdPage.vue" import NotFound from "../components/NotFound.vue" const routes = [ { path: "/", name: "first", component: FirstPage }, { path: "/2/70", name: "second", component: SecondPage }, { path: "/3/70", name: "third", component: ThirdPage }, // 替换为动态页面加载组件 { path: "/forms/form/:id", component: () => import("../components/DynamicPageLoader.vue") }, { path: "/:catchAll(.*)", component: NotFound } ] const router = createRouter({ history: createWebHistory(), routes }) export default router
注意事项
- 所有需要被动态渲染的页面必须放在
src/pages目录下,文件名和路由id参数完全一致,大小写敏感 - 如果
pages下存在嵌套子目录,只需要把Vite的glob规则改成import.meta.glob("../pages/**/*.vue"),对应调整路径拼接逻辑即可 - 该方案默认做懒加载,不会一次性打包所有页面,访问对应路由时才会加载目标组件代码,性能和静态路由一致
- 后续需要加页面权限校验、加载动画、页面缓存逻辑时,直接在
DynamicPageLoader.vue里扩展即可,不需要修改路由配置
内容的提问来源于stack exchange,提问作者Rahul Chandra
相关产品推荐
相关产品推荐

