Вложенный маршрут категорий в Vue.js

#vue.js #vue-router #nested-lists

#vue.js #vue-маршрутизатор #вложенные списки

Вопрос:

Каков подходящий способ достижения вложенных «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>
 

И есть вложенная страница для 1-го уровня дочерних элементов:

     <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>
 

Как бы мне сделать несколько повторяющихся вложенных дочерних элементов?

Комментарии:

1. Я не уверен, правильно ли я понял ваш вопрос. Вы собираетесь иметь определенный маршрут для каждой отдельной страницы, или вам нужны динамические маршруты? Если это так, взгляните на: router.vuejs.org/guide/essentials /…

2. @ThomasBay — У меня есть вложенный набор категорий, где каждая запись содержит дочерние узлы, если другие записи ссылаются на нее как на «parent_id». Для лучшей читаемости URL-адреса я хотел бы сделать URL-адрес вложенным, чтобы пользователь мог глубже проникнуть в дерево категорий

Ответ №1:

Функция динамического сопоставления маршрутов маршрутизатора Vue позволяет определить динамический параметр, который захватывает более одного сегмента пути, определяя ваш маршрут как /categories/:categoryId (маршрутизатор использует путь к регулярному выражению для сопоставления)

При таком определении 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 amp;amp; 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> 

Комментарии:

1. Я использовал ваш подход с немного измененной логикой, и он работает. Спасибо!