- Notifications
You must be signed in to change notification settings - Fork433
Description
Summary
- Composition functions are available in class property initializers by wrapping
setup
helper.- Class property initializers are handled in
setup
function under the hood.
- Class property initializers are handled in
- Only
$props
(and its derived prop values),$attrs
,$slots
and$emit
are available onthis
in class property initializers.
Example:
<template> <div>Count: {{ counter.count }}</div> <button@click="counter.increment()">+</button></template><script lang="ts">import {ref,reactive,onMounted }from'vue'import {Vue,setup }from'vue-class-component'function useCounter () {const count=ref(0)function increment () {count.value++ }onMounted(()=> {console.log('onMounted') })return {count,increment }}exportdefaultclassCounterextendsVue { counter=setup(()=>useCounter())}</script>
Details
Prior to v7, class component constructor is initialized indata
hook to collect class properties. In v8, it will be initialized insetup
hook so that the users can use composition function in class property initializer.
The above class component definition is as same as following canonical component definition.
functionuseCounter(){constcount=ref(0)functionincrement(){count.value++}onMounted(()=>{console.log('onMounted')})return{ count, increment}}exportdefault{setup(){return{counter:useCounter()}}}
setup
helper
Wrapping a composition function withsetup
helper is needed because we need to delay the invocation of the composition function. Let's see the following example:
functionusePost(postId){constpost=ref(null)watch(postId,asyncid=>{post.value=awaitfetch('/posts/'+id)},{immediate:true})return{ post}}classAppextendsVue{postId='1'// DO NOT do thispost=usePost(toRef(this,'postId'))}
In the above example,this.postId
will be referred bywatch
helper to track reactive dependencies immediately but it is not reactive value at that moment. Then the watch callback won't be called whenpostId
is changed.
setup
helper will delay the invocation untilthis.postId
become a proxy property to the actual reactive value.
setup
unwrapping
As same assetup
in the Vue core library,setup
in Vue Class Component unwrapsref
values. The unwrapping happens shallowly:
// The returned value is:// {// count: { value: 0 },// nested: {// anotherCount: { value: 1 }// }// }functionuseCount(){constcount=ref(0)constanotherCount=ref(1)return{ count,nested:{ anotherCount}}}classCounterextendsVue{// counter will be:// {// count: 0, <-- unwrapped// nested: {// anotherCount: { value: 1 }// }// }// The shallow ref (count) is unwrapped while the nested one (anotherCount) retainscounter=setup(()=>useCount())}
In addition, if you return a single ref insetup
helper, the ref will also be unwrapped:
// The returned value is: { value: 42 }functionuseAnswer(){constanswer=ref(42)returnanswer}classAnswerextendsVue{// answer will be just 42 which is unwrappedanswer=setup(()=>useAnswer())}
Available built in properties onthis
Since the class constructor is used insetup
hook, only following properties are available onthis
.
$props
- All props are proxied on
this
as well. (e.g.this.$props.foo
->this.foo
)
- All props are proxied on
$emit
$attrs
$slots
Example using$props
and$emit
in a composition function.
functionuseCounter(props,emit){functionincrement(){emit('input',props.count+1)}return{ increment}}exportdefaultclassAppextendsVue{counter=setup(()=>{returnuseCounter(this.$props,this.$emit)})}
Alternative Approach
Another possible approach is using super class and mixins.
import{ref}from'vue'import{setup}from'vue-class-component'constSuper=setup((props,ctx)=>{constcount=ref(0)functionincrement(){count.value++}return{ count, increment}})exportdefaultclassAppextendsSuper{}
Pros
- Can define properties directly on
this
.
Cons
Need duplicated props type definition.
// Props type definition for setupinterfaceProps{foo:string}constSuper=setup((props:Props)=>{/* ... */})exportdefaultclassAppextendsSetup{// Another definition for foo prop to use it in the class @Propfoo!:string}