What is a composite API?

The composite API, the setup component option new to Vue3.0, executes before creating the component, once the props are resolved, and acts as an entry point to the composite API. Because setup runs around beforeCreate and Created lifecycle hooks, there is no need to explicitly define them. In other words, any code written in these hooks should be written directly in the Setup function.

// Previous version
export default {
    beforeCreate () {
        console.log('this is beforeCreate')
    },
    created () {
        console.log('this is created')}}/ / 3.0 version
export default {
    setup() {
        console.log('write beforeCreate and created code')}}Copy the code

Because the component instance has not been created when setup is executed, there is no this in the Setup option. Therefore, you can only access the following property:

  • props
  • attrs
  • slots
  • emit

In other words, you will not have access to the following component options:

  • data
  • computed
  • methods

Simple usage

The setup option should be a function that accepts props and context.

export default {
    props: {
        title: String
    },
    setup(props) {
        console.log(props.title)
    }
}
Copy the code

Everything we return from Setup is exposed to the rest of the component (computed properties, methods, lifecycle hooks, and so on) as well as to the component’s template.

<template> 
    <h1>This is {{title}}</h1>
</template>

<script>
import { ref } from 'vue'

export default {
    setup() {
        const title = ref('Vue3.0');
        return {
            title
        }
    }
}
</script>
Copy the code

In the code block above we create a response variable using ref. In Vue 3.0, we can make any response variable work anywhere through a new ref function that takes a parameter and returns it wrapped in an object with a value property. This property can then be used to access or change the value of a reactive variable. See the Responsiveness Basics API for more information about reactive variables

import { ref } from 'vue'

const counter = ref(0)

console.log(counter) // { value: 0 }
console.log(counter.value) / / 0

counter.value++
console.log(counter.value) / / 1
Copy the code

The access component cycle hook in Setup

In Setup, you can access a component’s lifecycle hook by prefixing it with “on”.

