如何避免创建数百个 files.vue

How do I avoid creating hundreds of files.vue

我正在建立一个包含很多项目的网站。 所以路径是这样的:

example.com/catalog/indoor/fireplace/awesome-one

目前我正在创建成百上千的 Vue 个实例:

AwesomeOne.vue
AnotherOne.vue
YetAnotherThing.vue
...
AhundredthThing.vue

它们内部只有一个过滤数组,并将一个 prop 传递给使用 Vuex 存储中的数据的可重用组件。 所以我在想,有没有办法避免创建这么多 Vue 文件?我有点用 Nuxt.js 对问题进行了排序,但它只会自动生成使开发稍微容易一些的路由。但是我仍然需要创建很多内部代码几乎相同的文件(实际上只有 3 个字的区别:计算的名称 属性、过滤器选项和道具的名称)。

我正在考虑一些计算 属性,它会使用来自 Vuex.state 的数据动态分配一个 URL。但是我不能把东西放在一起。

根据要求发布代码: Catalog.vue

<template>
  <v-main>
    <v-row>
      <v-col
        v-for="category in categories"
        :key="category.i"
      >
        <v-card
          router
          :to="category.link"
        >
          <v-img
            height="250"
            width="250"
            :src="category.img"
          />
          <v-card-title>
            {{ category.name }}
          </v-card-title>
        </v-card>
      </v-col>
    </v-row>
  </v-main>
</template>

<script>
export default {
computed: {
  categories () {
    return this.$store.state.categories
  }
}
}
</script>

CatalogCategories.vue(有 5 个)

<template>
  <v-sheet>
    <h1>Изделия для дома</h1>
    <v-row>
      <v-col
        v-for="indoorItem in indoorItems"
        :key="indoorItem.i"
      >
        <a :href="indoorItem.link">{{ indoorItem.name }}</a>
      </v-col>
    </v-row>
  </v-sheet>
</template>

<script>
export default {
  computed: {
    subCategories () {
      return this.$store.state.subCategories
    },
    indoorItems () {
      return this.subCategories.filter((category) => category.type === "indoor" || category.type === "bothdoor");
    },    
  }

}
</script>

Banquet.vue(类别之一,超过20个)

<template>
  <v-sheet>
    <h1>Банкетки</h1>
    <item-view :items="this.banquet" />
  </v-sheet>
</template>

<script>
import ItemView from "../../../components/ItemView.vue"
export default {

components: { ItemView },
computed: {
  items () {
    return this.$store.state.items
  },
  banquet () {
      return this.items.filter((item) => item.type === "banquet");
    },    
}
}
</script>

ItemView.Vue(可重用组件)

<template>
  <v-sheet>
    <v-row>
      <v-col
        v-for="item in items"
        :key="item.i"
      >
        <v-card
          route
          :to="item.path"
        >
          <v-img
            width="250"
            height="250"
            :src="item.img"
          />
          <v-card-title> {{ item.name }} </v-card-title>
          <v-card-subtitle> Цена: {{ item.price }} грамм конфет му-му </v-card-subtitle>
        </v-card>
      </v-col>
    </v-row>
  </v-sheet>
</template>

<script>
export default {
    props: {
        items: {
        type: Array,
        required: true,
        },
    },
        
    }


</script>

IndividualItem.vue(我必须创建超过 100 个)

<template>
  <v-sheet>
    <v-card>
      <individual-item-view :items="this.obossana" />
      <v-card />
    </v-card>
  </v-sheet>
</template>

<script>
import IndividualItemView from '../../../../components/IndividualItemView.vue'
export default {
  components: { IndividualItemView },
    
    computed: {
  items () {
    return this.$store.state.items
  },
  obossana () {
      return this.items.filter((item) => item.title === "obossana");
    },  

}
}
</script>

<style>

</style>

IndividualItemView.vue(可重用组件)

<template>
  <v-sheet>
    <v-card
      v-for="item in items"
      :key="item.i"
    >
      <v-card-title> {{ item.name }} </v-card-title>
      
      <expandable-image
        class="image"
        close-on-background-click       
        :src="item.img"
      />
      <v-card-subtitle> Цена: {{ item.price }} раз послать тебя ко всем чертям </v-card-subtitle>
      <v-card-text> {{ item.description }} </v-card-text>
    </v-card>
  </v-sheet>
</template>

<script>

export default {
    mounted() {
    const viewportMeta = document.createElement('meta');
    viewportMeta.name = 'viewport';
    viewportMeta.content = 'width=device-width, initial-scale=1';
    document.head.appendChild(viewportMeta);
  },
props: {
        items: {
        type: Array,
        required: true,
        },
    },
}
</script>

<style scoped>
.image {
  width: 400px;
  max-width: 100%;
}
</style>

