Vue(Vue2+Vue3)——80-83常用的Composition(组合)API

简介: Vue(Vue2+Vue3)——80-83常用的Composition(组合)API

Vue(Vue2+Vue3)——80-83常用的Composition(组合)API


CompositionAPI也叫组合式API

官方文档: https://v3.cn.vuejs.org/guide/composition-api-introduction.html


1 初识setup


setup是vue3的入门技术,想要学习vue3,最好从它开始,因为setup是所有Composition API(组合API)表演的舞台。如果没有setup,那么其他的组合式API都没地方写

setup是Vue3.0中一个新的配置项,值为一个函数,组件中所用到的:数据、方法等等,均要配置在setup中,它有两种返回值。对象和渲染函数,下面通过案例一一说明使用

注意:此处的案例只是测试一下setup,暂时不考虑响应式的问题。


返回对象(重点关注)


如果setup返回一个对象,则对象中的属性、方法, 在模板中均可以直接使用,这种返回方式也是用的比较多的一种

比如这里我定义了一些数据和方法。返回对象的时候如果key和value一致,那么可以只保留key

使用的时候直接进行插值语法获取和事件绑定即可,这里比vue2多了一步:需要手动return方法或数据

数据肯定是可以通过setup返回对象的方式获取到的


返回渲染函数(了解即可)


setup除了可以返回对象,还可以返回渲染函数,可以自定义渲染内容,这种返回方式相对来说用的比较少(了解即可)

在渲染之前需要手动引入


引入渲染h


vue3一个很大的特点就是使用什么就直接引入

这个h vue3已经帮助我们封装好了,直接引入并使用即可

// 引入渲染h
import {h} from 'vue'


细节问题


虽然在vue3中可以写一个vue2相关的配置,包括data、methos、computed...但是并不推荐在vue3中写vue2相关代码,因为虽然vue2可以访问到setup中的属性、方法,但是但在setup中不能访问到Vue2配置的data、methos、computed,另外如果有重名, setup优先


setup总结


  1. 理解:Vue3.0中一个新的配置项,值为一个函数。
  2. setup是所有Composition API(组合API) 表演的舞台 ”
  3. 组件中所用到的:数据、方法等等,均要配置在setup中。
  4. setup函数的两种返回值:
  1. 若返回一个对象,则对象中的属性、方法, 在模板中均可以直接使用。(重点关注!)
  2. 若返回一个渲染函数:则可以自定义渲染内容。(了解)
  1. 注意点:
  1. 尽量不要与Vue2.x配置混用
  1. Vue2.x配置(data、methos、computed...)中可以访问到setup中的属性、方法。
  2. 但在setup中不能访问到Vue2.x配置(data、methos、computed...)。
  3. 如果有重名, setup优先。
  1. setup不能是一个async函数,因为返回值不再是return的对象, 而是promise, 模板看不到return对象中的属性。(后期也可以返回一个Promise实例,但需要Suspense和异步组件的配合)


2 ref(引用)函数


此ref不同于vue2中元素中的ref属性,在vue2中它用来定义给元素定义ref属性控制元素的操作,类似于js的id,但是在vue3中它用于定义一个响应式的数据

它可以定义普通数据和对象类型的数据,两者之前有些不同,下面通过案例进行一一说明


案例演示(处理基本类型)


新增一个按钮,对已有的信息进行更改


发现问题


理论来说代码是没问题的,但是点击按钮数据不变,但是控制台也没有报错

为了验证是否修改成功,这边打印下

看起来数据是修改成功的,但是vue没有检测到数据的改变

问题是因为定义的name和age都是普通的字符串,不是响应式的数据,所以vue是不会检测到的,那么问题来了,如果把普通的数据定义成vue能监测到的响应式的数据呢?

这时候就要使用ref了


引入ref对象


使用ref之前,需要引入

// 引入ref对象
  import {ref} from "vue";


使用ref对象


ref对象使用起来特别的简单,直接使用ref(xxx)即可

这时候案例来说就可以了,我们再次测试下

发现还是不行


refImpl(引用对象)


这是因为修改的方式不对,可以看到被ref修饰后的属性变成了一个refImpl对象

refImpl这个单词拆分的话可以这么理解,ref(reference)引用,impl(implment)实现,专业话语叫:引用对象

它也有getter和setter,并且我们可以通过vulue修改属性的值

所以如果想要修改属性,可以使用value进行修改

再次修改


一个奇怪的现象


案例来说,插值语法读取属性的时候,也是应该通过xxx.value获取的,但是如果这么写就获取不到

