Usando slots no Vue como um exemplo de snippet de produto

Ao trabalhar com projetos nos quais o SSR (Server Side Rendering) não é usado ou não é possível implementá-lo, surge o problema de que algumas funções ou lógica são gravadas duas vezes para elementos estáticos que imprimem back-end e para componentes que processam o Vue.

Por exemplo, precisamos implementar um componente de snippet de produto que possua vários requisitos:

  • Ele pode ser impresso estaticamente a partir do back-end com todas as informações necessárias para SEO e lógica.
  • Ele pode ser usado como um componente regular do Vue, passando parâmetros através do v-bind, anexando eventos de clique etc.
  • Ele deve exibir o estado atual do botão de compra
  • Depois de clicar no botão "Comprar", um pré-carregador deve aparecer aguardando o status da cesta

Uma solução:

  1. Escreva lógica para trechos estáticos anexando eventos de clique, adicione e remova classes de carregamento no botão Comprar
  2. Escreva separadamente um componente no Vue que implemente a mesma lógica apenas no formato do modelo
  3. Use o primeiro item para saída do back-end, o segundo, por exemplo, para saída de dados limpos obtidos do estado ou do ajax

Assim, escreveremos duas lógicas, uma trabalhará diretamente com o DOM, a outra com dados puros.

Minha solução para esse problema é usar slots, ou seja, a capacidade de definir o conteúdo padrão, que conterá parâmetros dinâmicos, mas, ao mesmo tempo, se o componente for usado como um modelo embutido, todos esses parâmetros serão substituídos pelos desenhados pelo back-end.

Vamos escrever um componente que possa lidar com estática e dinâmica:

Listagem de componentes
<template> <div> <!--   image   ,       --> <slot name="image"> <!--    --> <a :href="url" class="snippet__image"> <img :src="image"> </a> </slot> <slot name="title"> <a :href="url" class="snippet__title">{{ title }}</a> </slot> <div v-if="!inCart" @click="add" :class="{ 'snippet__buy--load': load }" class="snippet__control" > <slot name="button"> <div class="snippet__button"></div> </slot> </div> <div v-if="inCart" class="snippet__control"> <div class="snippet__button"></div> </div> <div v-if="load" class="snippet__load"></div> </div> </template> <script> //  vuex     import { mapState, mapActions } from 'vuex' export default { props: { id: { type: Number, required: true }, url: { type: String }, image: { type: String }, title: { type: String } }, data() { return { //   load: false, //     inCart: false, } }, computed: { ...mapState({ //     cartItems: ({cart}) => cart.items }), }, mounted() { this.$nextTick(() => { //      this.inCart = this.cartItems.some(item => item.id === this.id) }) }, methods: { ...mapActions([ //     'addToCart' ]), add() { //    this.load = true //   this.addToCart({ id: this.id }) } }, watch: { //      cartItems(items) { //    this.load = false //      this.inCart = items.some(item => item.id === this.id) } } } </script> 


Usando um componente via back-end:

 <snippet :id="1" class="snippet"> <a slot="image" href="#" class="snippet__image"> <img src="photo.jpg"> </a> <a slot="title" href="#" class="snippet__title"> 1</a> <div slot="button" class="snippet__button"></div> </snippet> 

Usando um componente em outros componentes:

 <catalog-list> <snippet v-for="item in items" :key="item.id" v-bind="item"></snippet> </catalog-list> 

Agora, temos um componente que pode ser usado em diferentes casos.

Gostaria de ouvir sua opinião sobre essa abordagem, talvez haja uma solução melhor.

Source: https://habr.com/ru/post/pt422465/


All Articles