От автора: привет, ребята! Сегодня хочу поделиться с вами небольшой, но полезной статьей про создание компонента Vue. В моем текущем проекте у меня много многоразовых компонентов (я назвал их «виджетами»), которые можно разместить где угодно. И у меня возникла проблема с адаптивным дизайном. Наконец, я понял, что css @media запросы абсолютно не подходят под мой случай.
Так в чем проблема?
Представьте, что у нас есть компонент PostsItem . У нас есть PostsPage со списком постов:
Всего несколько строк CSS, не так ли?
1 2 3 4 5 6 7 |
.post__item { display: flex; } .post__image { flex: 0 0 200px; height: 200px; } |
И мы также хотим, чтобы в мобильном представлении тоже все было хорошо. Это может выглядеть так:
Вы можете сказать «легко», @media решает эту проблему, не так ли?
1 2 3 4 5 6 7 8 9 10 |
@media only screen and (max-width: 576px) { .post__item { flex-direction: column; } .post__image { flex: 0 auto; height: auto; } } |
Хорошо, способ работает. Но что, если мы будем использовать наш компонент PostsItem в другом месте?
О, бедные наши глаза. У нас большой экран, но мы не ожидали увидеть 3 поста подряд.
Итак, самая большая проблема @media запросов: «Адаптивность компонента зависит от размера экрана, а должна полагаться на собственные размеры»
vНо в этом случае компоновка компонентов зависит только от них самих. Эти компоненты должны быть атомарными, независимо определять свой собственный размер и адаптировать макет под себя.
Нам нужны локальные адаптивные стили. И здесь нам поможет ResizeObserver!
ResizeObserver
ResizeObserver — это новая функция, которая позволяет получать уведомления, когда прямоугольник содержимого элемента изменил свой размер, и реагирует на это соответствующим образом.
Использовать его очень просто:
1 2 3 4 5 6 7 8 9 10 |
const observer = new ResizeObserver(entries => { entries.forEach(entry => { const cr = entry.contentRect; console.log('Element:', entry.target); console.log(`Element size: ${cr.width}px x ${cr.height}px`); console.log(`Element padding: ${cr.top}px ; ${cr.left}px`); }) }) observer.observe(someElement) |
Вы можете сказать, что у него плохая поддержка в браузерах:
Но, к счастью, у ResizeObserver есть polyfill, основанный на MutationObserver:
Использование с Vue
Я сделал небольшую обертку ResizeObserver для Vue.js (также работает на Nuxt.js ), которая позволяет легко работать с адаптивными компонентами:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
<template> <Responsive :breakpoints="{ small: el => el.width <= 500 }"> <div slot-scope="el" :class="['post__item', { small: el.is.small }]"> <img class="post__image" :src="post.image" /> <div class="post__text">{{post.text}}</div> </div> </Responsive> </template> <script> import { Responsive } from "vue-responsive-components" export default { props: ['post'], components: { Responsive } } </script> <style lang="scss"> .post__item { display: flex; } .post__image { flex: 0 0 200px; height: 200px; } .post__item.small { flex-direction: column; .post__image { flex: 0 auto; height: auto; } } </style> |
И теперь макет выглядит отлично, даже когда мы помещаем 3 поста подряд:
Теперь наш компонент действительно независим! Это также дает возможность определять другую разметку html, а не только CSS. Например, я добавил вкладки и «доп. маленькое» представление поста для небольшого блока:
Кроме того, хочу отметить, что полностью удалил все @media запросы из текущего проекта в пользу ResizeObserver.
UPD: бонус: директива v-responsive
Я добавил v-responsive директиву, чтобы избавиться от компонента-оболочки:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
<template> <!-- Will add/remove .small if the width is less / greater --> <div class="post__item" v-responsive="{ small: el => el.width <= 500 }"> <img class="post__image" :src="post.image" /> <div class="post__text">{{post.text}}</div> </div> </template> <script> import { ResponsiveDirective } from "vue-responsive-components" export default { props: ["post"], directives: { responsive: ResponsiveDirective } } </script> |
Автор: Anton Kosykh
Источник: //itnext.io/
Редакция: Команда webformyself.