这是因为:在模板里面,不需要手动的.value的方式获取,vue3会自动帮助我们解析,如果发现是一个ref对象,就会自动帮我们获取.value属性


案例演示(处理对象类型)


除了可以处理基本类型,ref也支持处理对象类型,接下来升级一下案例,演示使用ref处理对象类型

定义一个对象类型的属性

把数据展示页面上

接下来提出需求,修改信息的时候把工作种类和薪水都改下,如果按照上面的写法使用.value获取,那就错了

如果使用ref传入对象,想要拿到属性就不用.value了


这说明,ref对基本数据类型直接使用的get和set,但是对于对象类型性来说,用的是ES6中的proxy


ref总结


  • 作用: 定义一个响应式的数据
  • 语法: const xxx = ref(initValue)
  • 创建一个包含响应式数据的引用对象(reference对象,简称ref对象)
  • JS中操作数据: xxx.value
  • 模板中读取数据: 不需要.value,直接:<div>{{xxx}}</div>
  • 备注:
  • 接收的数据可以是:基本类型、也可以是对象类型。
  • 基本类型的数据:响应式依然是靠Object.defineProperty()getset完成的。
  • 对象类型的数据:内部 “ 求助 ” 了Vue3.0中的一个新函数—— reactive函数。


3 reactive(响应式)函数


它和ref类似,也是把数据定义成响应成数据

注意:它不同于ref,只能定义对象类型的数据,不能定义基本类型的数据,所以说基本类型数据要使用ref定义,对象类型数据最好使用reactive函数

那么首先就可以验证只能使用reavtive定义基本类型数据这一说了


引用reactive


和ref一样,直接引用即可

// 引入ref和reactive函数
  import {ref,reactive} from "vue";

接下来我使用reactive定义一个普通数据

控制台直接报错了

所以应该改成一个对象类型的数据,如果使用了reactive定义了,获取属性的时候直接获取即可,就不再使用.value了

效果都是一样的

验证:深层次处理数据

reactive是处理对象类型的响应式的时候,是一个深层次的

可以定义一个套娃式的对象,使用的使用直接套就行

验证:处理数组类型数据

首先定义一个数据类型的数据

这时候点击修改,数据没有变,但是其实已经修改了

之所以没有变是因为vue没有检测到,所以要定义成响应式的

再次测试下,发现已经改了,说明reactive也是支持数组的

reactive定义数据技巧

如果非要使用reactive定义普通类型的数据,可以这么玩,把组件种用到的数据封装到一个对象里面,然后把这个对象交给reavtive修饰变成一个响应式对象或代理对象

效果也是一样的


reactive总结


  • 作用: 定义一个对象类型的响应式数据(基本类型不要用它,要用ref函数)
  • 语法:const 代理对象= reactive(源对象)接收一个对象(或数组),返回一个代理对象(Proxy的实例对象,简称proxy对象)
  • reactive定义的响应式数据是“深层次的”。
  • 内部基于 ES6 的 Proxy 实现,通过代理对象操作源对象内部数据进行操作。


4 ref对比reactive


  • 从定义数据角度对比:
  • ref用来定义:基本类型数据
  • reactive用来定义:对象(或数组)类型数据
  • 备注:ref也可以用来定义对象(或数组)类型数据, 它内部会自动通过reactive转为代理对象
  • 从原理角度对比:
  • ref通过Object.defineProperty()getset来实现响应式(数据劫持)。
  • reactive通过使用Proxy来实现响应式(数据劫持), 并通过Reflect操作源对象内部的数据。
  • 从使用角度对比:
  • ref定义的数据:操作数据需要.value,读取数据时模板中直接读取不需要.value
  • reactive定义的数据:操作数据与读取数据:均不需要.value


5 set的两个注意点


执行的时机


setup执行的时机是非常早的,它甚至比beforeCreate还要早,并且this是undefined而不是实例对象


setup接收到的参数


通过测试得知,setup可以接受到两个参数


参数1:props:用于组件传递数据


我们可以通过组件传递数据测试:

这时候出现了警告:

意思是给组件传递了数据但是缺没有接收数据,这个警告在vue2中是没有的

想用的话需要声明接收,这时候再看警告就没了,并且props里面接收到了参数

如果接收一个不存在的数据,也不会报错,只是会呈现undefined

参数2:context

说完了第一个参数,再说说第二个参数,在官方文档里面叫做context(上下文)

它里面主要有三个属性需要我们注意

1:attrs: 值为对象,包含:组件外部传递过来,但没有在props配置中声明的属性, 相当于 this.$attrs

2:slots: 收到的插槽内容, 相当于 this.$slots

