vue3中404页面显示问题Catch all routes (“*“) must now be defined using a param with a custom regexp.

vue3中404页面显示问题Catch all routes (“*“) must now be defined using a param with a custom regexp

一、背景与问题

在Vue3项目中,开发者常会遇到一个令人困惑的错误提示:

Catch all routes ("*") must now be defined using a param with a custom regexp

这个错误通常出现在使用Vue Router 4.x版本时,尝试使用*通配符定义404页面。这一行为在Vue Router 2.x版本中是被允许的,但在Vue Router 4.x中被移除并重新设计。此变更背后是Vue Router对路由匹配机制的重构,其核心目标是提升路由系统的灵活性和安全性。

二、基本原理

Vue Router 4.x的路由匹配机制发生了重大变化,其核心改进包括:

  1. 参数化通配符:将原本的通配符*改为必须定义参数并使用正则表达式
  2. 更精确的路由匹配:通过正则表达式控制路径匹配的边界条件
  3. 安全性增强:防止路径遍历漏洞(如../)的潜在风险

在Vue Router 2.x中,通配符路由的实现原理是:

{
  path: '*',
  component: NotFound
}

当所有其他路由都未匹配时,会自动匹配到这个通配符路由。但在Vue Router 4.x中,这种用法已被废弃,取而代之的是:

{
  path: '/:pathMatch(.*)*',
  component: NotFound
}

三、环境准备

确保你的开发环境满足以下条件:

  1. Vue 3.x
  2. Vue Router 4.x
  3. Node.js 14+
  4. 项目结构示例:
src/
├── App.vue
├── main.js
└── router/
    └── index.js

四、核心实现

1. 基础通配符配置

// src/router/index.js
import { createRouter, createWebHistory } from 'vue-router'
import NotFound from '../views/NotFound.vue'

const routes = [
  {
    path: '/:pathMatch(.*)*',
    name: 'NotFound',
    component: NotFound
  }
]

export default createRouter({
  history: createWebHistory(),
  routes
})

关键代码解释:

  • :pathMatch:定义一个参数名为pathMatch的捕获参数
  • (.*)*:正则表达式,.*匹配任意字符(除换行符),*表示重复0次或多次
  • 这种写法确保只有当所有其他路由都未匹配时,才会触发404页面

2. 带参数的通配符路由

{
  path: '/:pathMatch(.*)*',
  name: 'NotFound',
  component: NotFound,
  props: (route) => ({
    pathMatch: route.params.pathMatch
  })
}

关键代码解释:

  • props配置允许将捕获的参数传递给组件
  • 可通过this.$route.params.pathMatch访问参数值

3. 自定义正则表达式路由

{
  path: '/:pathMatch(^(?!/api/).*)*',
  name: 'NotFound',
  component: NotFound
}

关键代码解释:

  • 正则表达式^(?!/api/).*$表示:

    • ^:匹配字符串开头
    • (?!/api/):负向预查,确保不以/api/开头
    • .*:匹配任意字符
    • *:重复0次或多次
  • 这种写法可以防止匹配到API接口路径

五、完整案例

创建一个完整的Vue3项目,实现动态路由匹配和404页面:

npx create-vue my-project
cd my-project
npm install

src/router/index.js

import { createRouter, createWebHistory } from 'vue-router'
import Home from '../views/Home.vue'
import About from '../views/About.vue'
import NotFound from '../views/NotFound.vue'

const routes = [
  {
    path: '/',
    name: 'Home',
    component: Home
  },
  {
    path: '/about',
    name: 'About',
    component: About
  },
  {
    path: '/:pathMatch(^(?!/api/).*)*',
    name: 'NotFound',
    component: NotFound
  }
]

export default createRouter({
  history: createWebHistory(),
  routes
})

src/views/NotFound.vue

<template>
  <div class="not-found">
    <h1>404 - 页面不存在</h1>
    <p>当前访问的路径:{{ pathMatch }}</p>
  </div>
</template>

<script>
export default {
  props: ['pathMatch']
}
</script>

<style scoped>
.not-found {
  padding: 30px;
  text-align: center;
  color: #888;
}
</style>

src/main.js

import { createApp } from 'vue'
import App from './App.vue'
import router from './router'

createApp(App).use(router).mount('#app')

六、源码解析

Vue Router 4.x的路由匹配逻辑核心在于createRouter函数的实现。在createRouter中,会创建一个history对象,并注册onBeforeRouteUpdate和onBeforeRouteLeave钩子。

关键代码片段(简化版):

function createRouter(options) {
  const history = createWebHistory()
  const routes = options.routes

  function matchRoute(path) {
    for (const route of routes) {
      const { path: routePath, regex } = route
      if (regex.test(path)) {
        return route
      }
    }
    return null
  }

  return {
    history,
    matchRoute
  }
}

在matchRoute函数中,通过正则表达式进行路径匹配。当匹配到通配符路由时,会返回对应的组件。

七、进阶使用

1. 动态路由参数捕获

{
  path: '/:id/:name*',
  name: 'User',
  component: User
}

这个路由可以匹配/123、/123/John等路径,*表示可选捕获参数。

2. 通配符路由的优先级控制

{
  path: '/:pathMatch(^(?!/api/).*)*',
  name: 'NotFound',
  component: NotFound
}

通过正则表达式控制匹配优先级,避免误匹配到API路径。

3. 多级通配符路由

{
  path: '/:pathMatch(^(?!/api/).*)*',
  name: 'NotFound',
  component: NotFound
}

这种写法可以匹配多级路径,如/user/123/profile。

八、性能与工程实践

1. 性能优化

  • 避免使用过于宽泛的正则表达式
  • 对关键路由使用exact匹配
  • 使用v-pre指令避免Vue的编译干扰

2. 安全风险

  • 使用正则表达式防止路径遍历攻击:

    {
      path: '/:pathMatch(^[^/]+/[^/]+)*',
      name: 'NotFound',
      component: NotFound
    }
  • 限制路径深度:

    {
      path: '/:pathMatch(^[^/]{1,20}/[^/]{1,20})*',
      name: 'NotFound',
      component: NotFound
    }

3. 异常处理

{
  path: '/:pathMatch(^[^/]+/[^/]+)*',
  name: 'NotFound',
  component: NotFound,
  beforeEnter: (to, from, next) => {
    console.log('Invalid route:', to.fullPath)
    next()
  }
}

九、常见问题与踩坑

1. 通配符匹配不生效

错误示例:

{
  path: '*',
  name: 'NotFound',
  component: NotFound
}

解决方法:

  • 使用参数化通配符:

    {
      path: '/:pathMatch(.*)*',
      name: 'NotFound',
      component: NotFound
    }

2. 参数无法获取

错误示例:

{
  path: '/:pathMatch(.*)*',
  name: 'NotFound',
  component: NotFound
}

解决方法:

  • 在组件中使用this.$route.params.pathMatch获取参数
  • 使用props传递参数:

    props: (route) => ({
      pathMatch: route.params.pathMatch
    })

3. 正则表达式错误

错误示例:

{
  path: '/:pathMatch(.*)*',
  name: 'NotFound',
  component: NotFound
}

解决方法:

  • 使用更精确的正则表达式:

    {
      path: '/:pathMatch(^(?!/api/).*)*',
      name: 'NotFound',
      component: NotFound
    }

十、最佳实践

  1. 优先使用参数化通配符:始终使用/:pathMatch(正则表达式)*格式
  2. 限制路径深度:使用正则表达式限制路径长度
  3. 安全过滤:使用正则表达式防止路径遍历攻击
  4. 明确优先级:在路由配置文件中明确通配符路由的位置
  5. 使用props传递参数:通过props将参数传递给组件
  6. 异常处理:在路由配置中添加beforeEnter钩子处理异常情况

十一、总结

Vue3中404页面显示问题的核心在于对通配符路由的重新设计。通过参数化通配符和正则表达式,我们可以获得更精确的路由控制能力。在实际开发中,需要根据具体场景选择合适的正则表达式,既要保证路由匹配的准确性,又要防范潜在的安全风险。通过合理配置通配符路由,可以有效提升应用的健壮性和可维护性。

评论已关闭

推荐阅读

AIGC实战——Transformer模型
2024年12月01日
Socket TCP 和 UDP 编程基础(Python)
2024年11月30日
python , tcp , udp
如何使用 ChatGPT 进行学术润色?你需要这些指令
2024年12月01日
AI
最新 Python 调用 OpenAi 详细教程实现问答、图像合成、图像理解、语音合成、语音识别(详细教程)
2024年11月24日
ChatGPT 和 DALL·E 2 配合生成故事绘本
2024年12月01日
omegaconf,一个超强的 Python 库!
2024年11月24日
【视觉AIGC识别】误差特征、人脸伪造检测、其他类型假图检测
2024年12月01日
[超级详细]如何在深度学习训练模型过程中使用 GPU 加速
2024年11月29日
Python 物理引擎pymunk最完整教程
2024年11月27日
MediaPipe 人体姿态与手指关键点检测教程
2024年11月27日
深入了解 Taipy:Python 打造 Web 应用的全面教程
2024年11月26日
基于Transformer的时间序列预测模型
2024年11月25日
Python在金融大数据分析中的AI应用(股价分析、量化交易)实战
2024年11月25日
AIGC Gradio系列学习教程之Components
2024年12月01日
Python3 `asyncio` — 异步 I/O,事件循环和并发工具
2024年11月30日
llama-factory SFT系列教程:大模型在自定义数据集 LoRA 训练与部署
2024年12月01日
Python 多线程和多进程用法
2024年11月24日
Python socket详解,全网最全教程
2024年11月27日
python之plot()和subplot()画图
2024年11月26日
理解 DALL·E 2、Stable Diffusion 和 Midjourney 工作原理
2024年12月01日