Создание приложения
В этой статье мы рассмотрим шаги создания упрощённого клона Instagram с возможностью добавлять посты, фильтры фото и лентой постов, все как в настоящем Instagram!** Вот, что должно получиться в конце:
В приложении можно загрузить изображение, кликнув по иконке “квадрат” в футере. После загрузки изображения его можно отредактировать, используя разные фильтры, а также можно разделять текст на блоки.

В этом уроке рассмотрим созданиe пользовательского интерфейса приложения с помощью Vue.js, сосредоточимся на настройке интерфейса UI, никаких взаимодействий с сервером не планируется, будем редатрировать только фронтенд.
Контекст
Предполагаю, что вы немного знакомы с Vue, если что-то непонятно, то разберетесь в процессе написания кода.
В этом уроке мы поговорим о следующем:
- Развертывание проекта с помощью vue-cli.
- Создание компонентов в формате одного файла single-file.
- Обмен данными и событиями между компонентами.
- Загрузка файлов с помощью API FileReader.
- Редактирование изображений с помощью Instagram-подобных фильтров с использованием библиотеки CSSGram (Автор @Una).
- Перетаскивание прокрутки элементов с помощью библиотеки vue-dragscroll (Автор @don_jon243).
Чтобы сосредоточиться на использовании Vue, мы не будем разбирать стилизацию приложения, просто добавим необходимые стили и вызовем их.
Блоки кода
В процессе создания приложения я буду показывать изменения кода и окончательные файлы. Для показа целых файлов я буду использовать Github gist`ы, когда нужно будет показать какие-то конкретные изменения, то я буду показывать их кусочками файлов:
`
Для этого урока мы создадим с помощью vue-cli заготовку в CodeSandBox, редактор онлайн-кода, ориентированный на прототипирование и развертывание веб-приложений.
Для старта используем некоторый исходный шаблон приложения.
Давайте кратко рассмотрим структуру исходного каталога проекта.
data/
filters.js
posts.js
styles/
app.scss
filter-type.scss
instagram-post.scss
App.vue
index.html
index.js
package.json
Примечание: Редактор CodeSandbox включает файлы конфигурации, необходимые для настройки нашего приложения. Это позволит нам просто сосредоточиться на коде приложения.
data/
Папка data/ содержит данные в приложении, в двух отдельных файлах: filters.js и posts.js.
Файл filters.js ссылается на тип фильтров, который может быть применен к загруженному изображению:
export default [
{ name: "normal" },
{ name: "clarendon" },
{ name: "gingham" },
{ name: "moon" },
{ name: "lark" },
{ name: "reyes" },
{ name: "juno" },
{ name: "slumber" },
{ name: "aden" },
{ name: "perpetua" },
{ name: "mayfair" },
{ name: "rise" },
{ name: "hudson" },
{ name: "valencia" },
{ name: "xpro2" },
{ name: "willow" },
{ name: "lofi" },
{ name: "inkwell" },
{ name: "nashville" }
];
posts.js представляет собой набор объектов данных, которые представляют сообщения, которые уже были отправлены в ленту. Если наше приложение сохраняло данные на сервере, мы, вероятно, сделаем запрос GET на сервер для получения подобной информации:
export default [
{
username: "socleansofreshh",
userImage: "https://s3-us-west-2.amazonaws.com/s.cdpn.io/1211695/me_3.jpg",
postImage:
"https://s3-us-west-2.amazonaws.com/s.cdpn.io/1211695/tropical_beach.jpg",
likes: 36,
hasBeenLiked: false,
caption: "When you're ready for summer '18 ☀️",
filter: "perpetua"
},
{
username: "djirdehh",
userImage: "https://s3-us-west-2.amazonaws.com/s.cdpn.io/1211695/me2.png",
postImage:
"https://s3-us-west-2.amazonaws.com/s.cdpn.io/1211695/downtown.jpg",
likes: 20,
hasBeenLiked: false,
caption: "Views from the six...",
filter: "clarendon"
},
{
username: "puppers",
userImage:
"https://s3-us-west-2.amazonaws.com/s.cdpn.io/1211695/pug_personal.jpg",
postImage:
"https://s3-us-west-2.amazonaws.com/s.cdpn.io/1211695/puppers.jpg",
likes: 49,
hasBeenLiked: false,
caption: "Current mood",
filter: "lofi"
}
];
Каждый объект post содержит свойства, относящиеся к нему, такие как имя, изображение автора поста, загруженное изображение, кол-во лайков и т.д. Кроме того, каждый объект данных будет содержать свойство filter, которое будет определять применение фильтра к изображению поста.
styles/
Папка styles/ содержит все кастомные CSS, необходимые для нашего приложения. Когда мы будем создавать компоненты, мы будем привязывать правильные стили к компоненту и сфокусируемся на использовании Vue.
App.vue
Папка App.vue - это основной родительский компонент, который должен быть отображен из нашего экземпляра Vue. Если мы откроем файл App.vue, мы увидим простой однофайловый компонент:
`
` Однофайловые компоненты - это невероятно полезная фича, которая позволяет нам определять все HTML/CSS и JS компоненты в одном vue** файле.
В нашем файле App.vue компонент ** в данный момент просто отображает простое приветственное сообщение. **
index.html
Файл index.html является корневой страницей разметки нашего приложения:
`
` В *index.html* мы указываем внешние стили для нашего приложения. Мы добавили [Bulma](https://bulma.io/documentation/) в качестве CSS фреймворка, [Font Awesome](https://fontawesome.com/) для иконок и [CSSGram](https://una.im/CSSgram/) для реализации фильтров Instagram.Элемент div с id приложения является элементом DOM, на который будет примонтировано приложение Vue, как это задано в файле index.js.
index.js
Файл index.js* представляет собой отправную точку Vue приложения:
`import Vue from “vue”; import App from “./App”;
/* eslint-disable no-new */ new Vue({ el: “#app”, render: h => h(App) }); ` В верхней части файла index.js мы импортируем библиотеку Vue и компонент App. Затем мы создаем новый экземпляр Vue, объявляя его через new Vue ({…}). Экземпляр Vue является отправной точкой для всех приложений Vue и принимает объект параметров options, содержащий детали экземпляра.
В приведенном выше экземпляре мы указываем элемент DOM с идентификатором приложения, в котором должно быть установлено наше приложение Vue, и объявляем, что компонент App является самым верхним родительским компонентом, который должен быть отрендерен.
Теперь, когда у нас есть представление о том, как реализована структура нашего приложения, мы можем начать создавать приложение.
1) Домашняя страница
Первое, что мы начнем делать - это создавать ленту постов Главной страницы, создав основной компонент приложения App и привязав данные из файла data/posts.js.
На высоком уровне мы можем разбить компоновку будущего приложения на три раздела: шапку (phone-header), тело (phone-body) и подвал (phone-footer).

