В статье речь пойдет о низкоуровневой реализации цикла жизни компонента: объявление, монтирование в 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 в плагине, о котором не знает основное приложение.
Компонент объявляется глобально - один раз, при билдинге приложения
Экземпляры создаются при вызове плагина
Экземпляры дестроят сами себя по нажатию на кнопку.