0 前言
整理了一下自己在学Vue3的时候的笔记,如果有错误的地方还望指正~
1 setup函数
1.1 参数 setup(props,context)
props:父组件传递过来的属性
context:SetupContext,即是setup函数的上下文
1.1.1 参数1 props
如果想在setup函数中使用父组件传递过来的props,则是通过props这个参数获得。在Vue2中我们是可以同个this.props的形式获取props,但是在Vue3之后setup函数中不允许使用this,当然还有一个注意点是,你再setup函数接收父组件传递的props参数,但是在setup函数外还需要定义props选项,因为你需要约束需要接收什么类型的参数,什么参数。
注意点:
对于定义props的类型,还是和之前的规则是一样的,在props选项中定义
并且在template依然可以正常取使用props中的属性,比如message
如果我们在setup函数中要使用props,那么不可以使用this取获取
props有直接作为参数传递到setup函数中,所以我们可以直接通过参数来使用即可
1.1.2 参数2context
attrs:所有非prop的attribute
slots:父组件传递过来的插槽(
emit:当我们组件内部需要发布事件时会用到emit(因为我们不能访问this,所以不可以通过this.$emit发出事件
1.2 setup函数的返回值
setup既然是一个函数,那么它也可以有返回值,它的返回值用来做什么呢?
setup的返回值可以在模板template中被使用
也就是说我们可以通过setup的返回值代替data选项
甚至我们可以返回一个执行函数来代替methods中定义的方法
注意:以上对变量的操作时非响应式的,那么对变量进行+or-的操作不会是想界面的响应式的,要实现响应式还是需要我们下面引入的ref,reactive
1.3 setup中不可以使用this
其中,官网也有提及到,我们在setup中应该避免使用this,因为他不会找到组件实例,setup的调用发生在data、 computed或者methods被解析之前,所以它们无法在setup中被获取
上面这段话表达的含义:
this并没有指向当前组件实例
并且在setup被调用之前、data、computed、methods等都没有被解析
所以他无法在setup中获取this
2.Reactive
我们知道,我们在Vue2或者说在非setup函数中定义的data,return出来的对象属是具有响应式的,其实是Vue源码内部将这个data放在reactive(data)进行了响应式的处理,而在setup函数中,你只是单纯的返回变量的话,这时setup并没有做响应式的处理的。
2.1 使用
import {reactive} from 'vue'
setup(){
const state=reactive({
counter:100
})
return {
state
}
}
//模板中使用:{{state.counter}}
2.2 Reactive判断的API
isProxy
1)检查对象是否是由 reactive 或 readonly创建的 proxy。
isReactive
1)检查对象是否是由 reactive创建的响应式代理:
2)如果该代理是 readonly 建的,但包裹了由 reactive 创建的另一个代理,它也会返回 true;
isReadonly
1)检查对象是否是由 readonly 创建的只读代理。
toRaw
1)返回 reactive 或 readonly 代理的原始对象(不建议保留对原始对象的持久引用。请谨慎使用)。
shallowReactive
1)创建一个响应式代理,它跟踪其自身 property 的响应性,但不执行嵌套对象的深层响应式转换 (深层还是原生对象)。
shallowReadonly
1)创建一个 proxy,使其自身的 property 为只读,但不执行嵌套对象的深度只读转换(深层还是可读、可写的)
3. Ref
reactive API对传入的类型是有限的,它要求我们必须传入的是一个对象或者数组类型,如果我们传入一个基本类型的数组(String、Number、Boolean)会报一个警告,这时候Vue3给我们提供了另外一个Ref API
ref会返回一个可变的响应式对象,该对象作为一个响应式的引用维护着它内部的值,就是ref名称的来源
它内部的值是在ref的value属性中被维护的
const message = ref('hi')
注意点:
在模板中引入的ref的值时,Vue会自动帮助我们进行解包(即ref实质返回的是一个对象,比如message.value,但是解包之后即可以直接使用message)操作,所以我们并不需要在模板中通过ref.value的方式来使用,即直接使用{{message}}即可
但是在setup函数内部,它依然是一个ref引用(对象),所以对其进行操作的时候,我们依然需要使用ref.value的方式
3.1 关于Ref的自动解包
模板中的解包是浅层解包,比如我们将ref中的一个属性message赋值给另外一个变量info,这时在模板中使用,是不会解包的:{{info.message.value}}
若将ref中的一个属性message赋值给一个使用reactive包裹的属性,这时候会深层解包:{{info.message}}
3.2 toRefs API
注意:我们对响应式数据进行结构之后,其不具备响应式。要想将结构出来的数据具有响应式,那么可以使用toRefs API进行转换。使用toRef将info进行转换之后,info对象里的属性值即变为ref类型具有响应式的数据。因此进行{}结构的时候,其属性的值也是具有响应式的ps:若不适用toRefs进行转换的话,其实结构出来的值实质是值得结构,相当于常量不具有响应式
如果我们使用ES6的结构语法,对reactive返回的对象进行结构,那么之后无论是修改结构的变量,还是修改reactive返回的state对象,数据都不再是响应式的
Vue为我们提供了一个toRefs函数,可以将reactive返回的对象中的属性转成ref,那么我们再次进行结构出来的name、age本身都是ref类型的响应式数据
以上做法相当于已经在state.name和ref.value之间建立了链接,任何一个修改都会引起另外一个变化
3.3 toRef API
如果我们只希望转换一个reactive对象中的属性为ref,那么可以使用toRef的方法:
3.4 unref
如果我们想要获取一个ref引用的value,那么也可以通过unref方法
如果参数是一个ref,则返回内部值,否则返回参数本身
这是val=isRef(val)? val.value : val的语法糖函数
const name =ref('hi')
foo('hi')
function foo(bar){
unref(bar)//isRef(val)? val.value : val
}
3.5 isRef
判断值是否是一个ref对象
3.6 shallowRef
创建一个浅层的ref对象
默认是深层次的拷贝的,如下:
使用shallowRef之后
3.7 customRef
4. readonly
5. computed
同于Vue2的原理,当我们某些属性是依赖其他状态时,我们可以使用计算属性
在Vue2中即Options API 中,我们使用computed项来完成
在Composition API中,我们可以在setup函数中使用computed方法来编写一个计算属性
5.1使用方式
方式1:接收一个getter函数,并未getter函数返回的值,返回一个不变的ref对象
方式2:接收一个具有get和set的对象,返回一个可变的(可读写)ref对象
没有改变computed值需求的使用方式1,有则使用方式2
6. 侦听数据变化
在Vue2的Options API中,我们可以通过watch选项来侦听data或者props的数据变化,当数据变化时执行操作
在Vue3的Compositions API中,我们可以使用watch和watchEffect来完成响应式数据的侦听
watchEffect用于自动收集响应式数据的依赖
watch需要手动侦听的数据源
6.1 watchEffect
当真听到某些响应式数据发生变化的时候,我们希望执行某些操作,这个时候就可以使用watchEffect
默认情况下:
首先,watchEffect传入的函数会被立即执行一次,并在执行的过咸亨中收集依赖
其次,只有收集的依赖发生变化时,watchEffct传入的函数才会再次执行
页面挂载的时候会立即执行一次,是否会执行第二次取决于在watchEffect函数中、是否引用了响应式数据,若有,则当引用的数据发生变化的时候,会自动执行
特定情况下:
watchEffect还可以接收第二个参数,第二个参数是一个对象,对象中的flush属性执行watchEffect的执行时机。默认的flush为pre即在元素 挂载 或者 更新 之前执行。
pre:页面挂载之前执行watchEffect的回调函数
post:指定页面挂载之后再执行watchEffect的回调函数
sync:强制效果始终同步触发(少用)
6.2.1 watchEffct的停止侦听
6.2.2 watchEffect清除副作用
什么是副作用?
比如我们开发中我们需要在侦听还能输中执行网络请求,但是在网络请求还没有达到的时候,我们就停止了侦听或者侦听函数被再次执行了
那么上一次的网络请求应该被取消掉,这个时候我们就可以清除上一次的副作用
使用:
我们在给watchEffect传入的函数被回调时,其实可以获取到一个参数:onInvalidate
当副作用即将被重新执行或者侦听器被停止(组件被销毁阶段)时会执行该函数传入的回调函数
我们可以在传入的回调函数中,执行一些清除的操作
场景:在很多时候,我们会对数据进行侦听,当数据发生改变的时候,我们会侦听拿到这个最新的数据,然后发送网络请求,但是需要注意,网络请求是异步的,而当你频繁的改变侦听的属性的值的时候,那么就会频繁的进行网络请求,而网络请求又是异步的,那么就会出现数据更新了,网络请求没有及时发送。所以我们就需要用到watchEffect的清除副作用,用于当你频繁改变数据的时候,减少网络请求的次数。比如你连续触发更改了两次数据,那么他就会清除第一次的网络请求。
举例如下:
6.2 watch
watch时惰性的,只有当侦听的源发生改变时才会执行回调
跟watchEffect比较,watch允许我们:
惰性执行副作用(第一次不会直接执行)
更具体的说明当哪些状态发生改变时,触发侦听器的执行
访问侦听状态变化前后的值
6.2.1 watch侦听单个数据源
watch侦听函数的数据源有两种类型:
一个getter函数,但是getter函数必须引用可响应式的对象(比如reactive或者ref)
直接写入一个可响应式的对象,reactive或者ref(比较常用的时ref)
6.2.2 watch侦听多个数据源
6.2.3 侦听响应式对象
如果我们希望侦听一个数据或者对象,那么可以使用一个getter函数,并且对可响应式对象进行结构
6.2.4 watch的选项
如果我们希望进行一个深层的侦听,那么依然需要设置deep为true,也可以传入immediate立即执行。这里分为两种请情况
情况1:当你传入的是getter函数,切返回的是结构后的对象数据,那么此时是没有开启深度监听的,若想开启,则需要设置deep属性
情况2:不进行结构,直接传入reactive对象的时候,默认是开启深度监听的(区别于Vue2中的watch默认是不开启deep的),也就是我们改变info.friend.name的时候默认是可以监听
7.如何在setup中使用ref
我们只需要定义一个ref对象,绑定到元素或者组件的ref属性上即可
8. 生命周期钩子
如何再setup中使用生命周期?可以直接导入onX函数注册生命周期钩子,这里各个阶段的生命周期函数的作用与Vue2即Options API中的作用是一样的,这里不再继续展开讲述。但是有两个值得注意的点是
在setup中即Composition API中,一个组件中可以注册多个生命周期API,这个多次注册的生命周期在指定的阶段可以合并进行的。这样使得后面页面组件逻辑复杂或者有复用的时候,可以将这些抽取成独立的Hook,然后直接引入使用即可。
传入的是回调函数,组件到相应的阶段的时候回执行传入的回调函数里面的逻辑
Tip:在Vue3的setup中,不需要的跟Vue2中一样显示的定义beforeCreate和created生命周期钩子。如果有需要。这两个生命周期钩子中编写的任何代码都直接在setup函数中执行即可。
9. Provide与Inject函数
在Composition API也可以代替之前的provide和Inject的选项
可以通过provide方法来定义每个property,property可以传入两个参数
name:提供的属性名称
value:提供的属性值
在后代组件中,可以通过inject来注入需要的属性和对应的。其中,inject可以传入两个参数:
inject的property的name
默认值(可以不传)
以上的例子有点缺陷就是子组件也能修改父组件的数据,为了遵守单向数据流的规范,这里进行一步优化,即在父组件provide出去的数据以readonly进行包裹,以保证我们共享出去的数据补允许被子组件修改
10. Hooks
Vue官方中没有hook这么一说法,只是这一部分的作用与react中的hook相似,因此Vue社区中称之为hook;
解决的问题:在Vue2中的Options API中,页面中每个功能点的实现都是依赖data、methods、生命周期钩子的,那么功能点多的时候,则每个功能点的逻辑都是加在以上依赖中,比较散且不好定位。因为在Composition API中则出现了hooks,即是允许你对单个功能点所依赖的逻辑进行独立的抽取,抽取成hook的模式。然后在页面中直接引进这个hook函数即可。
例子1 实现计时器功能 useCounter
11. setup函数的顶层写法
上面我们所讲的所有属性已经例子中,我们的所有逻辑以及生命周期钩子都是写在 setup函数中的,然后在setup函数中返回模板中需要用到的数据已经method。想必我们也知道,每次都需要进行return多有的属性,这是非常麻烦的,因此Vue给出了更为简便的写法。
在script标签中定义setup之后与setup函数的写法还是有一些差异,如下: