VueRouter@4 路由的匹配语法(自定义正则)

简介

我在学习 vue-router@4 动态路由中自定义正则匹配规则的时候遇到了几处误区,以我"钻牛角尖"的性格,查阅相关文档后,初见其中原理,特写下这篇文章。本篇文章默认读者已初步了解动态路由及正则表达式的基本知识。

动态路由参数

以下面路由为例,当路径出现 /:[name] 时,表明该路由是动态路由,将匹配以/开头的任意路由,如 /a/dasdas 等。在模板中通过 $route.params.pathName 可以获取动态参数。

js 复制代码
const routes = [
    {
        // 动态字段以冒号开始,对应路径的参数将赋值给pathName
        // 如路径 /abcd, 则pathName的值为abcd
        path: '/:pathName' 
    }
]

由于动态路由在匹配的时候不区分动态参数名,因此以路径 /abcd 访问以下路由的时候,匹配的永远是第一条路由。

js 复制代码
const routes = [
    {
        path: '/:pathName1' 
    },
    {
        path: '/:pathName2' 
    }
]

若要加以区分,则可添加路径前缀,如下所示。

js 复制代码
const routes = [
    {
        // 匹配 /a/abcd
        path: '/a/:pathName1' 
    },
    {
        // 匹配 /b/abcd
        path: '/b/:pathName2' 
    }
]

但这样就平白多出了路由前缀,不够简洁,因此引出了自定义正则。

在参数中自定义正则

若定义的路由出现 :[name],则在底层中会将其转化为正则表达式 ([^/]+) (匹配至少一个不是/的字符),如下。

当出现路由 /abcd ,则该路由就会与转化的正则相匹配,然后根据动态参数的位置,将对应位置的动态参数abcd赋值给pathName。

js 复制代码
const routes = [
    {
        // 底层转化为  /^\/([^/]+)/i,注意表达式并非完整,只是便于观看
        path: '/:pathName'   // 模板中$route.params.pathName -> abcd
    }
]

由上面例子所知,如果有多条一级的动态路由,我们又不想添加路由前缀,则可以使用自定义正则,自定义正则的形式为/:pathName(正则),通过在动态路径后添加括号,在括号中为参数指定一个自定义的正则,指定的自定义正则将会替代默认的 ([^/]+), 如下所示。

js 复制代码
const routes = [
    {
        // 底层转化为 /^\/(\d+)/i,注意表达式并非完整,只是便于观看
        path: '/:pathName(\\d+)'   // 仅匹配数字
    },
    {
        // 底层转化为 /^\/(.*)/i,注意表达式并非完整,只是便于观看
        path: '/:pathName(.*)'   // 匹配任意字符
    },
    {
        // 底层转化为 /^\/([^/]+)/i,注意表达式并非完整,只是便于观看
        path: '/:pathName'   // 匹配至少有一个不是斜杠的字符
    }
]

TIP

确保转义反斜杠( \ ) ,就像我们对 \d ( 变成 \\d )所做的那样,在 JavaScript 中实际传递字符串中的反斜杠字符。

可重复参数

到这里我们就基本了解了什么是自定义正则了,但是我们在写下动态路由时,在底层中转化的正则表达式并非是全局匹配的,这也是为什么在上面的正则中出现i的原因。

从下面的例子看,我们以/assdf访问的时候,可以正确的匹配到 /:pathName ,但我们以 /asss/sss访问的时候,则无法匹配到 /:pathName 了,

js 复制代码
const routes = [
    {
        // 底层转化为 /^\/([^/]+)/i,注意表达式并非完整,只是便于观看
        path: '/:pathName'   // 匹配至少有一个不是斜杠的字符
        // /asss/sss 当出现了另一个斜杠,则匹配终止了,因此整个表达式不匹配
    }
]

为了匹配具有多个部分的路由,则需要使用到自定义正则的重复参数。 如 /abcd/ab, 我们需要用 *(0 个或多个)和 +(1 个或多个)将参数标记为可重复:

js 复制代码
const routes = [
    {
        // 底层转化为 /^\/((?:[^/]+?)(?:\/(?:[^/]+?))*)\/?$/i  完整表达式
        path: '/:pathName+'   // 多部分匹配至少有一个不是斜杠的字符(1个或多个)
                              // 匹配 /a,/a/b/c 。。。
    },
    {
        // 底层转化为 /^(?:\/((?:[^/]+?)(?:\/(?:[^/]+?))*))?\/?$/i 完整表达式
        path: '/:pathName*'   // 多部分匹配至少有一个不是斜杠的字符(0个或多个)
                              // 匹配 / ,/a,/a/b/c 。。。
    }
]

