Blog
BLOG

Debunking Vue.Js internals

Dhruv Patel
   

In the world of application software development, a variety of tools, techniques, and technologies are used by the development team to launch successful applications. Vue.Js, a JavaScript framework, is a recent technology that is prevalent for its smooth learning curve and flexibility. With just 18 to 21KB size, Vue.Js provides beneficiary features like simple integration, detailed documentation, and two-way communication through MVVM architecture.

To better understand Vue.Js technology, it is better to find answers to these essential questions:

  • What happens when creating a Vue.js instance?
  • What happens to a template internally?
  • What is the significance of Virtual DOM?
  • How does the template re-rendering happen when a property changes?

Using templates with Vue instance

A new Vue instance needs to be created to develop applications in Vue.js, where all the properties of the data object can be added. Vue provides reactive and composable components, and therefore when the values of the properties change, the display view is updated to match the new values. In this context, components are custom elements to attach a specified behavior in a Vue.js’ compiler. The component will have a template which must pass from various stages before appearing in the browser. A small template is used below to drive this article as an example.

<div id=”app”>
<span v-if=”dynamic”>Dynamic text</span>
<span><p>Static text</p></span>
<button @click=”toggleFlag”>Toggle Dynamic</button>
</div>

Here, JS (JavaScript) logic of the component is toggling between dynamic and static information, as mentioned in the template.

The compilation phase

The Vue compiler reads a component’s template, takes it through stages like parsing, optimizing, codegen and ultimately creates a render function as shown below. This function is responsible for creating a VNode which is used by the virtual DOM patch process to create the actual DOM. This allows the creation of dynamic web pages, because within a page JavaScript can:

  • add, change, and remove any of the elements and attributes
  • change any of the CSS styles
  • react to all the existing events
  • create new events

Parsing stage

This stage of compilation plays with the markup sent as a template for a particular component. As seen in the image, first of all, the parser parses the template into HTML parser and in turn, converts it to AST. (i.e., Abstract syntax tree).

AST — After Parsing stage

The AST contains information like attributes, parent, children, tags. The parsing process will parse directives similar to elements. The structural directives like v-for, v-if, v-once will be represented as key-value pairs in the AST for a particular element. The v-if directive in our template, after the parsing, will be pushed into the attrsMap as an object like {v-if: “dynamic”}.

Optimization stage

The goal of the optimizer is to walk through the generated AST and detect the sub-trees that are purely static, i.e., parts of the DOM that never need a change. As shown in the image, these elements will be marked as static.

AST — After optimization

Once it detects the static sub-trees, Vue will hoist them into constants, so that Vue will not create new nodes for them on each re-render. These nodes will be skipped entirely during the patching process of the virtual DOM.

CodeGen stage

The last stage of a compiler is the codegen stage, a stage where the actual render function will be created and will be used in the patch process.

Hierarchy of render functions

In the above image, the hierarchy of templates is converted into the hierarchy of render functions. Based on the static flag provided by the optimizer, the codegen will bifurcate the render function into two separate functions. One is a simple render function, and the other is a static render function. In the end, render functions will be used to create VNode while triggering the actual render process.

Note: The template compilation will take place ahead-of-time if a build step is used. e.g., single-file components.

The observer and watcher — Reactive component

Observer

Under the hood, Vue will walk through all the properties that we define into the data and convert them to getter/setters using Object.defineProperty. When any data property gets a new value, then the set function will notify the Watchers.

Watcher

A Watcher is created for each component when a Vue application is initialized. It parses an expression, collects subscribers, and fires callbacks when the expression value changes. This is used for both — $watch API and directives. The watcher instance handles dependency tracking and changes notifications. Every component instance has a corresponding watcher instance, which records any properties that are “touched” during the rendering of the component as dependencies. Later on, when a dependency’s setter is triggered, it notifies the watcher, which will ultimately trigger the patch process.

Whenever a data change is observed, it will open a queue and buffer all the data changes that happen in the same event loop. All the watchers are added into the queue. Each watcher has unique Id in incremental order, so if the same watcher is triggered multiple times, it will be pushed into the queue only once, and before consuming it, the queue will be sorted because the watcher should run from parent to child.

Internally, Vue tries a native Promise.then and MessageChannel for the asynchronous queuing with setTimeout(fn, 0).

nextTick function will consume and flush all watchers within the queue. Once all watchers have been consumed and flushed, the render process will be initiated from the Watcher’s run() function.

The Patch process

The patch process is a process which efficiently interacts with actual DOM using the Virtual DOM. A Virtual DOM is just a JavaScript object which represents a Document Object Model (DOM). Vue.js is internally using snabbdom library. So, let’s look at exactly what’s going into this patch process – the process is all about the game of old VNode (Virtual DOM Node) and new VNode. Ultimately both will be compared with each other.

The algorithm will work in the following way —

  • It will first check whether Old VNode is present or not and if it does not then create the DOM element for each VNode. When you land first time in-app and the first render process initiates, in that case, the old VNode won’t be there.
  • The other case, if the old VNode is present then the process of comparing children of both will start — The common node will remain as it is in DOM and the new node will be added, and the older unmatched node will be removed from Virtual DOM as well as from the actual DOM.
  • The styles, class, dataset, and event-listener for the matched node will be updated (the new thing will be added, or things which are not required will be removed) if needed.
  • Moreover, the tree of a static node will be untouched and used as it is since there is no interaction with the actual DOM for this sort of tree.

The same process will recursively take place for all the nodes.

Life cycle hooks

The lifecycle of the component can be segregated into four sections

  • The Creation
  • The Mounting
  • The Updating
  • The Destruction

As soon as the new instance of Vue is executed, the process of creating component starts. The stages are:

  • beforeCreation: Before the process of collecting the events and the data required for the component. In other words — the process of collecting watchers/dependencies.
  • created: When Vue is done with setup data and watchers.
  • beforeMount: Before the patch process. The VNode are getting created based on data and watchers.
  • mount: After the patch process.
  • beforeUpdate: The watcher updates the VNode and re-initiated the patch process again if the data changes.
  • update: Patch process is done.
  • beforeDestroy: Before destroying the component. Here, component is still fully present and functional.
  • destroyed: Teardown watchers and remove event listeners or child component that was attached to it have been removed.

As per the state of vue.js 2019 report, 59% of respondents stated, ease of use as the most critical reason behind adding Vue.js to the technology stack. Besides this, ease of integration, excellent documentation, performance, and highly involved community are the most significant advantages of vue.js.