Categories
JavaScript JavaScript Basics

JavaScript Best Practice — Replacing Old with New

Like any other programming language, JavaScript has its own list of best practices to make programs easier to read and maintain. There are a lot of tricky parts to JavaScript, and we can follow some best practices to improve our code.

Since ES6 was introduced, new constructs are replacing older ones for good reasons. It’s much shorter, cleaner, and easier to understand.

In this article, we’ll look at which older constructs that can be replaced with new ones, including replacing then with async and await, replacing dictionary objects with Maps, replacing apply with the spread operator, and replacing function constructors with the class syntax.

Replace Then with Async / Await

When we chain promises, we used to do it by using the then method and then returning another promise in the callback function that we pass into then.

This means that we have code that looks something like this:

Promise.resolve(1)  
  .then((val) => {  
    console.log(val);  
    return Promise.resolve(2);  
  })  
  .then((val) => {  
    console.log(val);  
    return Promise.resolve(3);  
  })  
  .then((val) => {  
    console.log(val);  
  })

With the introduction of the async and await syntax, which is just a shorthand for calling the then method repeatedly. We can write the code above like this:

(async () => {  
  const val1 = await Promise.resolve(1);  
  console.log(val1);  
  const val2 = await Promise.resolve(2);  
  console.log(val2);  
  const val3 = await Promise.resolve(3);  
  console.log(val3);  
})();

They both output 1, 2 and 3, but the second code snippet is so much shorter. It’s so much clearer that there’s no reason to go back to the old syntax.

Also, we can loop through promises and run them one after the other by using the for-await-of loop. We can loop through the promises we have above by rewriting the code like the following:

(async () => {  
  const promises = [  
    Promise.resolve(1),  
    Promise.resolve(2),  
    Promise.resolve(3),  
  ] 

  for await (let p of promises) {  
    const val = await p;  
    console.log(val);  
  }  
})();

Code like the one we have above is very handy for looping through many promises or promises that are created on the fly, which we can’t do in the earlier examples.

Replacing Dictionary Objects with Maps

ES6 also introduced the Map constructor, which lets us create hash maps or dictionaries without using JavaScript objects with string keys to do so.

Maps are also better because they have their own methods to get and manipulate the entries.

For example, instead of writing:

const dict = {  
  'foo': 1,  
  'bar': 2  
}

We write:

const dict = new Map([  
  ['foo', 1],  
  ['bar', 2]  
])

Then we can get an entry by using the get method as follows:

console.log(dict.get('foo'));

We can set the value of an existing entry by the key of the entry with the set method:

dict.set('foo', 2);

Also, we can check if an entry exists with the given key with the has method:

dict.has('baz');

There are also the keys and entries methods to get all the keys of the map and all the entries respectively.

For example, we can write:

console.log(dict.keys());

To get an iterator object with the keys of the Map . This means that we can loop through them with the for...of loop or convert it to an array with the spread operator.

Similarly, the entries method returns an iterator object with all the entries in the Map with each entry being an array of [key, value] .

There’s also the value method to get an iterator object with all the values in the Map .

We can also use other primitive values as keys. If we use objects, we can’t get the value back when we look them up since the lookup is done with the === operator which returns false is 2 objects that don’t have the same reference even if they have the same content.

These methods aren’t available in a regular object. Also, we might accidentally get or modify the object’s prototype’s properties instead of its own if we use the for...in loop.

Therefore, there aren’t many reasons to use a regular object as a dictionary anymore.

Replace Apply with Spread

If we don’t want to change the value of this inside the function, there isn’t much reason to use the apply method in functions.

If we only want to call a function with many arguments, we can use the spread operator when passing in an array for our arguments as follows:

const arr = [1, 2, 3, 4, 5];  
const add = (...args) => args.reduce((a, b) => a + b, 0);  
console.log(add(...arr));

All we have to do is to use the spread operator on our array, which is the 3 dots operator in the last line, and then the array will be separated into a comma-separated list of arguments.

Replace Constructor Functions with Classes

