Low-level implementation of the full life cycle of a component in Vue 2
The article will focus on the low-level implementation of the component’s life cycle: declaration, mounting in the DOM, destroying an instance of the component itself.
We are used to the most common use of components: declaration, registration, accessing a component from another with passing parameters. We describe the component in the componentName.vue file and call it in the template of another component as
Development environment:
vue: 2
Nuxt: ^2.15
Vuetify: ^2.6
Global component registration.
Inside my notification plugin, I register the simplest possible component – a dialog box with the type passed to the plugin, a message and a close button. The type determines what color the message is. Classical: success, info, error, warning. The dialog close button will debuild the component instance and remove it from the DOM. My component will have almost no logic – only a template. Therefore, the render function immediately goes into the component. Its task is to return the html code of the component.
let Nf = Vue.component('notificationDialog', {
render: function (createElement) {
}
})
There is good documentation on the function, I will not stop.
Next, we describe the nesting of html tags using 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]) // Предыдущий узел добавляется как дочерний в карточку
Who noticed something unusual, well done. Yes! createElement understands Vuetify tags. This is great, because saves time writing nested tags and styles for them! One more thing. For the same reason, you can change styles to classes that are relevant to them. An example is below.
const cardDivVNode = createElement('v-card', {
style:{
padding: ‘8px’,
display: ‘flex’,
flexDirection: ‘column’,
alignItems: ‘end’
},
class: ['pa-2', 'd-flex', 'flex-column', 'align-end',]
})
What you see in the style and class objects in the example above are the same. So padding: ‘8px’ is the same as replacing it with ‘pa-2’. It stands for ‘padding all = 2*4px’. Therefore, for a uniform design, I use Vuetify classes where possible:
const cardDivVNode = createElement('v-card', {
class: ['pa-2', 'd-flex', 'flex-column', 'align-end',]
})
Mounting the Component Instance and Adding it to the DOM
The component is declared. Next, in the plugin logic we:
create an instance of the class
mount it in DOM
and add as a child of our root element with id=”app”
let component = new Nf() // создаем экземпляр класса
component.$mount() // монтируем его в DOM
document.getElementById('app')?.appendChild(component.$el) // добавляем в корень
At this stage, everything.
Destroy component instance, remove from DOM
As mentioned above, the component instance will debuild itself. In the template of the notificationDialog component, in render function there is a button to close the dialog box:
const closeButtonVNode = createElement('v-btn', {
attrs: {
outlined: true, // аттрибуты Vuetify
color: 'primary',
block: false,
inline: true
},
on: {
// события размещаются тут
}
}, 'Close')
She will have an onClick method:
const closeButtonVNode = createElement('v-btn', {
…
on: {
click: () => {
this.$destroy() // дестроим текущий экземпляр
this.$el?.parentNode?.removeChild(this.$el) // удаляем из DOM
}
}
}, 'Close')
Conclusion
We managed to create a notificationDialog component in a plugin that the main application does not know about.
The component is declared globally – once, when building the application
Instances are created when the plugin is called
Instances destroy themselves at the push of a button.