TIP

  1. 正则表达式中 (?:x)为非捕获括号,如果表达式是 /foo{1,2}/{1,2} 将只应用于 'foo' 的最后一个字符 'o'。如果使用非捕获括号,则 {1,2} 会应用于整个 'foo' 单词。

  2. ? 如果紧跟在任何量词 *+?{} 的后面 ,将会使量词变为非贪婪 (匹配尽量少的字符),和缺省使用的贪婪模式 (匹配尽可能多的字符)正好相反。例如,对 "123abc" 使用 /\d+/ 将会匹配 "123",而使用 /\d+?/ 则只会匹配到 "1"。如果不是跟着量词后面的话,则表示为0次或1次。

详情请移步MDN查阅:(developer.mozilla.org/zh-CN/docs/...)

当采用重复参数后,动态参数则以数组的形式接受多部分动态参数,如下:

js 复制代码
const routes = [
    {
        // 底层转化为 /^\/((?:[^/]+?)(?:\/(?:[^/]+?))*)\/?$/i  完整表达式
        // 匹配   /a/b/c
        path: '/:pathName*'   // $route.params.pathName -> ['a','b','c']
    }
]

/:path* 、/:path(.*)与 /:path(.*)*

好了,到了"钻牛角尖"的时候了,请问上面小标题的三个例子有什么区别? 如果看完这篇文章的话那么心里大致就有了答案了。

  • /:pathName* 是多部分匹配动态参数,若访问路径为 /a/b/c ,则获取到的参数为 ['a','b','c'] ;若访问路径为 /a//// ,则无法正常匹配。
  • /:pathName(.*) 是自定义正则,用 (.*) 去替换 ([^/]+),因此匹配的字符是包括/的,所以哪怕路径是 /ab//b//, 那么也是可以成功匹配的,得到的参数为 ['ab//b//']
  • /:pathName(.*)* 同理,也是可以匹配到 /ab//b//,但是匹配的是多部分的,区别就在于,匹配得到的参数为 [ "ab", "", "b", "", "" ]

这也是为什么官方推荐用 /:pathName(.*)* 来捕获404路由,这可以极大限度地来防止 "老六" 乱输路径。

js 复制代码
const routes = [
    {
        // 底层转化为 /^\/((?:[^/]+?)(?:\/(?:[^/]+?))*)\/?$/i  完整表达式
        path: '/:pathName*'
    },
    {
        // 底层转化为 /^\/(.*)\/?$/i 完整表达式
        path: '/:pathName(.*)'
    },
    {
        // 底层转化为 /^(?:\/((?:.*)(?:\/(?:.*))*))?\/?$/i 完整表达式
        path: '/:pathName(.*)*'
    }
]

相关参考

  1. 本篇文章针对路径的正则转化均来自官方推荐网站:Vue Router Path Parser (esm.dev)
  2. 正则相关知识:正则表达式 - JavaScript | MDN (mozilla.org)
  3. VueRouter官方文档:路由的匹配语法 | Vue Router (vuejs.org)
相关推荐
_codeOH14 小时前
Vue 3 vs React 19:框架还在卷,核心原理就这些
前端·vue.js
英勇无比的消炎药15 小时前
新手必看玩转TinyRobot一定要避开这些坑
前端·vue.js
英勇无比的消炎药15 小时前
别再盲目混用AI组件库和传统组件库差距原来这么大
前端·vue.js
英勇无比的消炎药17 小时前
前端提效神器全新AI组件库TinyRobot改写日常开发模式
前端·vue.js
英勇无比的消炎药17 小时前
前端提效神器TinyRobot
前端·vue.js
CDwenhuohuo17 小时前
uni 背景色渐变 全屏
前端·javascript·vue.js
爱怪笑的小杰杰17 小时前
Vue 项目交付第三方开发,如何隐藏核心 JS 源码?
前端·javascript·vue.js
小二·18 小时前
Vue 3 组合式 API 进阶实战
前端·javascript·vue.js
rising start19 小时前
九、vue3 组件通信:全场景详解
前端·vue.js·typescript
编程技术手记19 小时前
Vue Scoped CSS 与动态创建 DOM 的兼容性问题
前端·css·vue.js