3:emit: 分发自定义事件的函数, 相当于 this.$emit


set的两个注意点总结


  • setup执行的时机
  • 在beforeCreate之前执行一次,this是undefined。
  • setup的参数
  • props:值为对象,包含:组件外部传递过来,且组件内部声明接收了的属性。
  • context:上下文对象
  • attrs: 值为对象,包含:组件外部传递过来,但没有在props配置中声明的属性, 相当于 this.$attrs
  • slots: 收到的插槽内容, 相当于 this.$slots
  • emit: 分发自定义事件的函数, 相当于 this.$emit


6 computed计算属性


与Vue2中computed配置功能一致,都是通过已有的数据算出点新东西


案例演示



vue3中也是支持vue2中编写计算属性的方式的,但是不推荐(都用vue3了,还写啥vue2啊!)

下面通过案例演示分别演示vue2和vue3的写法


vue2实现



vue3实现


vue中把computed

变成了组合式的API,所以首先要引入computed


引入computed


// 引入computed计算属性
  import {computed} from "vue";
简写形式

计算属性也是属性,也是可以被修改的,简写形式并不能支持被修改,如果被修改会报警错

这时候就要用到完整形式了

完整形式

和vue2是一样的,使用get和set编写逻辑


案例演示


计算属性总结


  • 与Vue2中computed配置功能一致
  • 写法:
import {computed} from 'vue'
setup(){
    ...
        //计算属性——简写
    let fullName = computed(()=>{
        return person.firstName + '-' + person.lastName
    })
    //计算属性——完整
    let fullName = computed({
        get(){
            return person.firstName + '-' + person.lastName
        },
        set(value){
            const nameArr = value.split('-')
            person.firstName = nameArr[0]
            person.lastName = nameArr[1]
        }
    })
}


7 watch(监视)函数


vue3的数据监视和vue2配置功能是一样的,都是对变化的数据进行监视


案例展示


vue3中也是支持vue2中编写数据监视方式的,但是不推荐(都用vue3了,还写啥vue2啊!)

下面通过案例演示分别演示vue2和vue3的写法


vue2实现


分为简单写法和完整写法


简单形式


直接把要监视的数据写成一个函数,会有两个参数,新值和老值


完整形式


完整形式要把监视的属性写成一个对象,而不是函数,对象里面有很多的配置,其中最重要的是handler,它同样可以接受到新值和老值两个参数

它相比于简单形式,优势在于可以配置更多的属性,比如立即监听(immediate),深度监视(deep)等

vue3实现


以上无论是简单形式还是完整形式,都只是vue2中的写法

在vue3中,watch和computed一样,变成了一个组合式的API,使用的时候需要先引入。

引入watch


// 引入watch函数
  import {watch} from "vue";

引入完成之后就可以使用了,在vue3中使用watch十分简单,但是有一些不同的使用场景和坑,下面说明

情况1:监视定义ref定义的一个响应式数据

情况2:监视定义ref定义的多个响应式数据

这时候新增一个需求,再定义一个ref响应式数据进行监听

最笨的办法就行,再写一个watch监听,把监听的数据写成info

这也是vue3和vue2的差距,在vue2中怎么可能写多个watch呢,只能写一个watch配置项,但是在vue3中就可以写多个watch函数

但是这种写法较为麻烦,有一个简单的写法,只写一个watch函数,监视的值为一个数组:

watch中加入属性

现在想在watch中加入一些属性,比如自动监听,这时候watch的第三个参数的作用就体现出来了,就是专门给我们加入监听的属性用的

watch的三个参数:

参数1:监视数据(属性),参数2:回调函数,参数3:配置属性

一上来就进行数据监视了,说明属性生效了

情况3:监视reactive定义的一个响应式数据全部属性

刚才监视的都是ref普通数据类型,如果使用reactive监视对象类型的数据,会有一些坑,下面演示一下

坑1:无法获取到正确的oldValue

注意:这里有一个坑,可以发现不管是姓名还是年龄,改变前后都一样,无法获取到正确的oldValue,而且这个问题目前无法解决

坑2:强制开启了深度监视且无法关闭

vue3默认(强制)开启了deep(深度监视)并且无法关闭

可以看到即使把deep设置为false,也是可以监视到的

既然deep属性关不了,那么肯定是影响效率的,所以就有了下面这种情况

情况4:监视reactive定义的一个响应式数据中的某一个属性

我们可以只监视对象中的某一个数学,可以使用函数,返回值就是这个属性:

情况5: 监视reactive定义的一个响应式数据中的某些属性

这种写法稍微复杂点,使用数组包裹起来要监视的属性即可:

