I just shared an article about the Vue3 composite API, but I don’t think it’s comprehensive and specific enough to cover how compute, Watch, methods, and prop can be migrated from option-API to the composite API. So let’s talk again today and use code to help you understand composite apis and how to migrate.

Let’s take a look at the limitations of Vue2, and then Vue3 addresses this problem. The limitations are as follows

  • As your component expands, its readability becomes hard, which means difficult to maintain
  • The pattern of sharing code between components is not so elegant
  • Vue2 has a type system

readability

Here, take the search page, and implement the search function first, which is distributed in Data and Methods, and maybe add the search filter and paging function later. Logically focused features are organized by component options. That is, for functionality they have logic scattered around data, computed, props, methods, and lifecycle. We want to keep the functional logic all in one place.

The composition Api allows you to compose these functions in setup. Setup does not allow you to split functions into options.

Explain how to replace the option-API with the Composition API in VUe2 with an example.

  • Composite apis replace data(primitive and compound types)
  • Combinatorial API replacement methods
  • The composite API replaces compute
  • The combined API replaces Watcher
  • How does a composite API scope prop properties

Responsive data for composite apis

In Vue2 we can define a basic type of data or a composite type of data, such as an object or an array, and then bind the data to the interface. The data defined in the data can be accessed by other properties, computed or methods, and the data is responsive.

data(){
    return{
        title:"machine leanring"
    }
}
Copy the code

The following is the composition API implementation. After a variable is defined by Setup, it is returned as an object property, which can be displayed on the interface via {{title}}

setup(){
  const title = "machine learning"
  return {
  	title
  }
}
Copy the code
<h1 class="title">{{title}}</h1>
Copy the code

Reactive base type data

Defining data this way is not reactive data, but Vue3 provides two ways to define reactive data in Setup: REF and Reactive for primitive and composite data, respectively. Let’s look at the base data type first. The reactive method is ref(base data), such as String, Number, or Boolean

        setup(){
            const title = ref("machine learning")
            return {
                title
            }
        }
Copy the code

Reactive composite type data

Reactive objects can be defined for objects, and then setup returns them as a property of the returned object for invocation.

setup(){
  const title = ref("machine learning");
	let count = ref(0)
  const tut = reactive({
		title:'machine learning',
		lesson:12,
		chapters:[
          {id:0,title:"introducion"},
          {id:2,title:"regression"},
          {id:3,title:"classification"},
				]
      })
		return {
      title,
      count,
      tut
		}
}
Copy the code

Vue2 implements reactive data by providing getters and setters to listen for data through the defineProperty API, but there are some problems with this implementation if there are a large number of attribute values

compute

Take a look at compute taking option-API from VUE to a computed function that takes a function that takes some combination of base values (operations).

            const refTitlContent = computed(function(){
                return `${refTitle.value} ${refContent.value}`
            })
Copy the code
The complete code
<template>
    <h1 class="title">{{refTitlContent}}</h1>
    <div class="card">
        <div class="card-header">
            <div class="card-header-title">{{title}}</div>
        </div>
        <div class="card-content">
            <div class="card-content-body">{{content}}</div>
        </div>
    </div>
    <div class="section">
        <div class="control">
            <input class="input" type="text" v-model="title"/>
        </div>
        <div class="control">
            <input class="input" type="text" v-model="content"/>
        </div>
    </div>
    <div class="section">
        <div class="control">
            <input class="input" type="text" v-model="refTitle"/>
        </div>
        <div class="control">
            <input class="input" type="text" v-model="refContent"/>
        </div>
    </div>
</template>

<script>
import { ref,computed } from 'vue'
    export default {
        name:"ReplaceComputed",
        setup(){
            const refTitle = ref('')
            const refContent = ref('')

            const refTitlContent = computed(function(){
                return `${refTitle.value} ${refContent.value}`
            })

            return {
                refTitle,
                refContent,
                refTitlContent
            }
        },
        data(){
            return {
                title:"",
                content:""
            }
        }
    }
</script>
Copy the code

watcher

Watch takes an observation. The first argument can be a variable to observe or an array of observed variables. The second handler callback takes a function of newVal and oldVal.

        setup(){
            const refTitle = ref('');
            const refContent = ref('content');

            watch([refTitle,refContent],(newVals,oldVals)=>{
                console.log('Old refValue',oldVals[0]);
                console.log('New refValue',newVals[0]);
                console.log('New refValue',newVals[1]);
                console.log('New refValue',newVals[1]);
            })
            return {
                refTitle,
                refContent
            }
        },
Copy the code

It is also possible to detect a response value, where the first argument is to accept a function that returns the value of the object to be monitored.

