nuxt - 自动生成动态路由bug

news/2024/7/10 0:33:15 标签: nuxt, vue-router, vue, bug, 源码

公司需要启动一个新的ssr项目,就选用nuxt搭建,搭建的过程也踩过不少的坑,后面有空再写一篇nuxt的坑点和开发注意事项,今天主要讲关于nuxt自动生成router.js的一个bug(可以直接拉到最后有图展示),和通过源码分析找到原因的过程。说实话个人感觉自动生成路由这个功能有点鸡肋这里就不展开为啥鸡肋,直接进入主题。

nuxt有个自动生成路由或者动态路由的功能,这里引用下官网的例子

项目pages下的文件结构:

pages/
--| _slug/
-----| comments.vue
-----| index.vue
--| users/
-----| _id.vue
--| index.vue

将自动生成route

router: {
  routes: [
    {
      name: 'index',
      path: '/',
      component: 'pages/index.vue'
    },
    {
      name: 'users-id',
      path: '/users/:id?',
      component: 'pages/users/_id.vue'
    },
    {
      name: 'slug',
      path: '/:slug',
      component: 'pages/_slug/index.vue'
    },
    {
      name: 'slug-comments',
      path: '/:slug/comments',
      component: 'pages/_slug/comments.vue'
    }
  ]
}

总结就是

  • 路径为/pages/a/_id.vue,自动处理成参数选填的动态路由/pages/a/:id?

  • 路径为/pages/a/_id/index.vue,自动处理成参数必填的动态路由/pages/:id

  • 路径为/pages/a/index.vue,自动处理成普通路由/pages/a

用起来还是十分简单的,后面用着用着发现有bug

pages/
--| order/
-----| index.vue
--| order-detail/
-----| _id.vue

上面目录,按照规则应该生成两个动态路由分别为(为了简便这里省略其他name、component等相关字段)

routes: [    { path: '/order' },    { path: '/order-detail/:id?' }]

但是却生成了

routes: [    { path: '/order' },    { path: '/order-detail/:id' }]

id后面的?不见了,baidu、google都没找到答案,好吧,开始扒源码,项目下载下来,找到了他生成路由的方法,在/packages/utils/src/route.js中的createRoutes方法

export const createRoutes = function createRoutes ({  files,  srcDir,   pagesDir = '',  routeNameSplitter = '-',  supportedExtensions = ['vue', 'js'],  trailingSlash}) {  ...}

这里主要关心files参数,整个流程可以概括成获取pages下的文件目录存到files数组中,传递到当前这个方法中处理成最终生成的route表。比如上面生成files数组值为

[  'pages/order/index.vue',  'pages/order-detail/_id.vue']

nuxt会对他们先做一次处理,生成一个初步的route表,如下:

routes: [  {    path: '/order',    name: 'order'  },  {    path: '/order-detail/:id?',    name: 'order-detail-id'  }]

然后nuxt会在二次处理这个routes。遍历整个刚生成的routes,将包含有index.vue或者index.js的文件路径用/分割,并去掉文件后缀,最后push到routesIndex的二维数组中,像这样(order文件夹包含index.vue所以会被push进去)

[  ['page', 'order', 'index']]

然后把path中带?的route分别在生成paths数组和names数组,生成规则如下:

route.path.split('/')分割成paths数组

['page', 'order-detail', '_id?']

route.name.split('-')分割成names数组

['page', 'order', 'detail', 'id']

然后做第三次处理,循环routes数组,把每一个带?的route再和routesIndex数组的每一项循环做比较,假设routesIndex的循环项为r,找到r中index字符串的索引i,如图代表索引i为2

// r数组, index字符串索引为2,所以i为2['page', 'order', 'index']
// paths数组['page', 'order-detail', '_id?']

如果i值小于paths数组,则执行循环,循环中和names做匹配,如果前i - 1项的内容都相等,则将paths的最后一项?去掉

