nuxt - 自动生成动态路由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

Nuxt.js中,可以使用`<nuxt-link>`来实现带参数的跳转。具体的写法是在`<nuxt-link>`标签的`to`属性中使用`params`来传递参数。例如: ```html <nuxt-link :to="{ name: 'products', params: { keyword: item.name }}">{{ item.name }}</nuxt-link> ``` 这样就可以将`item.name`作为`keyword`参数传递给名为`products`的路由页面。 接受参数的方法有两种: 1. 在`asyncData(ctx)`方法中获取参数。可以使用`ctx.params.keyword`来获取通过参数传递的值。例如: ```javascript async asyncData(ctx) { console.log(ctx.params.keyword) // 可以获取到通过参数传递的值 } ``` 2. 在`created()`生命周期中获取参数。可以使用`this.$route.params.keyword`来获取通过参数传递的值。例如: ```javascript created() { console.log(this.$route.params.keyword) // 可以获取到通过参数传递的值 } ``` 需要注意的是,`<nuxt-link>`的使用方式与`<router-link>`相同,所以在接受参数方面也可以参考`<router-link>`的用法。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* *3* [nuxt:nuxt-link的动态路由和传参](https://blog.csdn.net/weixin_45844049/article/details/105581675)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 100%"] [ .reference_list ]
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

hhzzcc_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值