Большинство функциональных возможностей приложения будут находится в секции phone-body; поэтому было бы целесообразно создать отдельный компонент, который представлял бы этот раздел. Мы создадим этот компонент как PhoneBody.vue в папке components/:
components/
PhoneBody.vue
data/
styles/
...
В файле PhoneBody.vue мы создадим простой однофайловый компонент:
`
` В теге script файла App.vue мы импортируем компонент PhoneBody и определим его свойства в components
`
…
` Мы сопоставили объявление phone-body с объектом компонента PhoneBody. Это позволит нам объявить недавно импортированный компонент в качестве phone-body в шаблоне приложения. Мы сделаем это и, кроме того, создадим элементы, которые представляют разделы шапки (phone-header) и подвала (phone-footer). Наш файл App.vue станет таким:
`
` Мы разместили изображение лого Instagram в phone-header; и иконки Главная и загрузить в phone-footer. На данный момент наше приложение будет выглядеть так:

Прежде чем мы сможем заполнять контент в разделе phone-body, давайте оценим данные, представляющие приложение.
Поскольку наше приложение довольно простое, то мы не будем использовать средства управления состоянием, вместо этого будем направлять все данные в самый верхний компонент приложения для родителей. Детальные компоненты будут более презентабельными и просто будут иметь свои данные в виде параметра, переданного из приложения.
Для начала мы будем импортировать сообщения и фильтровать массивы данных и объявлять их как свойства данных с тем же именем в функции data() компонента App.
` …
` Затем мы можем передавать массивы в posts и filters как параметры, через которые мы объявляем компонент phone-body. Мы будем использовать сокращенный синтаксис для директив v-bind:
`
`
` С массивом posts, доступным в виде параметров в компоненте PhoneBody, мы теперь можем отрендерить список элементов, которые являют собою отправленные посты. Отрендерим список с помощью директивыо Vue v-for.
Так как каждый пост содержит много разметки мы создадим компонент VuegramPost, который будет использоваться директивой v-for для построения списка. В папке components/ мы создадим файл компонента VuegramPost.vue:
components/
VuegramPost.vue
PhoneBody.vue
data/
styles/
Компонент VuegramPost будет шеллом, который выводит свойства объекта единичного объекта post:

Продолжим и создадим компонент VuegramPost в файле VuegramPost.vue:
`
{{post.likes}} likes
{{post.username}} {{post.caption}}
` Тут есть несколько моментов, на которые стоит обратить внимание:
- В script мы заявили, что компонент VuegramPost ожидает параметры объекта post, как показано в требовании проверки параметров (props: {post: Object}).
- Мы связываем параметры объекта post c компонентом шаблона используя синтаксис Mustache: ( {{ }} ) и директиву :v-bind.
- Мы указываем, что стили компонента должны быть получены из стилей vuegram-post.scss.
Теперь нам нужно использовать директиву v-for для рендеринга списка VuegramPost компонентов на основе коллекции данных posts. В файле PhoneBody.vue мы сначала объявим posts и filters параметры, которые передаются нам, чтобы мы могли использовать их в компоненте PhoneBody. Мы сделаем это, указав требование проверки параметра - они оба posts и filters должны быть массивами:
`
` Мы также импортируем компонент VuegramPost и объявим его в свойстве components компонента PhoneBody:
`
` Наконец, мы отобразим список постов в шаблоне и файл PhoneBody.vue обновится до следующего вида:
`
` Поскольку мы итерируем по коллекции posts, то post - это отдельный пост в директиве v-for. В каждый отрендеренный vuegram-post мы также передаем итерированный объект post и его параметры могут быть доступны в компоненте.
Теперь мы можем прокручивать ленту постов в нашем приложении!

Перед продолжением, давайте разберемся какой функционал “лайков” нужно будет добавить для постов Instagram. В реальном Instagram мы можем добавить в избранное щелкнув по иконке сердца под изображением поста или дважды щелкнув это изображение.
У нас есть два параметра в объекте post, связанных с функциональностью “лайков”, которые мы собираемся реализовать:
{
...,
likes: 36,
hasBeenLiked: false,
...,
},
likes - это число, которое мы будем увеличивать на один, когда пользователь «лайкает» пост. Поскольку мы не будем позволять пользователю постоянно увеличивать количество лайков к одному и тому же посту, hasBeenLiked будет той переменной логического типа, которая будет показывать лайкнут ли пост.
В script компонента VuegramPost мы сначала создадим метод like() в свойстве methods(), который будет “лайкать” или отменять “лайк”:
`
В шаблоне теперь мы можем добавить обработчики событий на элементах, которые пользователь будет нажимать, чтобы лайкнуть пост. Мы будем использовать сокращенный синтаксис для директивы v-on и укажем обработчик клика dblclick на изображении поста и обработчик клика click на иконке сердца.
`
С этими изменениями файл VuegramPost.vue будет иметь следующий вид:
`
{{post.likes}} likes
{{post.username}} {{post.caption}}
` Теперь, когда пользователь либо щелкает иконку сердца, либо дважды кликает на изображении, он/она «лайкнет» сообщение:

Ниже приведен пример нашего приложения на этом этапе!
2) Процесс отправки
Когда пользователь начинает процесс отправки, мы хотим изменить UI (пользовательский интерфейс) в зависимости от того, где находится пользователь (например, если пользователь находится на втором этапе, ему/ей должно быть предложено выбрать фильтр, который должен быть применен к изображению). В приложении из реального мира мы, вероятно, захотели бы использовать соответствующую библиотеку маршрутизации, такую как Vue Router, или, по крайней мере, построить настраиваемое решение маршрутизации.
Однако, для нашего приложения мы сделаем что-то гораздо проще. Мы изменим UI приложения на основе параметра step. Когда пользователь находится на step === 1, ему/ей будет показан лента новостей. На step === 2 будет возможность выбрать фильтр, и на этапе step === 3 пользователю будет предложено предоставить описание поста.

Элементы, отображаемые в заголовке (например, ссылки «Cancel» и «Next»), также будут условно отображаться на основе значения step.

В родительском компоненте App давайте сначала представим step свойство и установим его значение равным 1. Поскольку мы будем использовать свойство step, чтобы определять, как будут отображаться элементы в секции phone-body, мы передадим его как параметры для компонента phone-body.
`
В общем, нам нужно захватить выбранный фильтр selectedFilter, который хочет применить пользователь, изображение image, которое они хотели бы загрузить, и подпись к сообщению caption. Учитывая эти свойства, давайте объявим для них пустые начальные значения в компоненте App и передадим их как параметры в phone-body:
`
При инициализации основных свойств мы создадим метод, ответственный за направление пользователя с шага 1 на шаг 2. Одним из действий, ответственных за это, будет загрузка изображения по клику на иконку загрузки в футере (phone-footer):

В App.vue мы создадим новый метод uploadImage(), который будет нести ответственность за загрузку изображения и затем направлять пользователя на шаг 2.
Элемент input type = “file” позволяет пользователям выбирать один или несколько файлов с их устройства для загрузки. Поэтому в разделе футере шаблона App.vue мы создадим элемент ввода (type=“file”), в котором есть слушатель событий, который вызывает метод uploadImage() при срабатывании:
`
Элемент input имеет слушатель событий change, который вызывает метод uploadImage() при срабатывании. Мы создадим этот сопроводительный метод uploadImage() в свойстве methods в script теге файла App.vue. Чтобы загрузить изображения, мы будем использовать API FileReader. Вот метод uploadImage() в целом:
`
- Когда пользователь загружает изображение, объект события имеет список объектов файлов, к которым можно получить доступ из evt.target.files.
- Если файлов нет, мы возвращаемся раньше. Если файлы существуют, мы продолжаем установку new FileReader() в переменную reader.
- FileReader() позволяет асинхронно читать содержимое файлового объекта. Мы используем функцию readAsDataUrl из переменной reader для чтения содержимого загруженного файла (то есть первого файлового объекта file из evt.target.files).
- Когда содержимое файла считывается с readAsDataUrl, запускается обработчик события onload, с помощью которого мы используем для установки параметра компонента image в значение target.result события. Затем мы устанавливаем значение step компонента равным 2.
- Браузер Chrome не запускает событие изменения, если мы решили загрузить одно и то же изображение дважды. Чтобы выполнить небольшое изменение, чтобы обойти это, мы прямо установили значение поля ввода в пустую строку в конце метода. Теперь, когда пользователь снова пытается повторно загрузить тот же файл; это всегда будет воспринято как событие изменения change.
Со всеми изменениями, которые мы изложили выше; файл App.vue будет выглядеть следующим образом:
`
` В этот момент мы сможем успешно загружать файлы изображений.
Примечание: Для простоты мы не будем создавать функции ограничения загрузки пользователем определенных файлов изображений. Если пользователь загружает не изображения (т.е. не .jpg, .png, .gif), то ничего не будет отображаться на месте изображения.
Ленту домашней страницы следует отображать только в том случае, если пользователь находится на первом этапе. Чтобы проверить это, мы добавим оператор v-if для соответствующего элемента DOM в файле PhoneBody.vue со step === 1. Чтобы получить доступ к передаваемому параметру step, мы определим его в свойстве компонентов props:
`
3) Экран фильтра
Предварительный просмотр изображения и список фильтров
Когда пользователь загружает изображение и направляется на 2 этап, мы хотим отобразить выбранное изображение и фильтры, которые можно применить к нему.