还有来自 Vuex 商店的小片段:

 {
        name: "Обоссаная банкетка",
        title: "obossana",
        type: "banquet",
        path: "/catalog/indoor/banquet/obossana",
        price: "215361",
        img:
          "https://b2.3ddd.ru/media/cache/tuk_model_custom_filter_ru/model_images/0000/0000/0079/79546.53239b3804d0a.jpeg",
        description: "blah blah blah",
      },

你已经很接近了,有几种不同的方法可以解决这个问题。我将展示最简单的 IMO 方法,即继续使用 props 并将您拥有的硬编码数据推送到组件层次结构的更上一层楼。

我将使用 IndividualItem.vue 作为示例,因为它是迄今为止最明显的应用方法:

<template>
  <v-sheet>
    <v-card>
      <individual-item-view :items="this.items" />
      <v-card />
    </v-card>
  </v-sheet>
</template>

<script>
import IndividualItemView from "../../../../components/IndividualItemView.vue";
export default {
  components: { IndividualItemView },

  // Add a prop for the title
  props: {
    title: {
      type: String,
      required: true,
    },
  },

  // Filter only store items with that title
  computed: {
    items() {
      return this.$store.state.items.filter(
        (item) => item.title === this.title
      );
    },
  },
};
</script>

要使用它,您只需执行以下操作:

<IndividualItem title="obossana" />

以这种方式使用道具实际上允许您创建和使用可重用的组件。还有各种不同的策略,你可以申请传递道具。

例如,您可以传递 items,而不是传递 title,这将使 IndividualItem 组件的用户更好地控制他们想要的项目显示。

这里也有一个快速演示:

<template>
  <v-sheet>
    <v-card>
      <individual-item-view :items="this.items" />
      <v-card />
    </v-card>
  </v-sheet>
</template>

<script>
import IndividualItemView from "../../../../components/IndividualItemView.vue";
export default {
  components: { IndividualItemView },

  // Any items may be passed in, allowing the user to group
  // different items together however they need.
  props: {
    items: {
      type: Array,
      default: () => [],
    },
  },
};
</script>

然后,组件的用户可能会这样做(注意:您可以创建 obossanaItems 完全相同的方式,只是在父组件范围内):

<IndividualItem :items="obossanaItems" />

作为最后一条结束建议,这可能是您遗漏的一条,几乎所有硬编码数据(如 title: obossana)都应该直接来自您的页面组件,或者您的 API,这最终允许您创建可重用的组件。

例如,负责呈现页面的页面组件 example.com/catalog/indoor/fireplace/awesome-one 应该负责为其所有子组件提供呈现正确数据所需的信息。

这不是唯一的方法,但绝对是最容易上手的方法,也是我推荐的方法,而不是为每个类别、子类别等创建特定的组件。

正如我在本文开头提到的post,你已经很接近了,你只需要继续将具体的数据项抽象到父组件。 Vue 和 Nuxt 的其他功能,如 Vuex getters,也可以帮助使特定的过滤调用也可重用,但这将进入另一个主题。

好的,我明白了。

_view.vue 单个项目视图:

<script>
export default {
  async asyncData() {
    const items = await fetch(
      'API URL here'
    ).then((res) => res.json())

    return { items }
  },
  data() {
    return {
      params: this.$route.params.view,
      title: '',
    }
  },
  computed: {
    filteredItems() {
      return this.items.filter((item) => item.title === this.$route.params.view)
    },
  },
}
</script>

事实证明这很容易。我所要做的就是获取硬编码数据(我决定使用 API 因为将来我将不得不添加更多项目,我相信如果我使用 API 会更容易)并过滤 item.title 匹配路由 $route.params.view

template 标签(_view.vue 文件)内我有:

<template>
  <v-main>
    <h1>API: items</h1>
    <h2>'/catalog/_subcategory/_individualitem/_view.vue' here</h2>
    <h3>params.view: {{ params }}</h3>
    <v-card v-for="item in filteredItems" :key="item.i">
      <v-avatar size="200">
        <img :src="item.img" alt="картинка предмета" />
      </v-avatar>
      <v-card-title>
        {{ item.name }} and item.title: {{ item.title }}
      </v-card-title>
      <v-card-subtitle>Цена: {{ item.price }} сантиметров</v-card-subtitle>
      <v-card-text> {{ item.description }} </v-card-text>
    </v-card>
  </v-main>
</template>

h1h2h3 标签帮助我找出(通过显示)变量的值。

文件树是(根是目录本身):

├── index.vue
├── _subcategory
│   ├── _individualitem
│   │   └── _view.vue
│   └── _items.vue
└── _type.vue

还有一点点 api.json:

{
    "title": "stremnaya-arka",
    "path": "/catalog/outdoor/arcs/stremnaya-arka",
    "name": "Стрёмная арка",
    "type": "arcs",    
}

文件名可能有点混乱,但我希望不会。

我希望它能帮助其他和我一样挣扎的新手 ;)