DEV Community

Cover image for Vue 3: Common Options API things done the Composition way
Drew Clements
Drew Clements

Posted on

Vue 3: Common Options API things done the Composition way

As with all things new

For those living under a rock (or buried under legacy spaghetti), Vue 3 dropped in 2021 and in February of 2022 it was declared the default version moving forward. With it came the new composition API. The composition API brings a lot of changes in how we write, structure, and build Vue components.

This article is going to be looking at some of the more commonly used Vue features and what they look like in Vue 3, compared to their Vue 2 counterparts.

What we'll be looking at:

  • props
  • methods
  • data
  • computed properties
  • watchers

Props

Props are probably one of the most used features in Vue- or any framework really. Props are what allow passing data, whether it be static or dynamic, from component to component.

A common pattern is to define what props a component will be receiving, for a variety of reasons. Define types, whether or not it's required, and more importantly just so the component is aware of it and can be accessed in the template.

Here's what registering props in Vue 2 looks like:

<script>
  export default {
    props: {
      myProp: {
        type: String,
        default: "Hello",
      }
    }
  }
</script>
Enter fullscreen mode Exit fullscreen mode

There's nothing overly complicated about it. In fact, prop registration has never been a "difficult" thing in Vue.

Here's what registering props looks like in Vue 3:

<script setup>
const props = defineProps({
      myProp: {
        type: String,
        default: "Hello",
      }
})
</script>
Enter fullscreen mode Exit fullscreen mode

And both of these would be available in the template like so:

<template>
  <p>{{ myProp }}</p>
</template>
Enter fullscreen mode Exit fullscreen mode

Methods

In Vue 2, methods are where you would store functions for a component you would call elsewhere- either from the template or other lifecycle hooks: computed properties, watchers, mounted, etc.

The concept of methods doesn't exist in the context of script setup. You simply write the function you need and it's available!

Let's look at the Vue 2 way of doing this:

<script>
  export default {
    methods: {
      doTheThing() {
        console.log("The Thing")
      }
    }
  }
</script>
Enter fullscreen mode Exit fullscreen mode

And in Vue 3:

<script setup>
function doTheThing() {
  console.log("The Thing")
}
</script>
Enter fullscreen mode Exit fullscreen mode

And both of these would be available in the template like so:

<template>
  <button type="button" @click="doThething">Click me</button>
</template>
Enter fullscreen mode Exit fullscreen mode

Data

In Vue 2, there's a concept of the data object which is where you can define data points to use in your component as needed. These could be strings, arrays, objects- you name a data type and it can live here (within reason). Forgive my definition of it- this is how I explain it to myself. Think of it "state" for the component.

Here's the Vue 2 way:

<script>
  export default {
    data() {
      return {
        myText: 'Hello, I R Text'
      }
    }
  }
</script>
Enter fullscreen mode Exit fullscreen mode

There's actually two ways to accomplish this in Vue 3, with a ref or a reactive.

Reactive

Reactive objects are JavaScript Proxies and behave just like normal objects. The difference is that Vue is able to track the property access and mutations of a reactive object.
-Vue Docs

This is what a Reactive looks like in Vue 3:

<script setup>
import { reactive } from 'vue'

const state = reactive({count: 0})

console.log(state.count)
</script>
Enter fullscreen mode Exit fullscreen mode

This would be available in the template like so:

<template>
  <p>{{ state.count }}</p>
</template>
Enter fullscreen mode Exit fullscreen mode

There are some limitations to reactives. Check out the docs to learn more about what those are.

Ref

To address the limitations of reactive(), Vue also provides a ref() function which allows us to create reactive "refs" that can hold any value type
-Vue docs

Here's what a ref looks like in Vue 3:

<script setup>
import { ref } from 'vue';

const count = ref(0)

console.log(count.value)
</script>
Enter fullscreen mode Exit fullscreen mode

And this would be available in the template like so:

<template>
  <p>{{ count }}</p>
</template>
Enter fullscreen mode Exit fullscreen mode

Computed Properties

The concept of computed properties in Vue is to give you a way to "handle complex logic that includes reactive data" (official Vue documentation).

Let's say you want to display either the word 'open' or 'closed' depending on the time of day. That would be a lot of logic to cram into the template.

Let's look at what that would look like in a Vue 2 computed property:

// for brevity's sake we're going to pretend a data property isBeforeFive and isWeekend exists and returns true or false

<script>
  export default {
    computed: {
      isOpen() {
        return isBeforeFive && !isWeekend ? 'Open' : 'Closed';
      }
    }
  }
</script>
Enter fullscreen mode Exit fullscreen mode

Obviously this example is a little contrived, but you get the idea here.

Here's that same thing in Vue 3:

<script setup>
import { computed } from 'vue';

const isOpen = computed(() => {
  return isBeforeFive && !isWeekend ? 'Open' : 'Closed';
}
</script>
Enter fullscreen mode Exit fullscreen mode

Both of these would be available in the template like so:

<template>
  <p>{{ isOpen }}</p>
</template>
Enter fullscreen mode Exit fullscreen mode

Watchers

Last but not least of what we're covering today are watchers. Watchers can be an extremely useful tool in the arsenal because they allow you 'to perform "side effects" in reaction to state changes'(official Vue Documentation).

For example: let's say we want to trigger an alert anytime a counter is updated.

Here's that in Vue 2:

<script>
  export default {
    data() { 
     return {
      count: 0
    },
    watch: {
      count(newVal, oldVal) {
        if(newVal > oldVal) alert('Count ticked up!')
      }
    }
  }
</script>
Enter fullscreen mode Exit fullscreen mode

Here it is in Vue 3:

<script setup>
import { ref, watch } from 'vue'

const count = ref(0)

watch(count, (newVal, oldVal) => {
  if(newVal > oldVal) alert('Count ticked up!')
})
</script>
Enter fullscreen mode Exit fullscreen mode

Each of these will trigger an alert anytime the new value of count goes up.

Summary

Vue 3 seems like it's going to be a lot of fun. I can see where the simplicity of script setup will come in handy- but I'm also a huge creature of habit and I like how my Options API has kept things organized for me.

I used to ask "what's an edge case in which I should use composition over options?" After diving into it in more depth, to me, the answer is that there isn't an edge case that will ever force your hand into one or the other and that each has their merits.

Decide for yourself which one you prefer and use that one.

The Vue team has done an amazing job building a useful framework from the beginning and the composition API is just the next iteration of their efforts.

Top comments (0)