Why vue3 do I need the Composition API?

What is the Composition API?

Vue 3 introduced the Composition API, which has since swept the entire community. In my opinion, the only best feature of the Composition API is the ability to extract reaction states and functions into their own reusable modules or "composable".

What is composable?

So, what is Vue.js Composable? You can almost think of composition as a hybrid API equivalent of the option API. They provide a way to define reactive data and logic separate from any particular component. Not only that, they are doing better... Much better. In addition, they have done more.

Similar problems solved by Composables and Mixin

Let's first look at how composable and hybrid are similar. Like mixin, composability allows us to extract reactive data, methods, and computational properties and easily reuse them across multiple components.

Composition vs Mixin

If the recyclable composition and mixer have the same purpose, why introduce the recyclable composition when we already have a mixer? 2 reasons:

  1. They can better solve the same problem
  2. They can solve more problems

Clarity of data / method sources

Mixins = data source ambiguity

The use of mixins will eventually mask the source of reactivity data and methods, especially when multiple mixins are used for a single component or one mixin has been registered globally.

//MyComponent.vue
import ProductMixin from './ProductMixin'
import BrandMixin from './BrandMixin'
import UserMixin from './UserMixin'
export default{
    mixins:[ProductMixin, BrandMixin, UserMixin],
    created(){
        // Where in the world did name come from? 
        // Let me look through each of the registered mixins to find out
        // Oh man, it's not in any of them...
        // It must be from a globally registered mixin
        console.log(this.site)

        // Oh, I got lucky here turns out the first mixin I inspected has the name
        console.log(this.name)
    }
}

Composables = transparent source of data and functions

However, using reusable compositions, we can know exactly where our reusable data and functions come from. That's because we have to import composable items and explicitly use deconstruction to get our data and functions.

//MyComponent.vue
import useProduct from './useProduct'
import useBrand from './useBrand'
import useUser from './useUser'
export default{
    setup(){
        const { name } = useProduct()

        return { name }
  }
    created(){
        // Where in the world did name come from? 
        // ah, it's not in setup anywhere... this doesn't exist and is an error
        console.log(this.site)

        // Oh, nice I can see directly in setup this came from useProduct
        console.log(this.name)
    }
}

name conflict

Mixins = risk of naming conflicts

Using the same mixin example above, what happens if two mixins actually define a name data attribute? The result will be that the data from the last mixin will win and the data in any other mixin will be lost.

//MyComponent.vue

import ProductMixin from './ProductMixin' // name = AirMax
import BrandMixin from './BrandMixin' // name = Nike
import UserMixin from './UserMixin' // name = John Doe
export default{
    mixins:[ProductMixin, BrandMixin, UserMixin],
    created(){  
        // Actually I'm not so lucky,
        // yeah I found the name in ProductMixin
        // but turns out UserMixin had a name too
        console.log(this.name) // John Doe
    }
}

Composables = risk of no naming conflicts

However, this is not the case for the composable composition. Composables can expose data or functions with the same name, but the consumer component can rename these variables at will.

//MyComponent.vue
import useProduct from './useProduct' // name = AirMax
import useBrand from './useBrand' // name = Nike
import useUser from './useUser' // name = John Doe
export default{
    setup(){
        const { name: productName } = useProduct()
        const { name: brandName } = useBrand()
        const { name: userName } = useUser()

        return { productName, brandName, userName }
  }
    created(){
        // Yay! Nothing is lost and I can get the name of each of the things
        // together in my component but when writing the composables
        // I don't have to think at all about what variable names might collide
        // with names in other composables
        console.log(this.productName)
        console.log(this.brandName)
        console.log(this.userName)
    }
}

Response data from the variation module of the component

Usually, we want reusable modules (mixin or composable) to directly change the value of some reactive data without giving this ability to consumer components.

Mixins = unable to protect their reactivity data

Take the example of RequestMixin.

// RequestMixin.js
 export default {
  data(){
        return {
            loading: false,
            payload: null
        }
  },
    methods:{
        async makeRequest(url){
            this.loading = true
            const res = await fetch(url)
            this.payload = await res.json()
            this.loading = false
        }
    }
}

In this case, we may not want the consumer component to change the loadingor value payload at will. However, it is impossible to use mixin. Mixin has no mechanism to protect data.

Composables = you can protect your reactivity data

Now compare it to the same logic written as composable.

// useRequest.js
import { readonly, ref } from "vue";
export default () => {
    // data
  const loading = ref(false);
  const payload = ref(null);

    // methods
  const makeRequest = async (url) => {
    loading.value = true;
    const res = await fetch(url);
    payload.value = await res.json();
  };

    // exposed
  return {
    payload: readonly(payload), //notice the readonly here
    loading: readonly(loading), // and here
    makeRequest
  };
};

In this composable, we can change the values of loads and payloads at will, but once we expose them to any consumer component, we make them read-only. How sweet!

Composable global state

The last ability that can be combined is something mixin doesn't have. It's very cool. Maybe one of my favorite parts is that it's really simple. All data defined with mixins will always be reset for each new component instance that uses it.

//CounterMixins.js
export default{
    data(){
        return { count: 0 }
    },
    methods:{
        increment(){
            this.count ++
        }
    }
}

For the above mixin, the count of each component will always start from 0, and increasing the count in a component using mixin will not increase the count in another component using mixin.

We can use composable to achieve the same function.

//useCounter.js
import {ref, readonly} from 'vue'
export default () => {
  const count = ref(0)
    const increment = ()=> count.value++

    return {
        count: readonly(count), 
        increment
    }
}

Many times, this is expected behavior. Sometimes, however, we want reactive data to be synchronized across all components and more like a global state defined in something like Vuex.

How do we use composable to do this? Through a simple line feed.

//useCounter.js
import {ref, readonly} from 'vue'
const count = ref(0)
export default () => {
    const increment = ()=> count.value++

    return {
        count: readonly(count), 
        increment
    }
}

Can you see the difference? All we have done is move the definition of to the count export function. Now every time increment is called, no matter which component it is called from, it references the same count variable, because count is defined outside the scope of the exported function.

There are many problems that can be solved. For example, you can have a useAuthUser composable or useCart composable. Basically, you can use this technique for any data that should be global throughout the application.

conclusion

In short, composable intent is usually the same as mixin's: Reusable logic can be extracted from components to achieve reusability. However, in practice, mixin will eventually fail to meet the requirements, but the composition can do this work well.

Keywords: Vue

Added by Jason Batten on Mon, 29 Nov 2021 22:18:50 +0200