Functional Components breaking
Overview
In terms of what has changed, at a high level:
- Performance gains from 2.x for functional components are now negligible in 3.x, so we recommend just using stateful components
- Functional components can only be created using a plain function that receives
props
andcontext
(i.e.,slots
,attrs
,emit
) - BREAKING:
functional
attribute on single-file component (SFC)<template>
is removed - BREAKING:
{ functional: true }
option in components created by functions is removed
For more information, read on!
Introduction
In Vue 2, functional components had two primary use cases:
- as a performance optimization, because they initialized much faster than stateful components
- to return multiple root nodes
However, in Vue 3, the performance of stateful components has improved to the point that the difference is negligible. In addition, stateful components now also include the ability to return multiple root nodes.
As a result, the only remaining use case for functional components is simple components, such as a component to create a dynamic heading. Otherwise, it is recommended to use stateful components as you normally would.
2.x Syntax
Using the <dynamic-heading>
component, which is responsible for rendering out the appropriate heading (i.e., h1
, h2
, h3
, etc.), this could have been written as a single-file component in 2.x as:
// Vue 2 Functional Component Example
export default {
functional: true,
props: ['level'],
render(h, { props, data, children }) {
return h(`h${props.level}`, data, children)
}
}
Or, for those who preferred the <template>
in a single-file component:
<!-- Vue 2 Functional Component Example with <template> -->
<template functional>
<component
:is="`h${props.level}`"
v-bind="attrs"
v-on="listeners"
/>
</template>
<script>
export default {
props: ['level']
}
</script>
3.x Syntax
Components Created by Functions
Now in Vue 3, all functional components are created with a plain function. In other words, there is no need to define the { functional: true }
component option.
They will receive two arguments: props
and context
. The context
argument is an object that contains a component's attrs
, slots
, and emit
properties.
In addition, rather than implicitly provide h
in a render
function, h
is now imported globally.
Using the previously mentioned example of a <dynamic-heading>
component, here is how it looks now.
import { h } from 'vue'
const DynamicHeading = (props, context) => {
return h(`h${props.level}`, context.attrs, context.slots)
}
DynamicHeading.props = ['level']
export default DynamicHeading
Single File Components (SFCs)
In 3.x, the performance difference between stateful and functional components has been drastically reduced and will be insignificant in most use cases. As a result, the migration path for developers using functional
on SFCs is to remove the attribute and rename all references of props
to $props
and attrs
to $attrs
.
Using our <dynamic-heading>
example from before, here is how it would look now.
<template>
<component
v-bind:is="`h${$props.level}`"
v-bind="$attrs"
/>
</template>
<script>
export default {
props: ['level']
}
</script>
The main differences are that:
functional
attribute removed on<template>
listeners
are now passed as part of$attrs
and can be removed
Next Steps
For more information on the usage of the new functional components and the changes to render functions in general, see: