在Vue2中,我们
编写组件的方式是Options API
:
Options API的一大特点就是在
对应的属性
中编写
对应的功能模块
;
比如
data定义数据
、
methods中定义方法
、
computed中定义计算属性
、
watch中监听属性改变
,也包括
生命周期钩子
;
但是这种代码有一个很大的弊端:
当我们
实现某一个功能
时,这个功能
对应的代码逻辑
会被
拆分到各个属性
中;
当我们
组件变得更大、更复杂
时,
逻辑关注点的列表
就会增长,那么
同一个功能的逻辑就会被拆分的很分散
;
尤其对于那些一开始
没有编写这些组件的人
来说,这个组件的代码是
难以阅读和理解的
(阅读组件的其他人);
在Vue3中新增了Composition API
Composition API的出现呢个将同一个逻辑关注点相关代码收集在一起,使开发者的阅读成本降低了,大大增加了代码的可读性
下面就介绍一下Vue3新增组合式API
为了开始使用Composition API,我们需要有一个可以实际使用它的地方
在Vue3中这个位置就是setup函数
下面介绍一些setup函数
setup的使用
我们先来研究一个setup函数的参数,它主要有两个参数:
第一个参数:props
props非常好理解,它其实就是父组件传递过来的属性会被放到props对象中,我们在setup中如果需要使用,那么就可以直接通过props参数获取 (setup函数中的props是用法和Vue2中用法一样)
第二个参数:context 我们也称之为是一个SetupContext,它里面包含三个属性:
attrs
:所有的非prop的attribute;
slots
:父组件传递过来的插槽
emit:
当我们组件内部需要发出事件时会用到emit(因为我们不能访问this,所以不可以通过 this.$emit发出事件
setup既然是一个函数,那就可以有返回值, 那它的作用是什么呢?
setup的函数返回值可以在模板tamplate中使用,也就是说可以使用setup函数中的返回值代替data选项,甚至是我们可以返回一个执行函数来代替在methods中定义的方法
如果我们将 age 在 changeAge进行操作时,是否可以实现界面的响应式呢?
答案是不可以,这是因为对于一个定义的变量来说,默认情况下,Vue并不会跟踪它的变化,来引起界面的响应式操作
那么怎样才能为数据提供响应式特征呢,我们需要使用Reactive API
Reactive的使用
那么这是什么原因呢
这是因为当我们
使用reactive函数处理我们的数据之后
,数据
再次被使用
时就会
进行依赖收集
;
当
数据发生改变
时,所有
收集到的依赖
都是
进行对应的响应式操作
(比如更新界面);
Ref的使用
reactive API对
传入的类型是有限制的
,它要求我们必须传入的是
一个对象或者数组类型:
如果我们传入一个
基本数据类型(String、Number、Boolean)会报一个警告
;
这个时候Vue3给我们提供了另外一个API:ref API
ref会返回一个可变的响应式对象,改对象座位一个响应式的引用维护着它内部的值
它内部的值是在ref的value属性中被维护的
在模板中引入ref的值时,Vue会自动帮助我们进行解包操作,所有我们并不需要在模板中通过ref.value的方式来使用
但是在setup函数内部,它依然是一个ref引用,所以对其进行操作时,我们依然需要使用,ref.value的方式
readonly的使用
某些情况下,我们
传入给其他地方(组件)
的这个 响应式对象希望在另外一个地方(组件)被使用
,但是
不能被修改
,这个时候
如何防止这种情况的出现
呢?
Vue3为我们提供了readonly方法
readonly会返回原生对象的只读代理
(也就是它依然是一个Proxy,这是一个
proxy的set方法被劫持
,并且不 能对其进行修改)
在开发中常见的readonly方法会传入三个类型的参数:
类型一:
普通对象;
类型二:
reactive返回的对象;
类型三:
ref的对象;
在readonly的使用过程中,有如下规则:
readonly返回的对象都是不允许修改的;
但是经过readonly处理的原来的对象是允许被修改的;
比如 const info = readonly(obj),info对象是不允许被修改的;
当obj被修改时,readonly返回的info对象也会被修改;
但是我们
不能去修改readonly返回的对象info;
其实本质上就是
readonly返回的对象的setter方法
被劫持了而已;
Reactive判断的API
isProxy
检查对象是否是由 reactive 或 readonly创建的 proxy。
isReactive
检查对象是否是由 reactive创建的响应式代理:
如果该代理是 readonly 建的,但包裹了由 reactive 创建的另一个代理,它也会返回 true;
isReadonly
检查对象是否是由 readonly 创建的只读代理。
toRaw
返回 reactive 或 readonly 代理的原始对象(不建议保留对原始对象的持久引用。请谨慎使用)。
shallowReactive
创建一个响应式代理,它跟踪其自身 property 的响应性,但不执行嵌套对象的深层响应式转换 (深层还是原生对象)。
shallowReadonly
创建一个 proxy,使其自身的 property 为只读,但不执行嵌套对象的深度只读转换(深层还是可读、可写的)。
toRefs和toRef
Vue为我们提供了一个toRefs和toRef的函数,可以将reactive返回的对象中的属性都转成ref
那么我们再次进行结构出来的 name 和 age 本身都是 ref的;
如果我们只希望转换一个
reactive对象中的属性为ref
, 那么可以使用toRef的方法:
ref的其他API
unref
如果我们想要获取一个ref引用中的value,那么也可以通过unref方法:
如果参数是一个 ref,则返回内部值,否则返回参数本身;
这是 val = isRef(val) ? val.value : val 的语法糖函数;
isRef
判断值是否是一个ref对象。
shallowRef
创建一个浅层的ref对象;
triggerRef
手动触发和 shallowRef 相关联的副作用
computed
在前面的Options API中,我们是使用computed选项来完成的;
在Composition API中,我们可以在 setup 函数中使用 computed 方法来编写一个计算属性;
如何使用computed呢?
方式一:接收一个getter函数,并为 getter 函数返回的值,返回一个不变的 ref 对象;
方式二:接收一个具有 get 和 set 的对象,返回一个可变的(可读写)ref 对象;
watchEffect
当侦听到某些响应式数据变化时,我们希望执行某些操作,这个时候可以使用 watchEffect
watchEffect传入的函数会被立即执行一次,并且在执行的过程中会收集依赖;
只有收集的依赖发生变化时,watchEffect传入的函数才会再次执行
我们希望停止侦听,这个时候我们可以获取watchEffect的返回值函数,调用该函数即可。
在我们给watchEffect传入的函数被回调时,其实可以获取到一个参数:onInvalidate
当副作用即将重新执行 或者 侦听器被停止 时会执行该函数传入的回调函数;
我们可以在传入的回调函数中,执行一些清楚工作;
调整watchEffect的执行时机
当setup函数在执行时就会立即执行传入的函数,watchEffect可以接受第二个参数是一个对象
flush有三个选项,pre(默认选项) 它会在元素 挂载 或者 更新 之前执行,post元素挂载完毕,sync这将强制效果始终同步触发。然而,这是低效的,应该很少需要。
watch的使用
watch的API完全等同于组件watch选项的Property
watch侦听函数的数据源有两种类型:
一个getter函数:但是该getter函数必须引用可响应式的对象(比如reactive或者ref)
直接写入一个可响应式的对象,reactive或者ref(比较常用的是ref);
侦听器还可以使用数组同时侦听多个源:
生命周期钩子
setup() : 开始创建组件之前,在 beforeCreate 和 created 之前执行,创建的是 data 和 method
onBeforeMount() : 组件挂载到节点上之前执行的函数;
onMounted() : 组件挂载完成后执行的函数;
onBeforeUpdate(): 组件更新之前执行的函数;
onUpdated(): 组件更新完成之后执行的函数;
onBeforeUnmount(): 组件卸载之前执行的函数;
onUnmounted(): 组件卸载完成后执行的函数;
onActivated(): 被包含在 <keep-alive> 中的组件,会多出两个生命周期钩子函数,被激活时执行;
onDeactivated(): 比如从 A 组件,切换到 B 组件,A 组件消失时执行;