export default {
    setup() {
        // mounted
        onMounted(() = > {
             console.log('Component is mounted! ')}}}Copy the code

The following table contains how to invoke lifecycle hooks within setup () :

Option type API Hook inside setup
beforeCreate Not needed*
created Not needed*
beforeMount onBeforeMount
mounted onMounted
beforeUpdate onBeforeUpdate
updated onUpdated
beforeUnmount onBeforeUnmount
unmounted onUnmounted
errorCaptured onErrorCaptured
renderTracked onRenderTracked
renderTriggered onRenderTriggered

Above we have shifted the result, beforeCreate, and created lifecycle methods to the setup() function.

Use watch in setup

We can import the watch function from Vue, which takes three arguments:

  • A reactive reference or getter function that we want to listen for
  • A callback
  • Optional configuration options
import { ref, watch } from 'vue'
export default {
    setup() {
        const counter = ref(0)
        watch(counter, (newValue, oldValue) = > {
            console.log('The new counter value is: ' + counter.value)
        })
    }
}
Copy the code

Use computed in setup

import { ref, computed } from 'vue'
export default {
    setup() {
        const counter = ref(0)
        const twiceTheCounter = computed(() = > counter.value * 2)

        counter.value++
        console.log(counter.value) / / 1
        console.log(twiceTheCounter.value) / / 2
        
        twiceTheCounter.value++ // error}}Copy the code

In the code block above we use the getter function and return an immutable reactive ref object for the value returned from the getter, so twicethecounter.value ++ generates an error. We can also use objects with get and set functions to create writable ref objects.

const count = ref(1)
const plusOne = computed({
  get: () = > count.value + 1.set: val= > {
    count.value = val - 1
  }
})

plusOne.value = 1
console.log(count.value) / / 0
Copy the code

Provide/inject is used in setup

When using provide in setup(), we first import the provide method explicitly from vue. This allows us to define each property by calling the provide time.

The provide function allows you to define a property with two arguments:

  1. Property name (type)
  2. The value of the property
<! -- src/components/MyMap.vue --><template>
  <MyMarker />
</template>

<script>
import { provide } from 'vue'
import MyMarker from './MyMarker.vue

export default {
    components: {
        MyMarker
    },
    setup() {
        provide('location', 'North Pole')
        provide('geolocation', {
            longitude: 90,
            latitude: 135
        })
    }
}
</script>
Copy the code

When you use Inject in setup(), you also need to import it explicitly from vue. Once we do that, we can call it to define how to expose it to our component. The Inject function takes two parameters:

  1. The name of the property to inject
  2. A default value (optional)
<! -- src/components/MyMarker.vue --><script>
import { inject } from 'vue'

export default {
  setup() {
    const userLocation = inject('location'.'The Universe')
    const userGeolocation = inject('geolocation')

    return {
      userLocation,
      userGeolocation
    }
  }
}
</script>

Copy the code

Why do YOU need a composite API?

Prior to vue3.0, we were able to extract the repeatable parts of the interface and its functionality into reusable pieces of code by creating Vue components, which took our applications further in terms of maintainability and flexibility. But that alone may not be enough, especially if your application becomes very large — think hundreds of components. Sharing and reusing code becomes especially important when dealing with such large applications.

Suppose that in our application, we have a view that displays a list of warehouses for a user. In addition, we want to apply search and filtering capabilities. This component contains the following functions:

  1. Get the repository for the username from the assumed external API and refresh it when the user changes
  2. usesearchQueryString search repository
  3. usefiltersObject filter repository

Prior to VUe3.0, our component code would look like this:

// src/components/UserRepositories.vue

export default {
  components: { RepositoriesFilters, RepositoriesSortBy, RepositoriesList },
  props: {
    user: { type: String }
  },
  data () {
    return {
      repositories: []./ / 1
      filters: {... },/ / 3
      searchQuery: ' ' / / 2}},computed: {
    filteredRepositories () { ... }, / / 3
    repositoriesMatchingSearchQuery () { ... }, / / 2
  },
  watch: {
    user: 'getUserRepositories' / / 1
  },
  methods: {
    getUserRepositories () {
      // Use 'this.user' to get the user repository
    }, / / 1
    updateFilters () { ... }, / / 3
  },
  mounted () {
    this.getUserRepositories() / / 1}}Copy the code

If we use the Vue3.0 composite API, our code can be refactored into the following code:

The external API gets the repository of the user name and refreshes it when the user changes

// src/composables/useUserRepositories.js

import { fetchUserRepositories } from '@/api/repositories'
import { ref, onMounted, watch } from 'vue'

export default function useUserRepositories(user) {
  const repositories = ref([])
  const getUserRepositories = async () => {
    repositories.value = await fetchUserRepositories(user.value)
  }

  onMounted(getUserRepositories)
  watch(user, getUserRepositories)

  return {
    repositories,
    getUserRepositories
  }
}
Copy the code

Then there is the search function:

// src/composables/useRepositoryNameSearch.js

import { ref, computed } from 'vue'

export default function useRepositoryNameSearch(repositories) {
  const searchQuery = ref(' ')
  const repositoriesMatchingSearchQuery = computed(() = > {
    return repositories.value.filter(repository= > {
      return repository.name.includes(searchQuery.value)
    })
  })

  return {
    searchQuery,
    repositoriesMatchingSearchQuery
  }
}
Copy the code

Use them in components:

// src/components/UserRepositories.vue
import useUserRepositories from '@/composables/useUserRepositories'
import useRepositoryNameSearch from '@/composables/useRepositoryNameSearch'
import { toRefs } from 'vue'

export default {
  components: { RepositoriesFilters, RepositoriesSortBy, RepositoriesList },
  props: {
    user: { type: String }
  },
  setup (props) {
    const { user } = toRefs(props)

    const { repositories, getUserRepositories } = useUserRepositories(user)

    const {
      searchQuery,
      repositoriesMatchingSearchQuery
    } = useRepositoryNameSearch(repositories)

    return {
      // Because we don't care about unfiltered warehouses
      // We can expose the filtered results under the name 'Repositories'
      repositories: repositoriesMatchingSearchQuery,
      getUserRepositories,
      searchQuery,
    }
  },
  data () {
    return {
      filters: {... },/ / 3}},computed: {
    filteredRepositories () { ... }, / / 3
  },
  methods: {
    updateFilters () { ... }, / / 3}}Copy the code

Final results:

// src/components/UserRepositories.vue
import { toRefs } from 'vue'
import useUserRepositories from '@/composables/useUserRepositories'
import useRepositoryNameSearch from '@/composables/useRepositoryNameSearch'
import useRepositoryFilters from '@/composables/useRepositoryFilters'

export default {
  components: { RepositoriesFilters, RepositoriesSortBy, RepositoriesList },
  props: {
    user: { type: String}},setup(props) {
    const { user } = toRefs(props)

    const { repositories, getUserRepositories } = useUserRepositories(user)

    const {
      searchQuery,
      repositoriesMatchingSearchQuery
    } = useRepositoryNameSearch(repositories)

    const {
      filters,
      updateFilters,
      filteredRepositories
    } = useRepositoryFilters(repositoriesMatchingSearchQuery)

    return {
      // Because we don't care about unfiltered warehouses
      // We can expose the filtered results under the name 'Repositories'
      repositories: filteredRepositories,
      getUserRepositories,
      searchQuery,
      filters,
      updateFilters
    }
  }
}
Copy the code