Another great ES6 feature is the class syntax for constructor functions. It’s simply syntactic sugar that makes the constructor function looks like a class.

The advantage of it is that it makes inheritance easy.

For example, if we want to inherit from a constructor function, we have to write something like this:

function Person(name) {  
  this.name = name;  
}

function Employee(name, employeeCode) {  
  Person.call(this, name);  
  Employee.prototype.constructor = Person;  
  this.employeeCode = employeeCode;  
}

const employee = new Employee('Joe', 1);  
console.log(employee)

This syntax looks strange coming from class-based object-oriented languages like Java.

However, the class syntax makes things look a lot familiar to developers that used other languages more than JavaScript. We can rewrite the code above to the following:

class Person {  
  constructor(name) {  
    this.name = name;  
  }  
}

class Employee extends Person {  
  constructor(name, employeecode) {  
    super(name);  
    this.employeecode = employeecode;  
  }  
}

const employee = new Employee('Joe', 1);  
console.log(employee)

The code does the same thing as what we have above. However, it’s clearer what we’re inheriting from since we have the extends keyword to indicate what we’re inheriting from.

With the constructor function, we have to worry about the value of this in the first argument of the call method, and what we pass into the subsequent arguments of call.

With the class syntax, we don’t have to worry about this. If we forgot to make the super call like the following code:

class Person {  
  constructor(name) {  
    this.name = name;  
  }  
}

class Employee extends Person {  
  constructor(name, employeecode) {     
    this.employeecode = employeecode;  
  }  
}

const employee = new Employee('Joe', 1);  
console.log(employee)

We’ll get the error ‘Uncaught ReferenceError: Must call super constructor in derived class before accessing ‘this’ or returning from derived constructor.’

It’s one less chance of making a mistake.

We don’t get any error if we omit the Person.call like in the Employee constructor function since the browser doesn’t know we want Employee to inherit from Person .

In addition, when we log the prototype of employee , we get that the prototype of employee is Person as expected with the class syntax.

However, we don’t get that unless we put Employee.prototype.constructor = Person; which is easily forgotten.

Conclusion

async and await and for-await-of are new constructs that make chaining promises much cleaner. It’s much better to use them instead of using then because of it.

for-await-of also lets us loop through promises that are generated dynamically which we can’t do with then or async and await alone.

Map s are much better than plain objects for hashes and dictionaries because it has its own methods to manipulate and get the entries. Also, we may accidentally be accessing the properties of prototype of plain objects.

If we don’t want to change the value of this inside a function, we can replace the apply method for calling functions with an array of arguments with the spread operator, which does the same thing.

Finally, the class syntax for constructors is much better than the original function syntax since we can inherit from parent classes easier than setting the prototype constructor of a constructor function.

Categories
JavaScript Vue

Introduction to Vue.js Templates

Vue.js is an easy to use web app framework that we can use to develop interactive front end apps.

In this article, we’ll look at how to create and use templates to display data and handle events.

How it Works

Vue templates let us bind data to the DOM.

Vue compiles the template into Virtual DOM render functions. It can figure out the minimal number of components to re-render and minimize DOM manipulation when data changes.

Interpolations

The most basic form of data binding is text interpolation with double curly braces.

For example, we can write:

<p>{{foo}}</p>

To show the value of foo from a Vue instance or component.

The code above will be updated when foo updates.

We can keep it from updating after the first render by using the v-once directive as follows:

<p v-once>{{foo}}</p>

Raw HTML

We can also put raw HTML into an element with the v-html directive.

This lets us add HTML elements and formatting into an element.

For example, if we have the following in src/index.js :

new Vue({  
  el: "#app",  
  data: {  
    rawHtml: "<b>bar</b>"  
  }  
});

and the following in index.html :

<!DOCTYPE html>  
<html>  
  <head>  
    <title>Hello</title>  
    <meta charset="UTF-8" />  
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>  
  </head> 
  <body>  
    <div id="app">  
      <p v-html="rawHtml"></p>  
    </div>  
    <script src="./src/index.js"></script>  
  </body>  