Прежде чем мы начнем отображать список фильтров, давайте создадим стартовый шаблон для этого второго шага. Мы начнем с добавления нового элемента div, который условно отображается, когда параметр step равен 2 в шаблоне компонента PhoneBody. Поскольку мы будем использовать image, который загрузил пользователь, мы также объявим этот параметр в props компонента.
`
Когда мы нажимаем иконку загрузки и загружаем изображение, мы перенаправляемся на шаг 2:

Давайте теперь посмотрим, как создать список параметров фильтра, которые пользователь может выбрать. Подобно тому, как мы использовали директиву v-for для отображения списка сообщений в ленте, мы будем использовать директиву v-for для отображения списка фильтрующих элементов на основе сбора данных фильтров.
Мы создадим новый компонент FilterType, который будет отвечать за отображение фильтра на изображении.

В компоненте/папке мы создадим новый файл компонента FilterType.vue:
components/
FilterType.vue
InstagramPost.vue
PhoneBody.vuedata/
styles/
...
Сначала мы заполним файл компонента FilterType.vue исходным содержимым:
`
{{filter.name}}
` Давайте рассмотрим, что содержит этот компонент:
- Мы используем синтаксис Mustache для привязки filter.name к шаблону.
- Шаблон также содержит изображениe, которому выставлены стиль background-image, который в свою очередь привязан к image свойству.
- К этому же изображению мы привязываем класс filter.name. Это нам позволит использовать преимущества библиотеки CSSGram. Мы будем применять фильтры подобные Instagram к изображениям просто добавляя класс с именем фильтра непосредственно к элементу.
- Мы валидируем свойство filter и image, которые использует этот компонент.
- И в конце мы задаём, что источник стилей компонентов находится в файле styles/filter-type.scss.
В файле PhoneBody.vue теперь мы можем отобразить список компонентов FilterType. Мы будем отображать этот список в элементе div class = “filter-container” и файл PhoneBody.vue станет таким:
`
С обновлениями весь наш файл PhoneBody.vue будет выглядеть следующим образом:
`
` Теперь, когда пользователь перенаправляется на второй шаг - они смогут видеть и скроллить превью фильтров:

