В своей работе, мне относительно недавно пришлось столкнуться с фреймворком Vue.js, т.к. до этого, я занимался в основном backend разработкой, пришлось со многим разбираться и многое было сложновато понять, особенно, когда раньше использовал только jQuery. В рамках данной статьи, я хочу помочь своему читателю разобраться с теми проблемами в понимании, с которыми столкнулся я. Конечно проблемы на этапе изучения чего-то нового у всех возникают разные, но и не мало тех, у кого они будут похожи, именно на это и будет направлена данная статья.
Я не буду проводить сравнение данного фреймворка с другими, думаю, что по этому поводу в интернете информации хватает, попытаемся разобраться именно с Vue.js и с “чем его едят?!”. В данном контексте будут рассматриваться примеры для сборки с помощью webpack или подобным системам. Примеры компонентного взаимодействия будут на примере однофайловых компонентов, поскольку они немного проще в понимании. Однако, принципы взаимодействия однофайловых и многофайловых компонентов ничем особо не отличаются.
Рассматриваемые аспекты
- Разберемся в областях видимости переменных внутри одного и нескольких компонентов.
- Рассмотрим возможности передачи переменных между компонентами.
- Разберемся с общим взаимодействием компонентов друг с другом.
Итак, первое с чем приходится столкнуться новичкам и испытать определенные сложности — это не jQuery, и работает он иначе. Vue предоставляет реактивные связки своих переменных внутри компонентов и при взаимодействии с другими компонентами, что открывает существенно новые возможности. Да на jQuery это все можно организовать тоже, но более “толстым” кодом и кучей обработчиков, в которых можно запутаться. Если разобраться с Vue, на нем все это делается гораздо проще.
Сразу дам небольшой совет: “Не нужно пытаться провести аналогии написанного с jQuery!”, чем больше будет попыток провести аналогию, тем больше будет путаницы и непонимания.
Области видимости переменных
В официальной документации, конечно есть вся необходимая информация, но с ней сложновато разобраться, пока не попробуешь и не разберешься сам. Я попробую облегчить этот процесс.
В любом компоненте Vue имеется набор данных называемых “props”. Это объект содержащий в себе те данные, которые могут быть определены при вызове компонента или иметь значение по умолчанию.
Пример объявления свойств компонента:
/**------**/
props: {
uuid: {
type: String,
default: '',
},
},
/**------**/
В данном примере, мы указываем, что в нашем компоненте есть некий uuid, который является строкой и по умолчанию он является пустой строкой.
Кроме этого, в компоненте содержится объект data{}, который выполняет взаимодействие нашего компонента, с какими-либо другими, которые мы можем использовать в своем. В Vue является нормальной практикой, когда один компонент, может в себе совмещать несколько других, для их объединения. data, часто объявляется как функция, такую практику вы встретите наиболее часто на форумах и сообществах, обсуждающих реализацию на Vue
/**------**/
data() {
return {
dialog: false,
indeterminate: false,
loading: false,
notifications: false,
sound: true,
widgets: false,
}
},
/**------**/
Как видите, объект data не задает типы переменных, а сразу присваиваются значения. В своем приложении, мы можем менять это значение по определенным событиям, а в свою очередь другие компоненты вызванные в нашем, будут отслеживать изменение этих переменных и определенным образом реагировать на них. Это и будет реактивная связка компонентов с определенными переменными.
Кроме этого, в общем объекте компонента задаются методы (функции), для работы с этими переменными и событиями внутри компонента. В зависимости от того, где они вызываются, они должны располагаться в определенных объектах. Более подробно, про них, вы можете ознакомиться в официальной документации, там вопросов вроде бы не возникает. Мы же говорим об области видимости. Поэтому рассмотрим пример:
<template>
<div id="inspire">
<v-dialog v-model="alert" max-width="290">
<v-toolbar
color="primary"
>
<v-toolbar-title>Внимание</v-toolbar-title>
<v-spacer></v-spacer>
<div class="dialog-close-button" @click="alert=false">
<i class="fas fa-times"></i>
</div>
</v-toolbar>
<v-card>
<v-card-text>{{ alertMessage }}</v-card-text>
</v-card>
</v-dialog>
</div>
</template>
В данном примере, мы в своем компоненте создаем шаблон, в котором вызываем компонент Vuetify dialog
Для работы с ним, на понадобится модель alert, которая будет указывать на то открыто ли это окно сейчас или закрыто (соответственно true или false), а так же переменная alertMessage — которая будет в себе нести сообщение об ошибке или предупреждении. Каждому свойству, например color или max-width мы можем задать переменные, которые должны находиться в объекте data(){}, и с помощью своих методов изменять их. Но для простоты ограничимся двумя. Итак для управления этими свойствами, мы должны правильно распределить объекты внутри скрипта компонента.
<script>
export default {
data() {
return {
/**------------**/
alert: false,
alertMessage: 'У вас нет прав на это действие',
}
},
methods: {
deleteObject() {
axios.delete(‘http: //example.com/’)
.then(response => {
/** ------- **/
})
.catch(error => {
this.alert = true;
this.alertMessage = “Что - то пошло не так”;
});
},
},
/**----------**/
</script>
На данном примере видно, что у нас есть некий метод deleteObject(), заданный в объекте methods, который делает запрос на удаление чего-то на сайте example.com, каким то образом обрабатывает ответ, а в случае провала выбрасывает исключение, в котором, уже мы вызываем наш компонент диалог, присваивая переменной alert значение true и присваиваем сообщение, которое будет выведено в шаблоне. Теперь обратите внимание, что в шаблоне, мы обращаемся к переменным в дате напрямую, просто указывая их название, а в методах, через объект this. Все методы, где бы они не были заданы, если они работают с data, они используют эти переменные через this. Если один метод, должен вызывать какой-то другой, определенный в объекте methods, он тоже вызывается через this.methodName().
Также обратите внимание на обработчик события клика в шаблоне:
<div class="dialog-close-button" @click="alert=false"><!--/**------**/ !--></div>
Здесь можно без метода изменить значение переменной alert в data, и поскольку она реактивно связана с моделью — компонент сразу отреагирует на ее изменение.
В данном случае, тем кто привык работать с классическими объектами можно запутаться, потому что связь выглядит немного нелогичной. Но на самом деле, никакой магии здесь нет. Когда мы вызываем экземпляр класса Vue и подключаем туда компоненты, он их интерпретирует через собственные объекты внутри js-фреймворка, именно поэтому у него образуется некое подобие собственной области видимости.
Передача данных между компонентами
Часто из нашего компонента, мы должны управлять состоянием других компонентов, вызванных внутри нашего. Как я уже писал выше, для этого существует объект `props`, а передача осуществляется путем присвоения этого значения, либо с помощью нашей переменной в data либо сразу присвоением значения этому свойству.
В нашем же примере, который я использовал выше уже это есть, для удобства, я продублирую его снова, и постараюсь объяснить:
<template>
<div id="inspire">
<v-dialog v-model="alert" max-width="290">
<v-toolbar
color="primary"
>
<v-toolbar-title>Внимание</v-toolbar-title>
<v-spacer></v-spacer>
<div class="dialog-close-button" @click="alert=false">
<i class="fas fa-times"></i>
</div>
</v-toolbar>
<v-card>
<v-card-text>{{ alertMessage }}</v-card-text>
</v-card>
</v-dialog>
</div>
</template>
Возьмем для примера, встроенный в наш шаблон компонент
<v-toolbar
color="primary"
>, <!-- /**------**/ !-->
В котором объявлено свойство color. Именно это свойство должно быть задано внутри скриптов компонента <v-toolbar>. В данном примере, мы присвоили ему определенное значение. Но мы его можем менять. Если нам требуется его динамически изменять, мы можем забиндить слежение этого свойства за нашей переменной, тогда в шаблон немного изменится:
<v-toolbar
:color="toolbarColor"
>,<!-- /**------**/ !-->
В объекте data, мы должны объявить эту переменную, тогда мы сможем ее изменять методами нашего компонента, указанными в объекте methods:
<script>
export default {
data() {
return {
/*------*/
toolbarColor: ’primary’,
}
},
/** --- **/
</script>
Теперь мы можем объявить метод, и изменять переменную toolbarColor внутри него, используя конструкцию this.toolbarColor = “Значение”, и компонент <v-toolbar> будет на нее реагировать.
Таким образом, мы можем передавать значения в дочерние компоненты.
Для обратной связи, т.е. из дочернего компонента получить данные в родительский используются другие, чуть более сложные конструкции. В Vue есть встроенные методы, для этого, они хорошо описаны в официальной документации Vue, по ссылке,
поэтому я не буду подробно останавливаться на них. Проблема в том, что это не всегда удобный способ, и когда вы его подробно изучите, то сами убедитесь в этом. Он, так сказать, применим не во всех случаях.
Есть еще один хороший способ, использование библиотеки Vuex, которая создает некое хранилище для общих переменных, для управления состоянием приложения. На ней я тоже не буду подробно останавливаться, потому что, если вы поймете логику взаимодействия переменных внутри методов компонента и других компонентов, разобраться с Vuex, не должно составить труда. Кроме того, у них есть хорошая русскоязычная документация.
Для работы с Vuex, я прошу обратить особое внимание на то, как она правильно устанавливается, и что для доступа к данным, вам как минимум нужно использовать Мутации и Геттеры. Про них советую прочитать наиболее подробно. Я сначала пытался бегло понять суть и приступить к программированию, но столкнулся с множеством непонятных мне, на тот момент, проблем. Поэтому этим разделам уделите особое внимание.
Я обращу ваше внимание на саму систему взаимодействия. Она немного отличается от стандартного взаимодействия внутри Vue. Это как раз то, что я сначала упустил, а потом потратил много времени, чтобы разобраться. Возьму пример из официальной документации:
const store = new Vuex.Store({
state: {
todos: [
{ id: 1, text: '...', done: true },
{ id: 2, text: '...', done: false }
]
},
getters: {
doneTodos: state => {
return state.todos.filter(todo => todo.done)
}
}
})
Здесь, мы объявляем сам store и getters методы для него. Обратите внимание, что метод внутри getters задан не совсем стандартно, но еще более нестандартно его использование в своем компоненте. В нашем компоненте, если мы хотим использовать переменную из store, мы должны вызвать ее с помощью новых переменных, и метод будем вызывать не как метод, а как свойство объекта store
this.$store.getters.doneTodos;
И никаких скобок, поскольку это метод, который вернет готовый объект, он и вызывается как просто свойство объекта getters внутри $store.
С мутациями немножкуо проще, там сразу написано, что нужно использовать comit('methodName', ПЕРЕДАВАЕМЫЕ_В_МЕТОД_ПЕРЕМЕННЫЕ).
Более подробно про мутации здесь.
Заключение
Мы познакомились с областью видимости переменных внутри компонентов, узнали способы обмена данными между компонентами, отсюда уже должно сложиться общее понимание о взаимодействии компонентов друг с другом в Vue.js. От себя скажу, что система достаточно гибкая, и уже существует множество готовых компонентов и библиотек, расширяющих возможность Vue. Каждый желающий может присоединиться к развитию этой системы и облегчать жизнь другим разработчикам. Компонентная система позволяет легче обслуживать код, соединять готовые компоненты внутри других, и не дублировать код, что приближает нас к способом “красивой” разработки. Новичкам желаю терпения и успеха, в познании новых для себя областей.
P.S.
Ниже я добавил ссылки, с которых я использовал материалы для написания статей и примеров. Для библиотеки Vuetify я напишу две ссылки, на англоязычную и на русскоязычную документацию, поскольку русскоязычная на момент написания статьи переведена не полностью, и в ней может чего-то не быть. Но в англоязычной, вы сможете найти все что нужно знать об этой библиотеке. По работе с данной библиотекой, могу написать более подробно, если будут вопросы по ней или что-то станетне понятно.
Ссылки:
Русскоязычная документация по фреймворку Vue.js
Русскоязычная документация по библиотеке Vuex
Англоязычная документация по Vuetify
Русскоязычная документация по Vuetify
Автор: dastanaron_dev