К примеру нам нужно реализовать компонент сниппета товара у которого есть ряд требований:
- Его можно распечатать статично с бекенда со всей нужной информацией для SEO и логики
- Его можно использовать как обычный компонент Vue, передавая параметры через v-bind, навешивая события click и т.д.
- Он должен отображать актуальное состояние кнопки купить
- После нажатия на кнопку «Купить», должен появится прелоудер ожидающий статус корзины
- Написать логику для статичных сниппетов, навешивая события click, добавлять и удалять классы load на кнопке «Купить»
- Отдельно написать компонент на Vue реализующий туже логику только в формате шаблонов
- Использовать первый пункт для вывода с бекенда, второй к примеру для вывода чистых данных полученных из состояния или ajax
Таким образом мы напишем две логики, одна будет работать непосредственно с html, другая с чистыми данными.
Мое решение этой проблемы использовать — slots, а именно возможность установить контент по умолчанию, который как раз и будет содержать динамические параметры, но в тоже время если компонент будет использоваться как inline-template, то все эти параметры будут заменены на отрисованные бекендом.
Напишем компонент который сможет обработать и статику и динамику:
<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>
Использование компонента через backend:
<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>
Использование компонента в других компонентах:
<catalog-list> <snippet v-for="item in items" :key="item.id" v-bind="item"></snippet> </catalog-list>
Теперь у нас есть один компонент который можно использовать в разных случаях.
Хотелось бы услышать ваше мнение по поводу такого подхода, возможно есть более удачное решение.
ссылка на оригинал статьи https://habr.com/post/422465/
Добавить комментарий