Migration to Vue 3, Part 3. – Components

functional attribute on single-file component <template> is removed.

Vue 2

<!-- template -->

<template functional>
    <component
        :is="`h${props.level}`"
        v-bind="attrs"
        v-on="listeners"
    />
</template>

// script

export default {
    props: ['level']
}

Vue 3

<!-- template -->

<template>
    <component
        :is="`h${props.level}`"
        v-bind="$attrs"
    />
</template>

// script

export default {
    props: ['level']
}

functional option in components created by functions is removed.

Vue 2

export default {
    functional: true,
    props: ['level'],
    render(h, { props, data, children }) {
        return h(`h${props.level}`, data, children)
    }
}

Vue 3

import { h } from 'vue'

const DynamicHeading = (props, context) => {
    return h(`h${props.level}`, context.attrs, context.slots)
}

DynamicHeading.props = ['level']

export default DynamicHeading

New defunctional option in components created by functions is removed.

  • component option renamed to loader,
  • loader function does not inherently receive resolve and reject arguments and must return a Promise.

Vue 2

const asyncModal = () => import('./Modal.vue')

// or

const asyncModal = {
    component: () => import('./Modal.vue'),
    delay: 200,
    timeout: 3000,
    error: ErrorComponent,
    loading: LoadingComponent
}

Vue 3

const asyncModal = defineAsyncComponent(() => import('./Modal.vue'))

// or

const asyncModalWithOptions = defineAsyncComponent({
    loader: () => import('./Modal.vue'),
    delay: 200,
    timeout: 3000,
    errorComponent: ErrorComponent,
    loadingComponent: LoadingComponent
})

Vue 2

const oldAsyncComponent = (resolve, reject) => {
    /* ... */
}

Vue 3

const asyncComponent = defineAsyncComponent(
    () =>
        new Promise((resolve, reject) => {
            /* ... */
        })
)

New emits option, similar to the existing props option, that can be used to define the events that a component can emit to its parent.

Vue 2

<!-- template -->

<div>
    <p>{{ text }}</p>
    <button v-on:click="$emit('accepted')">OK</button>
</div>

// script

export default {
    props: ['text']
}

Vue 3

<!-- template -->

<div>
    <p>{{ text }}</p>
    <button v-on:click="$emit('accepted')">OK</button>
</div>

// script

export default {
    props: ['text']
    emits: ['accepted']
}