特殊情况:

如果要监视reactive里面的多层次对象,需要开启deep:true:

watch函数总结

  • 与Vue2.x中watch配置功能一致
  • 两个小“坑”:
  • 监视reactive定义的响应式数据时:oldValue无法正确获取、强制开启了深度监视(deep配置失效)。
  • 监视reactive定义的响应式数据中某个属性时:deep配置有效。
//情况一:监视ref定义的响应式数据
watch(sum,(newValue,oldValue)=>{
        console.log('sum变化了',newValue,oldValue)
},{immediate:true})
//情况二:监视多个ref定义的响应式数据
watch([sum,msg],(newValue,oldValue)=>{
        console.log('sum或msg变化了',newValue,oldValue)
}) 
/* 情况三:监视reactive定义的响应式数据
                        若watch监视的是reactive定义的响应式数据,则无法正确获得oldValue!!
                        若watch监视的是reactive定义的响应式数据,则强制开启了深度监视 
*/
watch(person,(newValue,oldValue)=>{
        console.log('person变化了',newValue,oldValue)
},{immediate:true,deep:false}) //此处的deep配置不再奏效
//情况四:监视reactive定义的响应式数据中的某个属性
watch(()=>person.job,(newValue,oldValue)=>{
        console.log('person的job变化了',newValue,oldValue)
},{immediate:true,deep:true}) 
//情况五:监视reactive定义的响应式数据中的某些属性
watch([()=>person.job,()=>person.name],(newValue,oldValue)=>{
        console.log('person的job变化了',newValue,oldValue)
},{immediate:true,deep:true})
//特殊情况
watch(()=>person.job,(newValue,oldValue)=>{
    console.log('person的job变化了',newValue,oldValue)
},{deep:true}) //此处由于监视的是reactive素定义的对象中的某个属性,所以deep配置有效


8 watchEffect()函数


watchEffect()函数是vue3中的一个新的函数,Effect翻译过来有反应的意思,所以又叫监视反应

它也可以做监视,但是它最大的特点就是不说监视谁,直接写回调

下面通过案例演示下watchEffect的简单使用


引用watchEffect


和其他函数一样,在vue3中想要使用,就必须先定义

// 引入watchEffect函数
  import watchEffect} from "vue";


编写watchEffect


它最大的特点就是不说监视谁,直接写回调,而且没有newValue和oldValue这两个参数

可以发现一上来就执行了,但是谁也每被监视

watchEffect很智能,它会通过函数体发现用到了哪个属性,用到了哪个属性才会监视哪个属性,并且会自动解析多级对象

这样就能帮助我们写一些复杂的逻辑,只要监测到改变哪个属性,就能执行哪些逻辑或者函数,可以提高我们的开发效率


watchEffect函数总结


  • watch的套路是:既要指明监视的属性,也要指明监视的回调。
  • watchEffect的套路是:不用指明监视哪个属性,监视的回调中用到哪个属性,那就监视哪个属性。
  • watchEffect有点像computed:
  • 但computed注重的计算出来的值(回调函数的返回值),所以必须要写返回值。
  • 而watchEffect更注重的是过程(回调函数的函数体),所以不用写返回值。
//watchEffect所指定的回调中用到的数据只要发生变化,则直接重新执行回调。
watchEffect(()=>{
    const x1 = sum.value
    const x2 = person.age
    console.log('watchEffect配置的回调执行了')
})


9 vue3生命周期


  • Vue3.0中可以继续使用Vue2.x中的生命周期钩子,但有有两个被更名:
  • beforeDestroy改名为 beforeUnmount
  • destroyed改名为 unmounted
  • Vue3.0也提供了 Composition API 形式的生命周期钩子,与Vue2.x中钩子对应关系如下:
  • beforeCreate===>setup()
  • created=======>setup()
  • beforeMount ===>onBeforeMount
  • mounted=======>onMounted
  • beforeUpdate===>onBeforeUpdate
  • updated =======>onUpdated
  • beforeUnmount ==>onBeforeUnmount
  • unmounted =====>onUnmounted


10 自定义hook函数


hook(钩住)本质上是一个函数,把setup函数中使用的Composition API进行封装成自定义的公共逻辑,如果其他组件想要使用这个逻辑直接引入hook即可,类似vue2中的mixin(混入)

hook把组合式API体现的淋漓尽致,下面通过案例演示一下hook的使用,一起感受下hook函数的强大以及什么叫组合式API。


编写案例


现在想编写一个鼠标点击页面,展示对应x,y之的功能。

