В статье речь пойдет о низкоуровневой реализации цикла жизни компонента: объявление, монтирование в DOM, дестрой экземпляром компонента самого себя.

Мы привыкли к наиболее частому использованию компонентов: объявление, регистрация, обращение к компоненту из другого с передачей параметров.  Описываем компонент в файле componentName.vue и вызываем в шаблоне другого компонента как <component-name /> А как быть если мы хотим вызвать скажем диалоговое окно из плагина? И хотим чтобы о нашем диалоговом окне не знало все приложение, а знал только плагин? Именно с такой задачей столкнулась на рабочем проекте и собирать информацию пришлось по крупицам. Расскажу как было реализовано все перечисленное. Я делала в плагине, но это не обязательно. Этот подход можно реализовывать и в самом проекте.

Среда разработки:

  • Vue: 2

  • Nuxt: ^2.15

  • Vuetify: ^2.6

Глобальная регистрация компонента.

Внутри моего плагина notification регистрирую максимально простой компонент - диалоговое окно с переданным в плагин типом, сообщением и кнопкой закрытия. Тип определяет какого цвета сообщение. Классические: success, info, error, warning. Кнопка закрытия диалогового окна будет дестроить экземпляр компонента и удалять его из DOM. Логики у моего компонента почти не будет - только шаблон. Поэтому в компоненте сразу идет render функция. Ее задача - вернуть html-код компонента. 

   let Nf = Vue.component('notificationDialog', {

        render: function (createElement) {

	 }

})

По функции есть хорошая документация, не буду останавливаться.

Далее, описываем вложенность html тегов при помощи createElement

const alertVNode = createElement('v-alert', {

     attrs: { type: message.type, icon: false }, // атрибуты

    style: {	// стили элемента

        width: '100%'

        }

    },

    message.text) // содержимое: String или Array<VNodes>

const cardVNode = createElement('v-card', {

    class: ['pa-2', 'd-flex', 'flex-column', 'align-end',]

    },

    [alertVNode]) // Предыдущий узел добавляется как дочерний в карточку

Кто заметил что-то необычное, тот молодец. Да! createElement понимает теги Vuetify. Это здорово, т.к. экономит время на написание вложенных тегов и стилей для них! И еще одно. По этой же причине  можно менять стили на релевантные им классы. Пример ниже.

 const cardDivVNode = createElement('v-card', {

     style:{

		padding: ‘8px’,

		display: ‘flex’,

		flexDirection: ‘column’,

		alignItems: ‘end’

     },

    class: ['pa-2', 'd-flex', 'flex-column', 'align-end',]

})

То что вы видите в объектах style и class в примере выше - одно и тоже. То есть padding: ‘8px’ - все равно, что заменить классом ‘pa-2’. Расшифровывается как ‘padding all = 2*4px’. Поэтому, для единообразного дизайна - пользуюсь классами Vuetify там, где это возможно:

const cardDivVNode = createElement('v-card', {

    class: ['pa-2', 'd-flex', 'flex-column', 'align-end',]

})

Монтирование экземпляра компонента и добавление в DOM

Компонент объявлен. Далее, в логике плагина мы:

  • создаем экземпляр класса

  • монтируем его в DOM

  • и добавляем как дочерний элемент нашего корневого элемента с id=”app”

    let component = new Nf() 	// создаем экземпляр класса

    component.$mount()		// монтируем его в DOM

    document.getElementById('app')?.appendChild(component.$el) // добавляем в корень

На этом этапе все.

Дестрой экземпляра компонента, удаление из DOM

Как говорила выше, дестроить экземпляр компонента будет сам себя. В шаблоне компонента notificationDialog, в рендер функции присутствует кнопка закрытия диалогового окна:

const closeButtonVNode = createElement('v-btn', {

        attrs: {

            outlined: true, // аттрибуты Vuetify

            color: 'primary',

            block: false,

            inline: true

        },

        on: {

        // события размещаются тут

        }

}, 'Close')

У нее и будет метод onClick:

const closeButtonVNode = createElement('v-btn', {

        …

        on: {

            click: () => {

                this.$destroy() // дестроим текущий экземпляр

                this.$el?.parentNode?.removeChild(this.$el) // удаляем из DOM

            }

            }

        }, 'Close')

Заключение

У нас получилось создать компонент notificationDialog в плагине, о котором не знает основное приложение. 

  • Компонент объявляется глобально - один раз, при билдинге приложения

  • Экземпляры создаются при вызове плагина

  • Экземпляры дестроят сами себя по нажатию на кнопку.

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


  1. dopusteam
    17.06.2023 12:39
    +1

    pa-2’. Расшифровывается как ‘padding all = 2*4px’

    А откуда 4 взялось?

    Погуглил, это в verify так решили сделать, оказывается


  1. B_bird
    17.06.2023 12:39
    +3

    Можно сократить. Статья c громким названием нам всего лишь пересказывает (примешивая зачем-то сюда вьютифай) два небольших раздела документации, а именно:

    1. рендер-функции (https://v2.vuejs.org/v2/guide/render-function.html#createElement-Arguments)

    2. маунт (https://v2.vuejs.org/v2/api/#vm-mount)

    С целью сделать создания компонента as service.

    Добро пожаловать в мир вью, вас ждет еще много интересного, если вызывает удивление это:

    createElement понимает теги Vuetify


  1. vasyakolobok77
    17.06.2023 12:39

    Некоторые замечания по коду:

    new Nf()

    Абсолютно неинформативное имя класса / компонента.

    style:{ padding: ‘8px’

    На слое js безусловно можно управлять оформлением, но лучше выносить это на слой css, и управлять со слоя js только посредством css-классов.

    document.getElementById('app')

    Жесткая привязка к элементу #app. Может у меня корневой элемент vue назван иначе?

    И я не увидел api, которым будет пользоваться прикладной разработчик. Что-то типа showNotification(....). Вы же не хотите обязать каждого создавать руками объекты и монтировать их?


    1. golubeva-webmaster Автор
      17.06.2023 12:39

      >И я не увидел api, которым будет пользоваться прикладной разработчик.


      1. golubeva-webmaster Автор
        17.06.2023 12:39

        Оставила это "за скобками", т.к. это уже про реализацию плагина. Да, стоит добавить способы передачи параметров, тут соглашусь.


  1. Dimava
    17.06.2023 12:39

    Напоминаю, что поддержка Vue2 заканчивается в конце 2023 года
    Если собираетесь переезжать на Vue3 - пора
    Начинать на Vue2 новые проекты точно не стоит

    https://v2.vuejs.org/lts/


    1. golubeva-webmaster Автор
      17.06.2023 12:39

      Да, я в курсе. Спасибо.