Выбор фильтра
Со списком представленных фильтров мы хотим дать пользователю возможность выбрать фильтр, который приведет к включению фильтра в расширенный предварительный просмотр изображения.
Выбранная фильтрация в настоящее время передается от компонента приложения до PhoneBody. Чтобы в конечном итоге отобразить выбранный фильтр при просмотре изображения, нам нужно объявить опору и связать его с классом в элементе большого изображения div (т.e. div class=“selected-image”).
Это приведет к обновлению файла PhoneBody.vue:
`
Компонент FilterType является дочерним компонентом компонента PhoneBody и внуком компонента App.

Когда пользователь нажимает на определенный тип фильтра из списка фильтров (т.e. кликает по компоненту FilterType), нам нужно сообщить компоненту App, что значение selectedFilter должно измениться. Поскольку мы будем отправлять информацию на два уровня наверх; то мы будем использовать EventBus для прямой передачи специального события на два уровня вверх при выборе типа фильтра.
An EventBus - это экземпляр Vue, который позволяет изолированным компонентам подписываться и публиковать события между собой. Он предлагает быстрое и простое решение для обработки и передачи данных между компонентами, хотя и имеет некоторые ограничения.
Давайте посмотрим на это в действии. В корне папки; мы создадим файл event-bus.js:
...
styles/
App.vue
event-bus.js
...
В файле event-bus.js мы создадим и экспортируем новый экземпляр Vue:
`import Vue from “vue”; const EventBus = new Vue();
export default EventBus; ` Теперь мы можем начать создание диспетчера событий и слушатель.
Мы создадим диспетчер событий в файле FilterType.vue. В FilterType мы добавим слушатель кликов, который при срабатывании запускает настраиваемое событие filter-selected. Когда мы запускаем событие, мы передаем объект, который содержит значение выбранного фильтра. Мы будем использовать экземпляр EventBus для создания настраиваемого события:
`
{{filter.name}}
Хук created() запускается сразу как только Vue экземпляра/компонента был создан и можно получить доступ к данным экземпляра и его событиям.
Создав слушатель событий в хуке created(), мы создадим слушателя в момент создания компонента. В callback-функции слушателя мы устанавливаем свойство selectedFilter компонента приложения в значение filter из объекта обработчика события:
`

Навигация вперед
Единственное, что осталось на этом этапе, это создать элементы навигации, чтобы пользователь мог перейти к 3 шагу или отменить процесс отправки.

В заголовке телефона мы хотим вывести «Cancel» и «Next», когда пользователь находится на 2 шаге. Фактически, мы хотим отобразить ссылку «Cancel», когда пользователь находится на 2 шаге . Это можно сделать используя v-if директиву,
`
Метод goToHome() будет возвращать пользователя на 1 шаг. К тому же он сбросит все потенциально заполняемые значения, которые мог ввести пользователь - например, image, selectedFilter и caption. Мы разместим этот метод в свойствах methods() компонента:
`

В результате, мы присоединим тот же самый слушатель событий к иконке “Главной”:
`
Давайте также условно отобразим ссылку «Next». Ссылка Next будет отображаться только тогда, когда пользователь находится на step === 2, так как ссылка Share будет показана на следующем шаге. Мы добавим этот элемент ниже cancel-cta в шаблоне приложения:
`
` Для элемента «Next» мы просто подключили слушатель кликов, чтобы увеличить значение *step* на 1.Одна вещь, которую мы хотели бы - это позволить бы пользователю загружать изображение, если не на домашнем индексном экране (т.е. при step ! == 1 ). Чтобы пользователь не мог кликнуть на input для загрузки изображения мы свяжем свойство disabled для input type = file и будем применять его при step ! == 1:
`
` input загружаемого изображения теперь будет отключен, если пользователь не находится на первом шаге. Это завершение построения второго шага нашего UI. Со всеми внесенными изменениями наш файл App.vue будет выглядеть следующим образом:
`
` Если мы протестируем наше приложение, мы сможем загрузить изображение и выбрать наш фильтр выбора.
Все, что нам осталось сделать, это захватить введенный пользователем текст на третьем шаге и это позволит пользователю поделиться созданным постом!
4) Завершение процесса оправки сообщения
Захват контента
Когда пользователь находится на 3 шаге, мы хотим отобразить выбранный предварительный просмотр изображения, но в этом случае также присутствует вход в textarea, где мы можем позволить пользователю отправить подпись в свой пост.