虽然效果实现了,但是完全如果哪天某一个组件也想要这个功能呢,难道要重新写一遍吗,这个时候就体现出来hook的作用了,我们完全可以把这些逻辑写在hook中,哪些组件想要使用这些逻辑,直接引入这个hook文件就行

编写hook文件


一般编写hook文件,都会在src里面定义一个hooks文件,里面专门存放hook函数,这样文件夹就命名好了

关于文件名,理论来说是可以随便命名,但是为了规范,hook文件有一个特点:一般会以usrxxx开头,像我们这个hook逻辑,就是针对坐标点进行操作的,所以就可以命名为usePoint。

然后就把公共的逻辑放到一个函数里面返回即可,需要什么就在hook中引入即可

这样一个简单的hook函数自定义逻辑就完成了,我们只需要引入它使用即可!

注意不要忘了把这个hook文件进行暴露!!!


引入hook文件并使用


直接引入hook文件,因为hook本质就是一个函数,并且有返回值,所以我们可以直接调用接收返回值

我们只关心hook的返回结果,并不关心hook里面的逻辑

效果也是一样的

这个时候如果有一个组件也想实现这个功能,那也是一样的引入hook即可


自定义hook函数总结


  • 什么是hook?—— 本质是一个函数,把setup函数中使用的Composition API进行了封装。
  • 类似于vue2.x中的mixin。
  • 自定义hook的优势: 复用代码, 让setup中的逻辑更清楚易懂。


11 toRef API和toRefs API


toRef也是一个很强大并且常用的API,它用于创建一个 ref 对象,并且value值指向另一个对象中的某个属性

下面通过案例演示下toRef API的使用


编写案例


现在有个问题,我返回的是一个person对象,所以用到里面的属性都要通过person.的方式来获取,那么能不能优化一下呢?

当然是可以的,我们可以通过toRef去优化


引入并使用toRef API


和其他函数一样,使用之前需要引入

// 引入toRef API
  import {toRef} from "vue";

它有两个参数:参数1:对象,参数2:对象里面的属性

也是可以正常修改数据的

但是这样频繁的写toRef也不好,所以就有了toRefs这个API


引入并使用toRefs API


toRefs是toRef的升级版,它可以一次性处理多个对象属性

和toRef一样,第一步也要先引入

// 引入toRefs API
  import {toRefs} from "vue";

引入完成后就可以使用了

但是toRefs只能获取第一层对象,所以处理多层对象的时候,还得自己手动嵌套处理


toRef总结


  • 作用:创建一个 ref 对象,其value值指向另一个对象中的某个属性。
  • 语法:const name = toRef(person,'name')
  • 应用:   要将响应式对象中的某个属性单独提供给外部使用时。
  • 扩展:toRefstoRef功能一致,但可以批量创建多个 ref 对象,语法:toRefs(person)
相关文章
|
11小时前
|
JavaScript API UED
Vue3中的Suspense组件有什么用?
Vue3中的Suspense组件有什么用?
15 6
|
11小时前
|
JavaScript 前端开发 索引
Vue3基础之v-if条件与 v-for循环
Vue3基础之v-if条件与 v-for循环
5 0
|
11小时前
|
JavaScript
Vue3基础之v-bind与v-on
Vue3基础之v-bind与v-on
7 0
|
11小时前
|
JavaScript 测试技术 API
Vue3中定义变量是选择ref还是reactive?
Vue3中定义变量是选择ref还是reactive?
11 2
|
11小时前
|
移动开发 前端开发
ruoyi-nbcio-plus基于vue3的flowable为了适配文件上传改造VForm3的代码记录
ruoyi-nbcio-plus基于vue3的flowable为了适配文件上传改造VForm3的代码记录
17 1
|
11小时前
|
JavaScript 前端开发
vue2升级到vue3的一些使用注意事项记录(四)
vue2升级到vue3的一些使用注意事项记录(四)
11 0
|
11小时前
|
移动开发 前端开发
ruoyi-nbcio-plus基于vue3的flowable收回任务后重新进行提交表单的处理
ruoyi-nbcio-plus基于vue3的flowable收回任务后重新进行提交表单的处理
13 0
|
11小时前
|
移动开发 前端开发
ruoyi-nbcio-plus基于vue3的flowable多租户机制
ruoyi-nbcio-plus基于vue3的flowable多租户机制
|
11小时前
|
移动开发 前端开发
ruoyi-nbcio-plus基于vue3的flowable的消息中心我的消息的升级修改
ruoyi-nbcio-plus基于vue3的flowable的消息中心我的消息的升级修改
|
11小时前
|
JavaScript 前端开发
vue3中使用动态组件
vue3中使用动态组件