Vue组合式API
Composition API简介
Composition API:组合式 API;一组低侵入式的、函数式的 API,使得我们能够更灵活地【组合】组件的逻辑。
这是有别于 Options API 的一种函数式 API。无需通过很多选项来完成业务逻辑,Composition API提供了一个setup函数,我们可以将data数据、计算属性、方法等等,都放在setup函数中,这样就可以对业务进行集中处理了。
1.1setup()入口
setup 函数是一个新的组件选项,它是在组件内使用 Composition API 的入口点。它会在Vue实例创建完成前被调用。所以,setup函数中没有this指针
<template>
<div></div>
</template>
<script>
export default {
setup() {
//这里书写本地状态(data数据)、计算属性或方法等
console.log('setup函数');
}
}
</script>
如果 setup 返回一个对象,则对象的属性将会被合并到组件模板的渲染上下文,我们就可以在模板里使用对应的属性和方法。所以,我们可以将本地状态(data数据)、方法、计算属性等写在 setup 函数中。
<template>
<div id="app">
{{num}}
</div>
</template>
<script>
export default{
setup(){
let num=0;
return {num};
}
}
</script>
1.2ref 响应式监听(了解,后续有更好的方案)
接下来,试着在其中加入函数
<template>
<div id="app">
{{num}}
<button @click="add">+</button>
</div>
</template>
<script>
export default{
setup(){
let num=0;
function add(){
num++;
}
return {num,add};
}
}
</script>
运行后发现数字没有变化 ,原因很简单,num只是声明的一个普通数据,不具备响应功能。
在 Vue 3.0 中,我们可以通过一个 ref 函数来实现响应式数据监听。ref 接受一个参数,并将其包裹在一个带有 value 属性的对象中返回,然后可以使用该 value 属性访问或更改响应式变量的值:
<template>
<div id="app">
{{num}}
<button @click="add">+</button>
</div>
</template>
<script>
//1、注意:要导入ref
import {ref} from 'vue';
export default{
setup(){
let num=ref(0);//2、ref
function add(){
num.value++;//3、 .value
}
return {num,add};
}
}
</script>
为什么要将值封装在一个对象中,看似没有必要,但为了保持 JavaScript 中不同数据类型的行为统一,这是必须的。因为在 JavaScript 中,Number 或 String 等基本类型是通过值传递的,而不是通过引用传递的
1.3reactive与toRefs
上面实例中,操作数据时需要使用 value 属性,比较麻烦。
可以使用 reactive 与 toRefs 解决这个问题。首先使用 reactive 创建响应式对象,封装数据。
<template>
<div id="app">
<!--data.num -->
{{data.num}}
<button @click="add">+</button>
</div>
</template>
<script>
//1、注意:要导入reactive
import {reactive} from 'vue';
export default{
setup(){
//2 reactive({})定义数据
let data=reactive({
num:0
})
function add(){
data.num++;//3、 直接访问,不需要加.value,但要通过data访问
}
return {data,add};
}
}
</script>
更复杂一点的示例
<template>
<div id="app">
<!--data.num -->
{{data.num}}<button @click="add">+</button><br>
{{data.userObj.userName}}
<ul v-for="(item,index) in data.userList" :key="item.id">
<li>{{item.userId}}-{{item.userName}}</li>
</ul>
</div>
</template>
<script>
//1、注意:要导入reactive
import {reactive} from 'vue';
export default{
setup(){
//2 reactive({})定义数据
let data=reactive({
num:0,
userObj:{
userName:'wang.qj'
},
userList:[{userId:1,userName:'张三'}]
})
function add(){
data.num++;//3、 直接访问,不需要加.value,但要通过data访问
}
return {data,add};
}
}
</script>
但是因为只有data是响应式数据,而data中的那些数据还不是响应式的。所以在视图访问数据时都需要使用 data作为前缀才可以。这就比较麻烦了。
此时我们可以使用 toRefs 进行优化。toRefs可以将state中的每一个数据进行展开,且都包装成响应数据。这样视图层就可以直接使用了。
终极的写法
<template>
<div id="app">
<!--data.num -->
{{num}}<button @click="add">+</button><br>
{{userObj.userName}}
<ul v-for="(item,index) in userList" :key="item.id">
<li>{{item.userId}}-{{item.userName}}</li>
</ul>
</div>
</template>
<script>
//1、注意:要导入reactive
import {reactive,toRefs} from 'vue';
export default{
setup(){
//2 reactive({})定义数据
let data=reactive({
num:0,
userObj:{
userName:'wang.qj'
},
userList:[{userId:1,userName:'张三'}]
})
function add(){
data.num++;//3、 直接访问,不需要加.value,但要通过data访问
}
return {
...toRefs(data),
add};
}
}
</script>
● 在返回时使用 ...toRefs(data) ,这样视图层就可以不使用 data前缀了。
● 为什么要使用 ... 参数扩展运输符呢?因为toRefs(data) 将data对象展开,并包装成多个响应数据。
TIP:
ref 和 reactive 都可以做响应式数据,它们的区别如下:
● reactive:用于定义引用类型。只能修改数据,不能改变其引用。
● ref:用于定义基本类型和引用类型。可以修改数据,也可以改变其引用。
○ 在方法中修改数据时需要使用 value属性。因为,Ref的本质是通过Reactive创建的,Ref(10) 就相当于:Reactive({value:10});
○ 在视图模板调用可以省略value属性的书写。
1.4computed
<template>
<div id="app">
<!--data.num -->
...
{{comNum}}<br>
.
</div>
</template>
<script>
//1、注意:要导入reactive,toRefs,computed
import {reactive,toRefs,computed} from 'vue';
export default{
setup(){
//2 reactive({})定义数据
let data=reactive({
num:0,
userObj:{
userName:'wang.qj'
},
userList:[{userId:1,userName:'张三'}],
//计算属性
comNum:computed(()=>{
return data.num+"$"
})
})
function add(){
data.num++;//3、 直接访问,不需要加.value,但要通过data访问
}
return {
...toRefs(data),
add};
}
}
</script>
<style>
</style>
注意:计算属性写在reactive里
watch不需要写在reactive里
1.5监听器
● 使用watch函数来进行数据监听。watch函数有两个参数。
● 第一个参数:要监听的数据。
● 第二个参数:触发监听时的处理函数(包括newValue, oldValue)
<template>
<div id="app">
<!--data.num -->
{{num}}<button @click="add">+</button><br>
{{comNum}}<br>
{{userObj.userName}}
<ul v-for="(item,index) in userList" :key="item.id">
<li>{{item.userId}}-{{item.userName}}</li>
</ul>
</div>
</template>
<script>
//1、注意:要导入reactive
import {reactive,toRefs,computed,watch} from 'vue';
export default{
setup(){
//2 reactive({})定义数据
let data=reactive({
num:0,
userObj:{
userName:'wang.qj'
},
userList:[{userId:1,userName:'张三'}],
comNum:computed(()=>{
return data.num+"$"
})
})
watch(data,(newvalue,oldvlaue)=>{
console.log(newvalue)
console.log(oldvlaue)
})
function add(){
data.num++;//3、 直接访问,不需要加.value,但要通过data访问
}
return {
...toRefs(data),
add};
}
}
</script>
<style>
</style>
上面实例中直接监听state响应对象。但我们知道,在state中会有很多数据,如果只想监听其中的某个数据,就需要换一种写法:
<template>
<div id="app">
<!--data.num -->
{{num}}<button @click="add">+</button><br>
{{comNum}}<br>
{{userObj.userName}}
<ul v-for="(item,index) in userList" :key="item.id">
<li>{{item.userId}}-{{item.userName}}</li>
</ul>
</div>
</template>
<script>
//1、注意:要导入reactive
import {reactive,toRefs,computed,watch} from 'vue';
export default{
setup(){
//2 reactive({})定义数据
let data=reactive({
num:0,
userObj:{
userName:'wang.qj'
},
userList:[{userId:1,userName:'张三'}],
comNum:computed(()=>{
return data.num+"$"
})
})
watch(()=>data.num,(newvalue,oldvlaue)=>{
console.log(newvalue)
console.log(oldvlaue)
})
function add(){
data.num++;//3、 直接访问,不需要加.value,但要通过data访问
}
return {
...toRefs(data),
add};
}
}
</script>
<style>
</style>
第一个参数要写成函数返回值的形式,这样就能监听state响应对象中的某个数据了。