if (i < paths.length) {  for (let a = 0; a <= i; a++) {          if (a === i) {            paths[a] = paths[a].replace('?', '');          }          // 遇到和names中不相等的项直接中断          if (a < i && names[a] !== r[a]) {            break          }  }}

那么为什么nuxt要做这一步处理呢,因为上面有提到,nuxt需要支持生成带?的动态路由,同时也要支持不带?的动态路由,所以他会先把所有动态路由先变成带?的,然后通过上面那个循环做处理,将和index文件同层级带?的路由去掉?。问题就出在这里,order-detail在names数组中会被拆解成order和detail,导致上面循环不会跳出,所以运行到最后把问号去掉了。

// order-detail对应的names['pages', 'order', 'detail', '_id']
// order-detail对应的paths['pages', 'order-detail', '_id?']
// order对应的routeIndex, index索引为2, 并且index之前的项和names刚好// 一一对应,所以会一直循环到最后,最终将paths[2]的?去掉['pages', 'order', 'index']

贴一下核心的源代码,有兴趣可以去nuxt github上看看

routes.forEach(route => {  // ...省略  // 如果是带?的route和routesIndex做比较  if (route.path.includes('?')) {    routesIndex.forEach(r => {      // 找到index的索引i      const i = r.indexOf('index');      if (i < paths.length) {        for (let a = 0; a <= i; a++) {          // 循环到最后一项,清除?          if (a === i) {            paths[a] = paths[a].replace('?', '');          }          // 遇到和names中不相等的项直接中断          if (a < i && names[a] !== r[a]) {            break          }        }      }    });  }});

贴两张直观图,这样会引发bug,生成route中的?没了

 

改成这样就不会了

 

所以如何避免呢?这边也总结了两小点

  • 动态路由文件命名需要带有-时,-之前的字符串不要和其他包含index的文件名重复,比如上面order和order-detail,可以命名调整成orders和order-detail。

  • 比较极端,直接重写一份route,从nuxt.config.js中引入,覆盖掉nuxt自己生成的routes。

结语:

后面有空给nuxt团队提个issue


http://www.niftyadmin.cn/n/764533.html

相关文章

docker ip地址_docker安装踩坑

害&#xff0c;docker安装配置花费了我接近两天时间起因是这样的&#xff0c;项目开发需要docker来作为运行容器&#xff0c;第一次项目需求的时候&#xff0c;大佬建议我安装一个docker后续方便统一环境。本来呢&#xff0c;我以为是很简单的一件事&#xff0c;不就是去docker…

基础巩固 - flex宽度为内容宽度

关注公众号&#xff0c;每天都能领外卖红包 当设置标签display: flex则默认标签具有display: block的特性&#xff0c;宽度会根据父元素而撑满&#xff0c;如下 <div class"box-parent"> <div class"box">box</div></div> .box…

arcgis python脚本融合卡死_ArcGIS Python编程案例(12)-异常和错误处理

我们将在本章介绍以下案例&#xff1a; 查看Python默认的错误消息 添加Python异常处理语句结构&#xff08;try/except/finally&#xff09; 调用GetMessages()函数获取工具消息 使用严重性级别筛选工具消息 调用GetMessage()返回单个消息 测试并响应特定错误消息 引言 ArcGIS地…

js原型和原型链 - 草履虫都看得懂

关注公众号&#xff0c;每天都能领外卖红包 1、概念 搬自MDN - 实例对象的原型链指向它构造方法的原型。原型使用prototype访问&#xff0c;原型链用__proto__访问&#xff0c;虽然__proto__更大主流浏览器都支持&#xff0c;但MDN更推荐使用以下方法进行原型链读写&#xff0c…

Vue - 项目中如何封装好一个业务组件

前言 在日常业务开发中&#xff0c;我们会经常封装一些业务组件&#xff0c;一个页面中可能就有多个业务组件构成&#xff0c;这里和大家分享一下个人的经验&#xff0c;如何封装好业务组件&#xff0c;便于维护 打个广告&#xff0c;最底下的公众号&#xff0c;可以每天领外…

python数据挖掘面试题_数据挖掘150道笔试题

作者&#xff1a;白宁超 2016年10月16日13:44:06 摘要&#xff1a;正值找工作之际&#xff0c;数据挖掘150道面试题涵盖很多基础知识点&#xff0c;如果你针对求职提前针对性准备&#xff0c;可以以此为为参照检查自己水平&#xff0c;如果你不为求职&#xff0c;也可以针对这些…

Vue修炼系列教程 - 筑基篇1

前言 准备有空花点时间写一个对小白友好的Vue常用系列文章&#xff0c;同时也希望让看到的有缘人可以更快速的掌握和使用Vue&#xff0c;同时也为我可怜的公众号增加一些关注量(文章末尾会贴出来)。文章会尽量结合平时开发经验给到尽量详细的解答&#xff0c;有问题欢迎评论区留…

异常重试_【最佳实践】程序员如何优雅的进行重试

专注于Java领域优质技术&#xff0c;欢迎关注文章转载&#xff1a;清风学塾 &#xff0c; 作者 弗兰克的猫预计阅读&#xff1a;20分钟0|1说明最近公司在搞活动&#xff0c;需要依赖一个第三方接口&#xff0c;测试阶段并没有什么异常状况&#xff0c;但上线后发现依赖的接口有…