const tut = reactive({ tutTitle:'', tutContent:'' }); watch(()=>{return {... tut}}, function(newVal,oldVal){ console.log('tut title',oldVal.tutTitle) console.log('tut title',newVal.tutTitle) // console.log('new tut title',newVal.tutContent) // console.log('new tut Content',newVal.tutContent) })Copy the code
            watch(()=>tut.tutTitle,function(newVal,oldVal){
                console.log('old tut title',newVal);
                console.log('new tut title',oldVal);
            })
Copy the code

You can specify deep:true in the second argument if you want to deeply detect objects.

            const tut = reactive({
                tutTitle:'',
                tutContent:'',
                category:{
                    name:''
                }
            });
Copy the code
            const tut = reactive({
                tutTitle:'',
                tutContent:'',
                category:{
                    name:''
                }
            });

            watch(()=>tut.category,function(newVal,oldVal){
                console.log('new tut category',newVal)
                console.log('old tut category',oldVal)
            },{
                deep:true
            })
Copy the code

Dojo.provide and inject

Create three components: ChildA, ChildB, and ChildC

In the app.vue file

  • Introduce the ChildA component
  • And then we import it from VUEprovideThe function takes two arguments, the first providing the name of the variable and the second the value of the variable
<template>
<div class="container">
  <ChildA/>
</div>

</template>

<script>
import ChildA from './components/Option/ChildA.vue'
import {provide} from 'vue';
export default {
  
  name: 'App',
  
  setup(){
    provide('refProvideTitle','machine learning 2');
  },
  
  data(){
    return {
      title:"machine learning",
    }
  },
  components:{
    ChildA
  },
  provide(){
    return{
      title:this.title
    }
  }
}
</script>
Copy the code

In the ChildC file

  • Imported from VUEinject
  • The function theninjectThe first parameter accepts a variable namerefProvideTitleThe second parameter is the default value for this parameter
  • injectReturn receive toinjectedTitleA responsive data, and then thesetupReturns for use by templates
<template>
    <div>
        <h1 class="title">Child C</h1>
        <h1 class="title">{{title}}</h1>
        <h1 class="title"> injected Title:{{injectedTitle}}</h1>
    </div>
</template>

<script>
    import {inject} from 'vue';

    export default {

        name:"ChildC",
        setup(){
            const injectedTitle = inject('refProvideTitle','default machine leaerning2');
            return {
                injectedTitle
            }
        },
        inject:['title']
    }
</script>
Copy the code

Above we completed how to transfer values between different components using the composite API,

    const count = ref('0');
    const refTut = reactive({
      title:"machine learning title",
      content:"machine learning content"
    });

    provide('count',count);
    provide('tut',refTut);
Copy the code
<template>
<div class="container">
  <div class="control">
    <button class="button" @click="incrementCount" > increment </button>
  </div>
  <ChildA/>
</div>

</template>

<script>
import ChildA from './components/Option/ChildA.vue'
import {provide, ref, reactive} from 'vue';
export default {
  
  name: 'App',
  
  setup(){
    provide('refProvideTitle','machine learning 2');
    const count = ref('10');
    const refTut = reactive({
      refTitle: "machine learning title",
      refContent:"machine learning content"
    });

    function incrementCount(){
      count.value ++;
    }

    provide('count',count);
    provide('refTut',refTut);

    return {
      count,
      incrementCount,
    }
  },
  
  data(){
    return {
      title:"machine learning",
    }
  },
  components:{
    ChildA
  },
  provide(){
    return{
      title:this.title
    }
  }
}
</script>
Copy the code
The complete code
<template>
<div class="container">
  <div class="control">
    <button class="button" @click="incrementCount" > increment </button>
  </div>
  <ChildA/>
</div>

</template>

<script>
import ChildA from './components/Option/ChildA.vue'
import {provide, ref, reactive} from 'vue';
export default {
  
  name: 'App',
  
  setup(){
    provide('refProvideTitle','machine learning 2');
    const count = ref('10');
    const refTut = reactive({
      refTitle: "machine learning title",
      refContent:"machine learning content"
    });

    function incrementCount(){
      count.value ++;
    }

    provide('count',count);
    provide('refTut',refTut);
    provide('incrementCount',incrementCount)

    return {
      count,
      incrementCount,
    }
  },
  
  data(){
    return {
      title:"machine learning",
    }
  },
  components:{
    ChildA
  },
  provide(){
    return{
      title:this.title
    }
  }
}
</script>


Copy the code

Not only can data be passed across component levels via Provide and Injection, but methods can also be passed, although this is still passed from top to bottom.

<template>
    <div>
        <h1 class="title">Child C</h1>
        <h1 class="title">{{title}}</h1>
        <h1 class="title"> injected Title:{{injectedTitle}}</h1>
        <h1 class="title"> injected count:{{childCount}}</h1>
        <h1 class="title"> injected refTitle:{{refTitle}}</h1>
        <h1 class="title"> injected refContent:{{refContent}}</h1>
        <div class="control">
            <button class="button" @click="incrementCount" > increment </button>
        </div>
    </div>
</template>

<script>
    import {inject, toRefs} from 'vue';

    export default {

        name:"ChildC",
        setup(){
            const injectedTitle = inject('refProvideTitle','default machine leaerning2');
            const childCount = inject('count',0);
            const childTut = inject('refTut',{});
            const incrementCount = inject('incrementCount')
            console.log(childTut)
            return {
                injectedTitle,
                childCount,
                ... toRefs(childTut),
                incrementCount
            }
        },
        inject:['title']
    }
</script>
Copy the code

Life cycle comparison

Option API Hook inside(setup)
beforeCreate Don’t need to
created Don’t need to
beforeMount onBeforemount
mounted onMounted
beforeUpdate onBeforeUpdate
update onUpdated
beforeUnmount onBeforeUnmount
errorCaptured onErrorCaptured
renderTracked onRenderTracked
renderTtriggered onRenderTtriggered

The template reference

<template>
    <div class="section">
        <div class="control">
            <input type="text" class="input" ref="inputRef">
        </div>
    </div>
</template>

<script>
    export default {
        name:"ReplaceTemplateRef",
        mounted(){
            this.$refs.inputRef.focus()
        }
    }
</script>
Copy the code

This is familiar from vue2, where you can get a reference to the DOM element after it is bound to the DOM tree by specifying a ref. It’s kind of like how jQuery handles the DOM.

Now let’s implement the combinatorial API. Note that the setup function is returned by this inputRef

<script>
    import {ref, onMounted} from 'vue';

    export default {
        name:"ReplaceTemplateRef",
        setup(){
            const inputRef = ref(null);
            onMounted(()=>{
                console.log(inputRef.value)
                inputRef.value.focus();
            })
            return {
                inputRef
            }
        },
        // mounted(){
        //     this.$refs.inputRef.focus()
        // }
    }
</script>
Copy the code

prop

Now let’s talk about how to access the prop property value from the setup function. The first setup parameter is the custom property of the prop child component that can be accessed through this parameter.

<template>
<div class="container">
  <Person/>
</div>

</template>

<script>
import Person from './components/Option/Person.vue';
     
export default {
  
  name: 'App',
  components:{
      Person
  }
}
</script>


Copy the code
<template>
    <div class="container">
        <div class="control">
            <input class="input" type="text" placeholder="tut title" v-model="tutTitle">
        </div>
        <div class="control">
            <input class="input" type="text" placeholder="tut content" v-model="tutContent">
        </div>
        <PersonGreeting :title="tutTitle" :content="tutContent" />
    </div>
</template>

<script>
    import {ref} from 'vue';
    import PersonGreeting from './PersonGreeting.vue'
    export default {
        name:"Person",
        components:{
            PersonGreeting
        },
        setup(){
            const tutTitle = ref('');
            const tutContent = ref('');
            return {
                tutTitle,
                tutContent,
            }
        }

    }
</script>

Copy the code
<template>
    <div class="section">
        <h1 class="title">{{title}}</h1>
        <h1 class="subtitle">{{content}}</h1>
    </div>
</template>

<script>
    export default {
        name:'PersonGreeting',
        props:['title','content']
        
    }
</script>
Copy the code
<template>
    <div class="section">
        <h1 class="title">{{title}}</h1>
        <h1 class="subtitle">{{content}}</h1>
        <h1 class="title"> description {{tutDescription}}</h1>
    </div>
</template>

<script>
import { computed } from 'vue'
    export default {
        name:'PersonGreeting',
        setup(props){
            const tutDescription = computed(()=>{
                return `${props.title} ${props.content}`
            })
            return {
                tutDescription 
            }
        },
        props:['title','content']
        
    }
</script>

<style scoped>

</style>
Copy the code

context

Setup takes two parameters. The first parameter, described earlier, is props, from which we can get the parameters passed in from the parent component. The second parameter context is the object attrs, emit, and slots. You can use emit to pass data from a child component to a parent. The parent component Person receives the child component that emits the event sendDescription handler.

<template>
    <div class="container">
        <div class="control">
            <input class="input" type="text" placeholder="tut title" v-model="tutTitle">
        </div>
        <div class="control">
            <input class="input" type="text" placeholder="tut content" v-model="tutContent">
        </div>
        <PersonGreeting :title="tutTitle" :content="tutContent" @sendDescription="descriptionHandler" />

    </div>
</template>

<script>
    import {ref} from 'vue';
    import PersonGreeting from './PersonGreeting.vue'
    export default {
        name:"Person",
        components:{
            PersonGreeting
        },
        setup(){
            const tutTitle = ref('');
            const tutContent = ref('');
            function descriptionHandler(decription){
                alert(decription)
            }
            return {
                tutTitle,
                tutContent,
                descriptionHandler,
            }
        }

    }
</script>

Copy the code

The emit method of the context sends the event with the first argument being the event name and the second argument being the value to be passed through the event.

<template>
    <div class="section">
        <h1 class="title">{{title}}</h1>
        <h1 class="subtitle">{{content}}</h1>
        <h1 class="title"> description {{tutDescription}}</h1>
        <button @click="sendEvent">send Description</button>
    </div>
</template>

<script>
import { computed } from 'vue'
    export default {
        name:'PersonGreeting',
        setup(props,context){
            const tutDescription = computed(()=>{
                return `${props.title} ${props.content}`
            })

            function sendEvent(){
                context.emit('sendDescription',tutDescription.value)
            }
            return {
                tutDescription,
                sendEvent
            }
        },
        props:['title','content']
        
    }
</script>
Copy the code