</html>

Then we get bar bolded displayed since the inner HTML of the p element is <b>bar</b> .

When we’re using this, we have to sanitize the HTML so that it won’t contain any code. This is to prevent cross-site scripting attacks since code will be run if there are any when we pass in a value to the v-html directive.

Attributes

We can set values of HTML element attributes dynamically with v-bind as follows.

In src/index.js , we write:

new Vue({  
  el: "#app",  
  data: {  
    isButtonDisabled: true  
  }  
});

Then when we write the following in index.html :

<!DOCTYPE html>  
<html>  
  <head>  
    <title>Hello</title>  
    <meta charset="UTF-8" />  
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>  
  </head> 
  <body>  
    <div id="app">  
      <button v-bind:disabled="isButtonDisabled">Button</button>  
    </div>  
    <script src="./src/index.js"></script>  
  </body>  
</html>

Then since we set the isButtonDisabled property to true , the button will be disabled. This also apply to any other truthy value.

On the other hand, if we set it to false , we see that the button is disabled. This also applies to other falsy values like null , undefined , 0 and empty string.

JavaScript Expressions

We can add any other JavaScript expressions between the curly braces.

For example, we can write:

{{ number + 10 }}  
{{ yes ? 'YES' : 'NO' }}  
{{ str.split('').reverse('').join('') }}

Also, we can write expressions as the value of the v-bind directive as follows:

<div v-bind:id="`div-${id}`"></div>

Statements will not run inside the curly braces. For example:

{{ let a = 1 }}

will give us an error. We’ll get the error ‘avoid using JavaScript keyword as the property name: “let”’ in the console.

We also can’t run conditional statements within curly braces:

{{ if (id) { return id } }}

We’ll get the error ‘avoid using JavaScript keyword as the property name: “if”’.

Directives

Directives are special attributes with the v- prefix. We can pass in a single JavaScript expression as the value.

v-for can accept more than one expression separated by ; .

For example, we can conditionally display content with v-if :

<p v-if="show">Hello</p>

If show is truthy, then we’ll see Hello . Otherwise, it won’t be shown.

Arguments

Some directive takes an argument, which is denoted by a colon after the directive name.

For example, we can set the href value of an a element by writing:

<a v-bind:href="'https://medium.com'">Medium </a>

The v-on directive also takes an argument. For example, we can handle the click event of an element by writing:

<a v-on:click="onClick"> Click Me </a>

Then the onClick method in our Vue instance or component will be run when we click on the a element.

Dynamic Arguments

Since Vue.js 2.6.0, we can use a JavaScript expression as an argument for a directive.

For example, we can use it as follows. In src/index.js , we write:

new Vue({  
  el: "#app",  
  data: {  
    click: `${"cl"}${"ick"}`  
  },  
  methods: {  
    onClick() {  
      alert("clicked");  
    }  
  }  
});

Then in index.html , we can use a dynamic argument as follows:

<!DOCTYPE html>  
<html>  
  <head>  
    <title>Hello</title>  
    <meta charset="UTF-8" />  
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>  
  </head> 
  <body>  
    <div id="app">  
      <a v-on:click="onClick"> Click Me </a>  
    </div>  
    <script src="./src/index.js"></script>  
  </body>  
</html>

This will reference the value of the click property of the data property in our Vue instance, and so we actually have v-on:click .

And therefore, when we click the a tag, we get an alert box with the clicked text displayed.

Dynamic argument expressions must evaluate to a string or null .

Spaces and quotes also aren’t valid inside the brackets.

Conclusion

We can create templates in various ways. The most basic is interpolation with the double curly brace syntax. We can put any JavaScript expression between the braces.

To set the values of attributes dynamically, we can use the v-bind directive. The value can be any JavaScript expression.

Some directives take an argument, which is the part of the directive name after the colon. Again the directive’s value can be any JavaScript expression in this case.

Dynamic arguments can be used since Vue 2.6.0. We can use it by putting an expression in square brackets.

