Logo
Published on

The Differences: Vue.js 2 vs. Vue.js 3, What You Need to Know

Authors
  • Name
    Twitter

Good morning, my friends. In this article, we’re going to introduce the differences between Vue 2.x and Vue 3.x, making it easy for those who are already familiar with Vue.js 2.x to quickly understand the distinctions and relationships between the two versions.

Here, I will develop a simple table component using both Vue 2 and Vue 3 to demonstrate the differences in developing components with these two versions. By the end of this article, you’ll have a concept of the disparities in component development between Vue 2 and Vue 3 and be prepared for your journey with Vue 3.

Creating a Template with VueJs

For components, most of the code in Vue 2 and Vue 3 is very similar. Vue 3 introduces Fragments, which means components can have multiple root nodes. This new feature can reduce the need for unnecessary div wrappers between components.

In Vue 2 development, you may have noticed that each component is often wrapped in a div element, resulting in many layers of unnecessary div elements. Fragments address this issue.

Although we won’t show an example here, let’s stick with simple single-root-node components.

Vue 2 Table Template

<template>
  <div class="form-element">
    <h2>{{ title }}</h2>
    <input type="text" v-model="username" placeholder="’Username’" />

    <input type="’password’" v-model="’password’" placeholder="’Password’" />
    <button @click="’login’">Submit</button>
    <p>Values: {{ username + ' ' + password }}</p>
  </div>
</template>

The only real difference in Vue 3 is data retrieval. In Vue 3, reactive Data is contained within a reactive state variable, so we need to access this reactive state to get the data values.

Vue 3 Table Template

<template>
  <div class="’form-element’">
    <h2>{{ state.title }}</h2>
    <input type="’text’" v-model="’state.username’" placeholder="’Username’" />

    <input
      type="’password’"
      v-model="’state.password’"
      placeholder="’Password’"
    />
    <button @click="’login’">Submit</button>
    <p>Values: {{ state.username + ‘ ‘ + state.password }}</p>
  </div>
</template>

Establishing Data (Data)

This is where Vue 2 and Vue 3 differ the most. Vue 2 uses the Options API, while Vue 3 uses the Composition API. The old Options API separates different properties: data, computed properties, methods, etc. The new Composition API allows us to use functions to split our code, making it more concise and tidy. Let’s compare Vue 2 and Vue 3 code to see the difference.

Vue 2 — Putting two data properties in the data object:

export  default {
props: {
title: String
},
data () {
return {
username: ‘’,
password: ‘’
}
}
}

In Vue 3, we use a new setup() method, which is triggered when the component is initially constructed. To gain more control over reactive data, we can use Vue 3’s reactivity API directly. Here’s how to create reactive data:

import { reactive } from ‘vue’
export default {
 props: {
   title: String
 },
 setup () {
   const state = reactive({
     username: ‘’,
     password: ‘’
   })
   return { state }
   }
}

The reactive data constructed here can be used in the template via state.username and state.password.

Methods in Vue 2 vs. Vue 3

In Vue 2, the Options API allows us to define methods directly within the methods property. Here’s an example of how to do it:

Vue 2 — Defining a method within the methods property:

export  default {
props: {
title: String
},
data () {
return {
username: ‘’,
password: ‘’
}
},
methods: {
login () {
// Login method
}
}
}

In Vue 3, the Composition API’s setup() method can also be used to define methods. Defining a method is similar to defining data — we declare a method and return it within setup(). This allows the method to be called within the component.

Vue 3 — Defining a method using setup():

export  default {
props: {
title: String
},
setup () {
const state = reactive({
username: ‘’,
password: ‘’
})
const  login = () => {
// Login method
}
return {
login,
state
}
}
}

Lifecycle Hooks

In Vue 2, you can directly call Vue’s lifecycle hooks within the component options. Here’s an example using the mounted hook to trigger a function after the component is mounted:

Vue 2 — Using the mounted hook

export default {
 props: {
   title: String
 },
 data () {
   return {
     username: ‘’,
     password: ‘’
   }
 },
 mounted () {
   console.log(‘Component has been mounted’)
 }
}

In Vue 3, the Composition API’s setup() method contains most of what you need, including lifecycle hooks. However, in Vue 3, lifecycle hooks are no longer globally accessible; you need to import them separately. For example, you can use onMounted to handle the mounted hook:

Vue 3 — Using onMounted within setup():

import { reactive, onMounted } from ‘vue’
export  default {
props: {
title: String
},
setup () {
// …
onMounted(() => {
console.log(‘Component has been mounted’)
})
// …
}
}

Computed Properties

Let’s try adding a computed property to transform the username into lowercase in both Vue 2 and Vue 3.

In Vue 2, defining a computed property is straightforward. We add it to the computed property section of the component:

Vue 2 — Defining a computed property:

export default {
  // ..
  computed: {
    lowerCaseUsername() {
      return this.username.toLowerCase();
    },
  },
};

In Vue 3, things are a bit different. Vue 3 allows for more granular control over what dependencies are imported, avoiding unnecessary imports that can affect performance or bundle size. In Vue 3, you need to explicitly import computed from Vue, and then you can define a computed property within setup():

import { reactive, onMounted, computed } from ‘vue’
export  default {
props: {
title: String
},
setup () {
const state = reactive({
username: ‘’,
password: ‘’,
lowerCaseUsername: computed(() => state.username.toLowerCase())
})
// …
}
}

Receiving Props

Receiving and using props is where the most significant difference between Vue 2 and Vue 3 lies. In Vue 2, this refers to the current component, not a specific property. Therefore, you can directly use this to access prop values.

For example, in Vue 2, you can access and log the title prop like this:

Vue 2 — Accessing and logging a prop:

mounted () {
console.log(‘title:+ this.title)
}

In Vue 3, this no longer directly accesses props, emits events, or other component properties. Instead, Vue 3’s new setup() method receives two parameters:

  • props: Immutable component props.
  • context: Exposed Vue 3 properties (emit, slots, attrs).

So, in Vue 3, receiving and using props looks like this:

Vue 3 — Receiving and using props within setup():

setup (props) {
// …
onMounted(() => {
console.log(‘title:+ props.title)
})
// …
}

Emitting Events

Custom events in Vue 2 are quite straightforward, but Vue 3 provides more freedom and control over emitting events.

For example, suppose we want to trigger a login event when the submit button is clicked. In Vue 2, we call this.$emit and pass the event name and a parameter object:

Vue 2 — Emitting a custom event:

login () {
this.$emit(‘login’, {
username: this.username,
password: this.password
})
}

In Vue 3, since this no longer directly accesses component properties, including $emit, we need to handle custom events differently.

Fortunately, the setup() method’s second argument, the context object, contains an $emit method that works similarly to $emit in Vue 2. We can use it to emit custom events within setup():

Vue 3 — Emitting a custom event within setup():

setup (props, { emit }) {
// …
const  login = () => {
emit(‘login’, {
username: state.username,
password: state.password
})
}
// …
}

Conclusion

In conclusion, I believe Vue 3 brings a whole new development experience to front-end developers, offering greater flexibility and control. If you’ve learned or worked with React and are now exploring Vue, you should be excited because many usage patterns are now very similar!

The new Composition API allows for more decoupled code, especially in large front-end applications where this effect becomes more pronounced. Additionally, on-demand imports provide finer control, allowing for better management of project performance and bundle size.

I hope this article has been helpful to you.

This is the second article in my Vue.js Base Knowledge series, which aims to help a Vue.js beginner to becoming an expert.

This article will also be added to my Vue.js Base Knowledge” library. If you’re interested, please feel free to follow me and the library.