В своей работе, мне относительно недавно пришлось столкнуться с фреймворком 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

Комментарии (11)


  1. deivan
    23.03.2018 22:19

    Всякий бэкендщик, который не понял «этот ваш гавеный джаваскрипт», лезет первее всех поучать хомячков, что хорошо во фронтенде, а что плохо. Особенно здоровски получается это у тех, кто когда-то кое-как вінужденно разобрался с jQuery и на этом, якобы, познал дзен джаваскрипт.
    Что же полезного нам довели до внимания?
    О, с официальной документацией, оказывается, «сложно разобраться»… Серьезно???? Чувак, ты видел документацию по Ангуляр5 ???.. вот где ад, а дока по Вью, — удивительно легка в сравнении с Ангуляром и Реактом, а тем более — очень большой объем русскоязычного перевода.
    Отдельная песня — фразы в стиле КО. Вот эта вот, к примеру: «В зависимости от того, где они вызываются, они должны располагаться в определенных объектах.» Спасибо, Кэп.
    Далее идет пример про обращение к внешнему ресурсу и упоминается экземпляр ошибки. Попробуйте прочитать этот текст от конца к началу, — уверяю, смысл не изменитсЯ, ибо это бредятина, вызванная какими-то веществами, которыми автор вряд-ли заделится, ибо в школу пора.
    Ага, автор поднял веки и дошел до передачи данных между компонентами. Но увы, его вялых сил хватило только на рерайт абзаца из документации о передачи данных в компонент через пропсы, а вот осветить самое интересное — возврат данных в родителя через ивенты, — автор не захотел. Достаточно просто написать «В Vue есть встроенные методы, для этого, они хорошо описаны в официальной документации», и хоба! — давайте писать статью дальше.
    О да, автор нашел в гугле, что в экосистеме Вью есть менеджер стейтов, и даже автор не поленился написать его название — Vuex. Честь и хвала автору.
    =======================
    Заключение. Систематически, в дни школьных каникул, на Хабре появляются разные новые статьи. Они хорошие и разные. К сожалению, разных в последнее время значительно больше, чем хороших. Остатся надежда, что когда-то это соотношение поменяется.


    1. alex6636
      24.03.2018 12:12
      -1

      Это ни в коем случае не отменяет того, что жаваскрипт говеный


      1. kentov
        26.03.2018 10:08

        И без него не работает ни один сайт (ну разве что какие-то говенные)


        1. alex6636
          26.03.2018 13:03

          только контент делает сайт хорошим или говеным, а не свистелки-перделки, сделанные криво на js, с десятками тонн кода и сотнями зависимостей


    1. clicker314
      26.03.2018 10:08

      deivan скиньте пожалуйста ссылочку на свои статью про Vue.js, хочется посмотреть работу мастера.

      dastanaron_dev Спасибо за статью ))


    1. tsapkin
      26.03.2018 10:08

      Полностью согласен. Статья ни о чем. Чуть vue, чуть Vuex, мимоходом AJAX, а толку никакого. Пример с Аxios: если уж пользуете vuex, то работайте с AJAX в actions. А офф. дока по вью очень хорошая.


    1. dastanaron_dev Автор
      26.03.2018 10:18

      Чувак, ты вроде бы понял, но как то не полностью. Да, я действительно backend — разработчик, которому время от времени приходится разбираться с фронтендом. И эта статья не призыв к действию или методика разработки. Я лишь пишу, с чем было сложно разобраться, мне как бекендщику, ибо таких как я довольно много. Если ты без проблем все понял, и прочитав две страницы официальной доки, флаг тебе и респект, но извини, фронт требует немного другого подхода и склада мысли, нежели бэк. Не для тебя просто эта статья, а унижать собрата своего по коду — это как минимум не вежливо. Не нравится статья, хорошо! Критика это всегда хорошо. Но вот про школьные каникулы и прочую лабуду — это пожалуй сильно лишнее. Да в ней нет много чего, про взаимодействие компонентов, но это и не тема данной статьи. Все в одну не упихаешь. У меня не было сложности разобраться с обратным обменом, от дочернего компонента, к родительскому, поэтому об этом я не писал, там сложного ничего нет, когда понимается сама суть работы либры, остальное не сложно понимается! За критику спасибо, но вот с излишествами не стоит!


      1. deivan
        26.03.2018 18:20

        ок, за школоту — приношу извинения, был взволнован.


  1. dravor
    26.03.2018 10:08

    Значение всех этих Ангуляров сильно переоценено. Все что мне нужно я успешно пишу на jQuery и знаю кучу отличных проектов, работающих на чистой ваниле. С нетерпением жду, когда большие корпоративные заказчики найдут себе новый фетишь, а Реакт с Ангуляром будут просто экологично развиваться.
    Vue хотя бы попытку сдела вылезти из дебрей сверхусложнений простых вещей.


    1. waul
      26.03.2018 18:10

      Мне вот тоже сложно представить, как я буду каждый раз переписывать свою CMS (с самописными редактором текста и файловым менеджером) под очередной модный фреймворк.


      1. dastanaron_dev Автор
        26.03.2018 18:10

        А зачем переписывать то, что работает на новый фреймворк? Новый фреймворк, для новых проектов!