В шаблоне PhoneBody.vue мы представим новый элемент, который отображается только тогда, когда шаг равен 3. Этот элемент будет содержать элемент предварительного просмотра div class = “selected-image”, как и второй шаг, но теперь также будет содержать поле textarea:
`
Использование v-модели в PhoneBody для непосредственной привязки к полю caption не будет работать, потому что дочерний компонент PhoneBody, напрямую будет мутировать значение данных родительского компонента (App). Это известная плохая практика, что приведёт к генерации Vue предупреждения в консоли.
Мы можем избежать этого, объявив более специфичную двухстороннюю привязку данных. Во-первых, мы уже объявили атрибут v-model, где компонент PhoneBody рендерится в шаблоне App.vue:
`
В PhoneBody мы можем теперь объявить значение, которое будет передаваться вниз и привязать его к полю textarea поста. Наш обновлённый файл PhoneBody.vue станет выглядеть следующим образом:
`
`
Поскольку PhoneBody является прямым дочерним элементом App, мы можем использовать интерфейс событий PhoneBody, чтобы напрямую передать событие.
Теперь, когда пользователь вводит подпись в PhoneBody, свойство данных caption в приложении обновляется с помощью настраиваемого input события, которое запускается.
Теперь файл PhoneBody.vue будет выглядеть следующим образом:
`
`
Публикация поста
Чтобы завершить отправку поста, мы должны предоставить пользователю возможность публиковать пост по клику на ссылку «Share» в шапке:

Ссылка на Share должна присутствовать только в том случае, если пользователь находится на 3м (последнем) шаге. Поэтому мы добавим этот элемент условно в заголовок раздела App:
`
` Мы подключили слушатель кликов, который вызывает метод *sharePost()* при нажатии ссылки «Share». Этот метод будет делать три вещи:Он подготовит post объект, который будет содержать данные, предоставленные пользователем
В дополнение к тому, что пользователь отправил, подготовленный post объект будет содержать информацию про имя пользователя username и изображение постера userImage. Для простоты мы просто зададим имя пользователя fullstack_vue, а userImage как изображение логотипа Vue.
Он будет пушить новый post объект в массив posts приложения
Чтобы пушнуть новый элемент в начало массива, мы будем использовать нативный метод Array.unshift().
Он сбросит всю информацию и вернет пользователя на главную страницу
У нас уже есть метод goToHome(), который мы, наконец, будем использовать для сброса информации о пользователе и установки значения шага step назад в 1.
Со всем этим мы укажем этот метод sharePost() в свойстве methods() приложения:
`
`
` Теперь мы можем пройти весь процесс отправки поста!

5) Перетягивание-прокрутка
UI нашего приложения почти закончен. Мы добавим одну новую дополнительную функцию для завершения урока.
Как вы могли заметить, нам пришлось прокручивать ленту (и список фильтров) для навигации по списку элементов. В традиционных мобильных приложениях мы часто можем перемещаться по приложению перетягиванием экрана. Чтобы включить аналогичную функцию с перетаскиванием в нашем приложении, мы будем использовать vue-dragscroll библиотеку.
Для включения новой библиотеки в наше приложение нам нужно установить её с помощью yarn или npm (или получить к ней доступ через CDN):
npm install vue-dragscroll --save
В нашей песочнице уже есть библиотека vue-dragscroll как зависимость.
С доступной библиотекой мы сначала зарегистрируем ее в нашем приложении. В файле index.js мы импортируем библиотеку vue-dragscroll и укажем Vue.use(), чтобы использовать плагин в нашем модульном приложении:
import Vue from "vue";
import App from "./App";
import VueDragscroll from "vue-dragscroll";
Vue.use(VueDragscroll);
/* eslint-disable no-new */
new Vue({
el: "#app",
render: h => h(App)
});
С установленным плагином мы можем использовать директивы v-dragscroll в наших шаблонах. Мы хотим, чтобы лента и списки фильтров приложения можно было перетаскивать вдоль осей Y и X соответственно:

В шаблоне PhoneBody.vue мы укажем возможность вертикального перетаскивания в ленте, добавив v-dragscroll.y в элемент ленты. Аналогично, мы добавим v-dragscroll.x в контейнер фильтров, который содержит список типов фильтров на 2 шаге.
`
Вывод
Без учета стилизации нашего приложения с помощью CSS мы прошли через урок создание полного UI, который имитирует процес отправки фото и сообщений в Instagram на мобильном устройстве. К тому же мы воспользовались библиотекой CSSGram для добавления фильтров как в Instagram, и vue-dragscroll для добавления функциональности прокрутки и перетаскивания постов.
Если вы застряли в любой момент и/или имеете дополнительные вопросы, вы более чем можете оставить комментарий или сообщение прямо мне! Обратная связь всегда приветствуется!