Vue.js 中类别的嵌套路径

Nested route of categories in Vue.js

根据当前所选类别实现嵌套“n”级路线的合适方法是什么?

我想正确访问路由,所以我的 URL 看起来像这样:

  1. /categories
  2. /categories/outfit/
  3. /categories/outfit/jackets/
  4. /categories/outfit/jackets/mountains/
  5. /categories/outfit/jackets/mountains/you_get_the_idea

我有一个这样的类别列表:

const categories = [
  {
    name: 'outfit',
    childs: [
      { name: 'jackets',
        childs: [
          { name: 'mountains' }
        ]
      },
      { name: 'pants', childs: ['many subcategories'] },
      { name: 'boots', childs: ['many subcategories'] }
    ]
  },
  {
    name: 'knives',
    childs: [
      { name: 'hunting' },
      { name: 'souvenirs' },
      { name: 'kitchen' },
      { name: 'skinning' },
      { name: 'throwing' }
    ]
  }
]

我有一个类别主页(顶级):

      <div class="col-6" v-for="category in categories" :key="category.id">
        <div class="block q-pa-md">
          <router-link :to="'/categories/' + category.slug">
            <h4>{{ category.name }}</h4>
          </router-link>
        </div>
      </div>

还有一个嵌套页面供第一级孩子使用:

    <h1>{{ category.name }}</h1>
    <q-img :src="category.image"/>
    <p>{{ category.description }}</p>
    <div class="row q-col-gutter-md">
      <div class="col-md-4 col-xs-6" v-for="subcategory in category.childs" :key="subcategory.id">
        <div class="block q-pa-md">
          <router-link :to="'/categories/' + subcategory.id">
            <h4>{{ subcategory.name }}</h4>
          </router-link>
        </div>
      </div>
    </div>

我将如何做一些重复的嵌套子项?

Dynamic Route Matching feature of Vue router allows to define a dynamic parameter which captures more than one segment of the path by defining your route as /categories/:categoryId+ (router uses path-to-regex匹配)

以这种方式定义时,route as /categories/outfit/jackets/mountains 将有一个 categoryId 参数,其值为 outfit/jackets/mountains,可以将其传递到组件中,轻松解析并使用...

请参阅下面的示例:

Vue.config.devtools = false
Vue.config.productionTip = false

const categories = [{
    name: 'outfit',
    childs: [{
        name: 'jackets',
        childs: [{
          name: 'mountains'
        }]
      },
      {
        name: 'pants',
        childs: [{
          name: 'short'
        }, {
          name: 'long'
        }]
      },
      {
        name: 'boots',
        childs: [{
          name: 'barefoot'
        }, {
          name: 'mountains'
        }]
      }
    ]
  },
  {
    name: 'knives',
    childs: [{
        name: 'hunting'
      },
      {
        name: 'souvenirs'
      },
      {
        name: 'kitchen'
      },
      {
        name: 'skinning'
      },
      {
        name: 'throwing'
      }
    ]
  }
]

const cat = Vue.component('categories', {
  data: function() {
    return {
      categories: categories // in real life, this data is shared for example using Vuex
    }
  },
  template: `
    <div>
      <template v-for="cat in categories">
        <router-link :to="'/categories/'+cat.name" :key="cat.name"> {{ cat.name }} </router-link>
        </br>
      </template>
    </div>
  `
})

const catView = Vue.component('category-view', {
  data: function() {
    return {
      categories: categories // in real life, this data is shared for example using Vuex
    }
  },
  props: ['categoryId'],
  template: `
    <div>
      <hr>
      <router-link :to="parentCategoryPath"> <- Back </router-link>      
      <div>Params: {{ categoryId }}</div>
      <hr>
      <template v-if="categoryDefinition && categoryDefinition.childs">
        <template v-for="cat in categoryDefinition.childs">
          <router-link :to="$route.path+ '/' +cat.name" :key="cat.name"> {{ cat.name }} </router-link>
          </br>
        </template>
      </template>
    </div>  
  `,
  computed: {
    categoryDefinition() {
      let subCategory = this.categories.find(cat => cat.name === this.categoryId[0]);

      for (i = 1; i < this.categoryId.length; i++) {
        subCategory = subCategory.childs.find(cat => cat.name === this.categoryId[i])
      }
      return subCategory
    },
    parentCategoryPath() {
      return '/categories/' + this.categoryId.slice(0, -1).join('/')
    }
  }
})

const router = new VueRouter({
  base: '/js',
  mode: 'history',
  routes: [{
      path: '/',
      redirect: '/categories',
    },
    {
      path: '/categories',
      component: cat,
    },
    {
      path: '/categories/:categoryId+',
      component: catView,
      props: route => ({
        categoryId: route.params.categoryId.split('/')
      })
    }
  ]
})

const vm = new Vue({
  el: '#app',
  router,
  data: function() {
    return {}
  }
})
hr {
  border: none;
  border-top: 3px double #333;
  color: #333;
  overflow: visible;
  text-align: center;
  height: 5px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue-router/3.5.1/vue-router.min.js" integrity="sha512-c5QVsHf336TmWncPySsNK2ncFiVsPEWOiJGDH/Le/5U6q1hU6F5B7ziAgRwCokxjmu4HZBkfWsQg/D/+X3hFww==" crossorigin="anonymous"></script>

<div id="app">
  {{ $route.path }}
  <router-view></router-view>
</div>