# Composition API

This section uses single-file component syntax for code examples

# setup

A component option that is executed before the component is created, once the props are resolved, and serves as the entry point for composition API's

  • Arguments:

    • {Data} props
    • {SetupContext} context
  • Typing:

interface Data {
  [key: string]: unknown
}

interface SetupContext {
  attrs: Data
  slots: Slots
  emit: (event: string, ...args: unknown[]) => void
}

function setup(props: Data, context: SetupContext): Data
1
2
3
4
5
6
7
8
9
10
11

TIP

To get type inference for the arguments passed to setup(), the use of defineComponent is needed.

  • Example

    With the template:

    <!-- MyBook.vue -->
    <template>
      <div>{{ readersNumber }} {{ book.title }}</div>
    </template>
    
    <script>
      import { ref, reactive } from 'vue'
    
      export default {
        setup() {
          const readersNumber = ref(0)
          const book = reactive({ title: 'Vue 3 Guide' })
    
          // expose to template
          return {
            readersNumber,
            book
          }
        }
      }
    </script>
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21

    With render function:

    // MyBook.vue
    
    import { h, ref, reactive } from 'vue'
    
    export default {
      setup() {
        const readersNumber = ref(0)
        const book = reactive({ title: 'Vue 3 Guide' })
        // Please note that we need to explicitly expose ref value here
        return () => h('div', [readersNumber.value, book.title])
      }
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
  • See also: Composition API setup

# Lifecycle Hooks

Lifecycle hooks can be registered with directly-imported onX functions:

import { onMounted, onUpdated, onUnmounted } from 'vue'

const MyComponent = {
  setup() {
    onMounted(() => {
      console.log('mounted!')
    })
    onUpdated(() => {
      console.log('updated!')
    })
    onUnmounted(() => {
      console.log('unmounted!')
    })
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

These lifecycle hook registration functions can only be used synchronously during setup(), since they rely on internal global state to locate the current active instance (the component instance whose setup() is being called right now). Calling them without a current active instance will result in an error.

The component instance context is also set during the synchronous execution of lifecycle hooks. As a result, watchers and computed properties created synchronously inside of lifecycle hooks are also automatically tore down when the component unmounts.

  • Mapping between Options API Lifecycle Options and Composition API

    • beforeCreate -> use setup()
    • created -> use setup()
    • beforeMount -> onBeforeMount
    • mounted -> onMounted
    • beforeUpdate -> onBeforeUpdate
    • updated -> onUpdated
    • beforeUnmount -> onBeforeUnmount
    • unmounted -> onUnmounted
    • errorCaptured -> onErrorCaptured
    • renderTracked -> onRenderTracked
    • renderTriggered -> onRenderTriggered
  • See also: Composition API lifecycle hooks

# Provide / Inject

provide and inject enables dependency injection. Both can only be called during setup() with a current active instance.

  • Typing:
interface InjectionKey<T> extends Symbol {}

function provide<T>(key: InjectionKey<T> | string, value: T): void

// without default value
function inject<T>(key: InjectionKey<T> | string): T | undefined
// with default value
function inject<T>(key: InjectionKey<T> | string, defaultValue: T): T
// with factory
function inject<T>(
  key: InjectionKey<T> | string,
  defaultValue: () => T,
  treatDefaultAsFactory: true
): T
1
2
3
4
5
6
7
8
9
10
11
12
13
14

Vue provides an InjectionKey interface which is a generic type that extends Symbol. It can be used to sync the type of the injected value between the provider and the consumer:

import { InjectionKey, provide, inject } from 'vue'

const key: InjectionKey<string> = Symbol()

provide(key, 'foo') // providing non-string value will result in error

const foo = inject(key) // type of foo: string | undefined
1
2
3
4
5
6
7

If using string keys or non-typed symbols, the type of the injected value will need to be explicitly declared:

const foo = inject<string>('foo') // string | undefined
1

# getCurrentInstance

getCurrentInstance enables access to an internal component instance useful for advanced usages or for library creators.

import { getCurrentInstance } from 'vue'

const MyComponent = {
  setup() {
    const internalInstance = getCurrentInstance()

    internalInstance.appContext.config.globalProperties // access to globalProperties
  }
}
1
2
3
4
5
6
7
8
9

getCurrentInstance only works during setup or Lifecycle Hooks

When using outside of setup or Lifecycle Hooks, please call getCurrentInstance() on setup and use the instance instead.

const MyComponent = {
  setup() {
    const internalInstance = getCurrentInstance() // works

    const id = useComponentId() // works

    const handleClick = () => {
      getCurrentInstance() // doesn't work
      useComponentId() // doesn't work

      internalInstance // works
    }

    onMounted(() => {
      getCurrentInstance() // works
    })

    return () =>
      h(
        'button',
        {
          onClick: handleClick
        },
        `uid: ${id}`
      )
  }
}

// also works if called on a composable
function useComponentId() {
  return getCurrentInstance().uid
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32

Deployed on Netlify.
Last updated: 2020-10-31, 11:56:33 UTC