Vue 3 Tutorial: Lesson 3 – The Application Instance


Creating an Instance

Every Vue application starts by creating a new application instance with the createApp function:

Vue.createApp(someVueClassOrOptionsObject)

Or TypeScript:

import { createApp } from 'vue'
import someVueClass from './someVueClass.vue'

createApp(someVueClass)

After the instance is created, we can mount it, passing a container to mount method. For example, if we want to mount a Vue application on

, we should pass #app

Vue.createApp(someVueClassOrOptionsObject).mount('#app')

Or TypeScript:

import { createApp } from 'vue'
import someVueClass from './someVueClass.vue'

createApp(someVueClass).mount('#app')

Although not strictly associated with the MVVM pattern, Vue’s design was partly inspired by it. As a convention, we often use the variable vm (short for ViewModel) to refer to our instance.

When you create an instance, you pass in an options object. The majority of this guide describes how you can use these options to create your desired behavior. For reference, you can also browse the full list of options in the API reference.

A Vue application consists of a root instance created with createApp, optionally organized into a tree of nested, reusable components. For example, a example app’s component tree might look like this:

Root Instance
└─ ExampleList
   ├─ ExampleItem
   │  ├─ DeleteExampleButton
   │  └─ EditExampleButton
   └─ ExampleListFooter
      ├─ ClearExamplesButton
      └─ ExampleListStatistics

We’ll talk about the component system in detail later. For now, just know that all Vue components are also instances, and so accept the same options object.

Data and Methods

When an instance is created, it adds all the properties found in its data to Vue’s reactivity system. When the values of those properties change, the view will “react”, updating to match the new values.

When an instance is created, it adds all the properties found in its data to Vue’s reactivity system. When the values of those properties change, the view will “react”, updating to match the new values.

import { createApp } from 'vue'

var data = {
    value1: 1,
    value2: 2
}

var App = { 
    data(){
        return data
    }
}

const vm = createApp(App).mount('#app')
 
//Getting the property on the instance, returns the one from the original data
console.log(vm["value1"] === data.value1) // true
 
//Setting the property on the instance also affects the original data
vm.$data["value1"] = 3
//or
//vm["value1"] = 3

console.log(data.value1) //3

When this data changes, the view will re-render. It should be noted that properties in data are only reactive if they existed when the instance was created. That means if you add a new property, like:

vm["value3"] = 3

Then changes to value3 will not trigger any view updates. If you know you’ll need a property later, but it starts out empty or non-existent, you’ll need to set some initial value. For example:

data() {
  return {
    newTodoText: '',
    visitCount: 0,
    hideCompletedTodos: false,
    todos: [],
    error: null
  }
}

The only exception to this being the use of Object.freeze(), which prevents existing properties from being changed, which also means the reactivity system can’t track changes.

const obj = {
  foo: 'bar'
}

Object.freeze(obj)

const vm = Vue.createApp({
  data() {
    return obj
  }
}).mount('#app')
<div id="app">
  <p>{{ foo }}</p>
  <!-- this will no longer update `foo`! -->
  <button @click="foo = 'baz'">Change it</button>
</div>

In addition to data properties, instances expose a number of useful instance properties and methods. These are prefixed with $ to differentiate them from user-defined properties. For example:

const vm = Vue.createApp({
  data() {
    return {
      a: 1
    }
  }
}).mount('#app')

console.log(vm.$data["a"]) // 1

In the future, you can consult the API reference for a full list of instance properties and methods.

Instance Lifecycle Hooks

Each instance goes through a series of initialization steps when it’s created – for example, it needs to set up data observation, compile the template, mount the instance to the DOM, and update the DOM when data changes. Along the way, it also runs functions called lifecycle hooks, giving users the opportunity to add their own code at specific stages.

For example, the created hook can be used to run code after an instance is created:

import { Vue } from 'vue-class-component'

var data = {
  value1: 1,
  value2: 2
}
export default class App extends Vue {  
  data(){
    return data
  }
  created(){
    console.log("value1 is: "+this["value1"]) //value1 is: 1
  }
}

There are also other hooks which will be called at different stages of the instance’s lifecycle, such as mounted, updated, and unmounted. All lifecycle hooks are called with their this context pointing to the current active instance invoking it.

Don’t use arrow functions on an options property or callback, such as created: () => console.log(this.a) or vm.$watch('a', newValue => this.myMethod()). Since an arrow function doesn’t have a this, this will be treated as any other variable and lexically looked up through parent scopes until found, often resulting in errors such as Uncaught TypeError: Cannot read property of undefined or Uncaught TypeError: this.myMethod is not a function.

Lifecycle Diagram

Leave a Reply