Categories
JavaScript JavaScript Basics

Why Should We Be Careful When Using the const JavaScript Keyword?

Since ES6 was released, lots of new features have been added to the JavaScript. One of them is the const keyword for declaring constants. It lets is create constants that can’t be reassigned to something else.

However, we can still change it in other ways. In this article, we’ll look at the often-overlooked characteristics of const and how to deal with the pitfalls.

Characteristics of const

const is a block-scope keyword for declaring constants that’s available within the block that it’s defined in. It can’t be used before it’s declared. This means that there’s no hoisting.

They can’t be reassigned and can’t be declared.

For example, we can declare a constant as follows:

const foo = 1;

We can’t access it before declaration, so:

console.log(foo);  
const foo = 1;

will get us the error “Uncaught ReferenceError: Cannot access ‘foo’ before initialization”

The scope can be global or local. However, when it’s global, it’s not a property of the window object, so it can’t be accessed from other scripts within the app.

It’s a read-only reference to a value. This means that it’s important to note that the object may still be mutable. This is something that’s often overlooked when we declare constants. The properties of objects declared with const can still be changed, and for arrays, we can add and remove entries from them.

For example, we can write:

const foo = {  
  a: 1  
};  
foo.a = 1;  
console.log(foo.a);

Then we get 1 from the console.log .

We can also add properties to an existing object declared with const :

const foo = {  
  a: 1  
};  
foo.b = 1;  
console.log(foo.b);

Again, we get 1 from the console.log .

Another example would be array manipulation. We can still manipulate arrays declared with const , just like any other object:

const arr = [1, 2, 3];  
arr[1] = 5;  
console.log(arr)

The console.log will be us [1, 5, 3] .

We can also add entries to it by writing:

const arr = [1, 2, 3];  
arr[5] = 5;  
console.log(arr)

then we get:

[1, 2, 3, empty × 2, 5]

As we can see, objects declared with const are still mutable. Therefore, if we want to prevent accidentally changing objects declared with const , we have to make them immutable.

Making Objects Immutable

We can make objects immutable with the Object.freeze method. It takes one argument, which is the object that we want to freeze. Freezing means that rhe properties and values can’t be changed.

Property descriptors of each property also can’t be changed. This means the enumerability, configurability, and writability also can’t be changed, in addition to the value.

A property being configurable means that the properties descriptors above can be changed and the property can be deleted.

Enumerability means that we can loop through the property with the for...in loop.

Writability means that the property value can be assigned.

Object.freeze prevents all that from happening by setting everything except enumerable to false .

For example, we can use it as follows:

const obj = {  
  prop: 1  
};Object.freeze(obj);  
obj.prop = 3;

In strict mode, the code above will get us an error. Otherwise, obj will stay unchanged after the last line.

Note that Object.freeze only freeze the properties at the top level of an object, so if we run:

/* 'use strict'; */  
const obj = {  
  prop: 1,  
  foo: {  
    a: 2  
  }  
};  
Object.freeze(obj);  
obj.foo.a = 3;

We’ll get:

{  
  "prop": 1,  
  "foo": {  
    "a": 3  
  }  
}

As we can see, obj.foo.a still changed after we called Object.freeze on it.

Therefore, to make the whole object immutable, we have to recursively call Object.freeze on every level.

We can define a simple recursive function to do this:

const deepFreeze = (obj) => {  
  for (let prop of Object.keys(obj)) {  
    if (typeof obj[prop] === 'object') {  
      Object.freeze(obj[prop]);  
      deepFreeze(obj[prop]);  
    }  
  }  
}

Then when we call deepFreeze instead of Object.freeze as follows:

const obj = {  
  prop: 1,  
  foo: {  
    a: 2  
  }  
};  
deepFreeze(obj);  
obj.foo.a = 3;

We get that that obj stays unchanged:

{  
  "prop": 1,  
  "foo": {  
    "a": 2  
  }  
}

The deepFreeze function just loops through all the own properties of obj and then call Object.freeze on it, if the value of the property is an object, then it calls deepFreeze on deeper levels of the object.

Primitive values are always immutable including strings, so we don’t have to worry about them.

Another good thing about freezing an object with Object.freeze is that there’s no way to unfreeze it since the configurability of the properties is set to false , so no more changes can be made to the structure of the object.

To make an object mutable again, we’ve to make a copy of it and assign it to another object.

Now that we know that const doesn’t actually make everything constant, we’ve to be careful when we use const . We can’t assign new values to an existing constant. However, we can still change the property values of an existing object assigned to a constant.

Constants declared with const aren’t actually constant. To make it actually constant, or immutable, we’ve to call Object.freeze on each level of the object declared with const to make sure it’s actually constant.

This only applies to object-valued properties. Primitives are immutable by definition so we don’t have to worry about them.

Categories
JavaScript Vue

Lifecycle Hooks of the Vue Instance

Vue.js is an easy to use web app framework that we can use to develop interactive front end apps.

In this article, we’ll look at the lifecycle hooks of a Vue instance.

Vue Instance Lifecycle Hooks

Each Vue instance goes through a lifecycle. The lifecycle events can be handled with functions, which are the lifecycle hooks.

For example, when a Vue instance is created, the created hook is called.

We can handle the created event emitted by Vue.js as follows:

new Vue({  
  data: {  
    foo: "bar"  
  },  
  created() {  
    console.log(`foo is ${this.foo}\`);  
  }  
});

Then we should see foo is bar from the console.log . Once the Vue instance is created and the initial data is set, the created hook is called.

This is why we see such output from the console.log .

Note that we can’t use arrow functions for lifecycle hooks since we reference the properties from this , which is the Vue instance.

There’re also other lifecycle hooks like mounted , updated , destroyed .

Lifecycle Diagram

The full lifecycle diagram is below. It outlines the whole workflow for creating a new Vue instance.

The red rounded rectangles have the hooks that are called.

Courtesy of vuejs.org

Creation Hooks

Creation hooks are called when the Vue instance begins its initialization process. It lets us do things before the Vue instance is added to the DOM.

This hook is also run during server-side rendering

We don’t have access to this.$el (the target mounting element) or the DOM within this hook since no DOM manipulation is done at this point.

beforeCreate

The beforeCreate hook runs during initialization. data has been made reactive and events aren’t set up yet.

For example, if we have:

new Vue({  
  data: {  
    foo: "bar"  
  },  
  beforeCreate() {  
    console.log("beforeCreated called");  
  },  
  created() {  
    console.log(`foo is ${this.foo}`);  
  }  
});

Then we see:

beforeCreated called  
foo is bar

which confirms that beforeCreate is called before created .

created

data and events are available in the created hook since the Vue instance has been initialized.

For example, if we write:

new Vue({  
  data: {  
    foo: "bar"  
  },  
  created() {  
    this.foo = "baz";  
    console.log(`foo is ${this.foo}`);  
  }  
});

Then we get:

foo is baz

since we changed the value of this.foo in the created hook before logging its value.

Mounting Hooks

The mounting hooks are run when DOM manipulation is being done, including mounting the Vue instance to the specified element.

We can use it to access or modify the DOM of our component.

beforeMount

The beforeMount hook runs before the initial render happens and after the template or render functions are compiled.

It doesn’t get called during server-side rendering.

For instance, if we write:

new Vue({  
  el: "#app",  
  data: {  
    foo: "bar"  
  },  
  created() {  
    console.log(`created called`);  
  },  
  beforeMount() {  
    console.log(`beforeMount called`);  
  }  
});

Then we get:

created called  
beforeMount called

mounted

When the mounted hook is called, we have access to the component, template, and the DOM.

It’s used frequently for the initialization of our own data like fetching data over the network and integrate non-Vue libraries.

For example, if we have the following in src/index.js:

new Vue({  
  el: "#app",  
  data: {  
    foo: "bar"  
  },  
  beforeMount() {  
    console.log(this.$el.textContent);  
  }  
});

and the following in index.html :

<!DOCTYPE html>  
<html>  
  <head>  
    <title>Hello</title>  
    <meta charset="UTF-8" />  
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>  
  </head> 
  <body>  
    <div id="app">foo</div>  
    <script src="./src/index.js"></script>  
  </body>  
</html>

Then we get foo logged since we have foo in the div with ID app .

Updating Hooks

Updating hooks are called when a reactive property used by our component changes or we some triggers re-rendering manually.

They’re useful for checking when a component is re-rendering.

However, we should use computed properties or watchers to track reactive property changes.

beforeUpdate

beforeUpdate runs after data changes in our component, right before the DOM is updated and re-rendered.

It lets us get the new state before the DOM is re-rendered.

For example, if we have the following in src/index.html :

new Vue({  
  el: "#app",  
  data: {  
    foo: "bar"  
  },  
  beforeUpdate() {  
    console.log(this.foo);  
  },  
  methods: {  
    toggle() {  
      this.foo = this.foo === "bar" ? "baz" : "bar";  
    }  
  }  
});

and the following in index.html :

<!DOCTYPE html>  
<html>  
  <head>  
    <title>Hello</title>  
    <meta charset="UTF-8" />  
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>  
  </head> <body>  
    <div id="app">  
      <p>{{foo}}</p>  
      <button @click="toggle">Toggle</button>  
    </div>  
    <script src="./src/index.js"></script>  
  </body>  
</html>

Then when we click the Toggle button, we can see the value changes logged in the console.log call of the beforeUpdate method.

updated

The updated hook runs after data changes on our Vue instance and the DOM re-renders.

For example, if we have the following in src/index.js:

new Vue({  
  el: "#app",  
  data: {  
    foo: "bar"  
  },  
  updated() {  
    console.log(this.$refs.foo.textContent);  
  },  
  methods: {  
    toggle() {  
      this.foo = this.foo === "bar" ? "baz" : "bar";  
    }  
  }  
});

and the following in index.html :

<!DOCTYPE html>  
<html>  
  <head>  
    <title>Hello</title>  
    <meta charset="UTF-8" />  
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>  
  </head> <body>  
    <div id="app">  
      <p ref="foo">{{foo}}</p>  
      <button @click="toggle">Toggle</button>  
    </div>  
    <script src="./src/index.js"></script>  
  </body>  
</html>

Then we can get the text content of the p element with ref set to foo by logging the value as follows:

console.log(this.$refs.foo.textContent);

in the updated hook and we can see the new value for it as we click the Toggle button.

Destruction Hooks

These hooks are run when the Vue instance is destroyed. Therefore, we can run clean up code in there.

These hooks aren’t run during server-side rendering.

beforeDestroy

beforeDestroy is run before teardown. Our Vue instance is still present and functional.

Therefore, we can run our clean up code in here.

For example, we can use it as follows:

new Vue({  
  el: "#app",  
  data: {  
    foo: "bar"  
  },  
  beforeDestroy() {  
    this.foo = null;  
  }  
});

destroyed

This is called after the Vue instance is destroyed. All the directives are unbound, event listeners are removed and child Vue instances are destroyed.

For example, we can use it as follows:

new Vue({  
  el: "#app",  
  data: {  
    foo: "bar"  
  },  
  destroyed() {  
    this.foo = null;  
  }  
});

Conclusion

The lifecycle hooks are useful for doing things during the lifecycle of the Vue instance.

The creation hooks can be used to run any initialization code.

The mounting hooks are run when the Vue instance is being mounted into the DOM.

The update hooks whenever data of the Vue instance updates, and the destroy hooks are run when the Vue instance is being destroyed.

Categories
JavaScript Vue

Vue.js Basics — The Vue Instance

Vue.js is an easy to use web app framework that we can use to develop interactive front end apps.

In this article, we’ll look more closely at the Vue instance, including how to define it and some properties of it.

Characteristics of the Vue Instance

Each Vue.js app begins by defining a new Vue instance. The Vue constructor takes an options object that takes various properties.

We often use vm to refer to a Vue instance, where vm stands for ViewModel.

A Vue app roughly follows the Model-View-ViewModel pattern, where the ViewModel has the business logic of the app, View has the markup that users see, and Model has the data.

For example, we can define a Vue instance as follows:

const vm = new Vue({ });

Each Vue app consists of a root Vue instance and it’s created with new Vue . We can organize it in a tree for easier maintenance.

Data and Methods

The options object that pass into the Vue constructor can have data and methods.

For example, if we define a Vue instance as follows:

const vm = new Vue({  
  el: "#app",  
  data: { foo: "bar" }  
});

Then when we add:

console.log(vm.foo);

below our vm definition, we get 'bar' since data.foo has the value 'bar' .

In other words, if we have:

const data = { foo: "bar" };  
const vm = new Vue({  
  el: "#app",  
  data  
});  
console.log(vm.foo === data.foo);

Then the console.log will log true .

When the data changes, the app will re-render with the new data.

If we create a new property in vm and set it as follows:

let data = { foo: "bar" };  
const vm = new Vue({  
  el: "#app",  
  data  
});  
vm.a = 1;

The app won’t re-render. On the other hand, if we write:

let data = { foo: "bar", a: 1 };  
const vm = new Vue({  
  el: "#app",  
  data  
});

Then the app will re-render. This means that we have to put our data that we want to render in the data field.

If we freeze the object that we pass to data with Object.freeze() , then the Vue app won’t re-render since properties can’t be changed, so new changes can’t propagate since they aren’t set in the object.

So if we have:

let data = { foo: "bar", a: 1 };  
Object.freeze(data);  
const vm = new Vue({  
  el: "#app",  
  data  
});

No changes can be made after the initial render since we froze data with Object.freeze .

The Vue instance also exposes a number of instance properties and methods.

They’re prefixed with the $ so that we know they’re part of the Vue instance.

$el

We have the $el property to get the DOM element that the Vue instance resides in.

For example, if we have:

let data = { foo: "bar" };  
const vm = new Vue({  
  el: "#app",  
  data  
});

console.log(vm.$el === document.getElementById("app"));

Then the console.log will log true since our Vue instance resides in an element with ID app .

$data

The $data property will get us the value of the data property that we set in the options object that we passed into the Vue constructor.

So if we have:

let data = { foo: "bar" };  
const vm = new Vue({  
  el: "#app",  
  data  
});

console.log(vm.$data === data);

Then we get true from the console.log since data references the same object as vm.$data since we set it as the value of the data property of the options object that we passed into the Vue constructor.

$watch

$watch is an instance method that lets us watch for changes in the data object that we set as the value of the options object.

For example, if we have:

let data = { foo: "bar" };  
const vm = new Vue({  
  el: "#app",  
  data  
});

vm.$watch("foo", (newValue, oldValue) => {  
  console.log(newValue, oldValue);  
});

in src/index.js and:

<!DOCTYPE html>  
<html>  
  <head>  
    <title>Hello</title>  
    <meta charset="UTF-8" />  
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>  
  </head> <body>  
    <div id="app">  
      <input type="text" v-model="foo" />  
    </div>  
    <script src="./src/index.js"></script>  
  </body>  
</html>

in index.html . Then when we type in something to the input box, we should get some console.log output from:

vm.$watch("foo", (newValue, oldValue) => {  
  console.log(newValue, oldValue);  
});

This is because changes are constantly being watched in the data.foo property. v-model automatically updates the value for foo as we type into the box.

So as we’re typing, the changes to foo are being watched and it’s logged by the handler function that we passed into the $watch method.

Conclusion

The Vue instance is created by passing in an options object with the data , el , and methods properties to the Vue constructor. It returns an instance of our Vue app.

The instance has the $el property to get the DOM element that our app resides in.

Also, there’s the $data property to get the value of the data property to get the data that we set when we pass it in as the value of the data property of the options object.

Finally, we have the $watch method to watch for changes in our data.