Introduction
Before we dig further into working with Vue.js, let’s pause to highlight a couple of key points that are helpful to understand when working with Vue.
Virtual DOM
When a web page is parsed by a browser, a tree-like data structure called a Document Object Model (DOM) is created.
For example, consider the following HTML:
<!doctype html>
<html lang='en'>
<head></head>
<body>
<ul class='list'>
<li class='list-item'>ABC</li>
</ul>
</body>
</html>
Visualized as a DOM tree, it would look like this:
Using the DOM API we can dynamically manipulate the DOM via JavaScript.
For example, to add a new list item, we could do something like this:
let newListItem = '<li class="list-item">XYZ</li>';
document.getElementsByClassName("list")[0].innerHTML = newListItem;
Complex web apps can contain thousands of nodes; finding and updating these nodes has a performance cost.
JS frameworks like Vue.js addresses this by mapping the DOM to a JavaScript object (aka, the Virtual DOM).
For example, the above DOM mapped to a JavaScript object would look something like this:
const virtualDom = {
tagName: "html",
children: [
{ tagName: "head" },
{
tagName: "body",
children: [
{
tagName: "ul",
attributes: { "class": "list" },
children: [
{
tagName: "li",
attributes: { "class": "list__item" },
textContent: "List item"
}
]
}
]
}
]
}
Instead of updating the DOM directly, we update the Virtual DOM (using the functionality that our framework provides).
These updates are optimized and batched together, and eventually propagated to the real DOM in a way that is more efficient than if we updated the DOM directly.
Reactivity system
As you saw in the example built in the introduction, when data properties of our Vue instance are changed, those changes are propagated wherever that data is being used.
This may include places where the data is text-interpolated to the page, bound to an input via v-model, or even evaluated as part of a JavaScript expression.
The magic of this reactivity system is all handled by Vue behind the scenes. If you want the full technical explanation of how this happens, you can read Reactivity in Depth. For a high-level explanation, though, we can boil it down to these two ingredients:
-
Dependency mapping When a Vue instance is set up, a map of dependencies for all of its data properties is created. For example, when we render a property in the template, that’s a dependency, and Vue keeps track of that so that when the property updates, so does the template.
-
Change notification Via a system of JavaScript proxies and getters/setters, whenever a property is changed, that change is propagated throughout the dependency map.
Having a high-level understanding of this reactivity system is important, because it’s the driving force of a shift in programming style from imperative to declarative programming.
Imperative vs. Declarative programming
Imperative
- You tell the program what you want to happen, step by step.
- Example: “Go to the store, go to the dairy aisle, add a carton of eggs to your cart, bring it to the checkout, pay for the eggs, bring them home.”
Declarative
- You describe what you want to happen, but you don’t necessarily explain the steps necessary to make it happen.
- Example: “Bring home eggs.”
Imperative JS example:
<label>What’s your name? <input type='text' id='nameInput'></label>
<p>Player: <span id='nameOutput'></span></p>
let nameInput = document.querySelector('#nameInput');
let nameOutput = document.querySelector('#nameOutput');
nameInput.addEventListener('input', function (evt) {
nameOutput.innerHTML = this.value;
});
This programming is very explicit in its instructions.
- It explicitly selects elements in the DOM.
- It explicitly sets up a listener for those elements.
- It explicitly specifies that when the listener is triggered, the output element is updated.
- Etc.
Declarative JS example:
<div id='app'>
<label>What’s your name? <input type='text' v-model='playerName'></label>
<p>Player: {{ playerName }}</p>
</div>
const Game = {
data() {
return {
playerName: '',
}
}
}
With declarative programming, the underlying mechanics of how the program works is obscured, so we can focus on the goals/outcomes of the program.
- We have a data point of
playerName
. - We tie that data point to the text input and the output on the page.
- Vue keeps them in sync.
In summary: Declarative programming is really just an abstraction. The details of how tasks are accomplished are abstracted by the language/framework we’re using, providing a more convenient pathway for us to accomplish our goals.
Vue abstracts the process of working with the DOM. Because of this, you should not see DOM-specific code within your Vue applications.