h is now globally imported instead of passed to render function as an argument.
Vue 2
export default { render(h) { return h('div') } }
Vue 3
import { h } from 'vue' export default { render() { return h('div') } }
VNodes now have a flat props structure.
Vue 2
{ staticClass: 'button', class: { 'is-outlined': isOutlined }, staticStyle: { color: '#34495E' }, style: { backgroundColor: buttonColor }, attrs: { id: 'submit' }, domProps: { innerHTML: '' }, on: { click: submitForm }, key: 'submit-button' }
Vue 3
{ class: ['button', { 'is-outlined': isOutlined }], style: [{ color: '#34495E' }, { backgroundColor: buttonColor }] id: 'submit', innerHTML: '', onClick: submitForm, key: 'submit-button' }
String ID can no longer be used to implicitly lookup registered components.
Vue 2
Vue.component('button-counter', { data() { return { count: 0 } } template: ` <button @click="count++"> Clicker {{ count }} times. </button> ` }) export default { render(h) { return h('button-counter') } }
Vue 3
import { h, resolveComponent } from 'vue' export default { setup() { const ButtonCounter = resolveComponent('button-counter') return () => h(ButtonCounter) } }
Slots are defined as children of the current node as an object.
Vue 2
h(LayoutComponent, [ h('div', { slot: 'header' }, this.header), h('div', { slot: 'content' }, this.content), ])
Vue 3
h(LayoutComponent, {}, { header: () => h('div', this.header), content: () => h('div', this.content), })
Scoped slots are now unified into the $slots option.
Vue 2
this.$scopedSlots.header
Vue 3
$listeners object has been removed – event listeners are now part of $attrs.
Vue 2
<!-- template --> <label> <input type="text" v-bind="$attrs" v-on="$listeners" /> </label> // script export default { inheritAttrs: false }
Vue 3
<!-- template --> <label> <input type="text" v-bind="$attrs" /> </label> // script export default { inheritAttrs: false }
$attrs now contains all attributes passed to a component, including class and style.
Vue 2
<!-- ChildComponent.vue - template --> <label> <input type="text" v-bind="$attrs" /> </label> // ChildComponent.vue - script export default { inheritAttrs: false } <!-- ParentComponent.vue - template --> <ChildComponent id="my-id" class="my-class" /> <!-- output --> <label class="my-class"> <input type="text" id="my-id" /> </label>
Vue 3
<!-- ChildComponent.vue - template --> <label> <input type="text" v-bind="$attrs" /> </label> // ChildComponent.vue - script export default { inheritAttrs: false } <!-- ParentComponent.vue - template --> <ChildComponent id="my-id" class="my-class" /> <!-- output --> <label> <input type="text" id="my-id" class="my-class" /> </label>