从Vue2到Vue3,Vue3变了什么?

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来完成响应式数据的侦听

  1. watchEffect用于自动收集响应式数据的依赖

  1. 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

  1. 当副作用即将被重新执行或者侦听器被停止(组件被销毁阶段)时会执行该函数传入的回调函数

  1. 我们可以在传入的回调函数中,执行一些清除的操作

场景:在很多时候,我们会对数据进行侦听,当数据发生改变的时候,我们会侦听拿到这个最新的数据,然后发送网络请求,但是需要注意,网络请求是异步的,而当你频繁的改变侦听的属性的值的时候,那么就会频繁的进行网络请求,而网络请求又是异步的,那么就会出现数据更新了,网络请求没有及时发送。所以我们就需要用到watchEffect的清除副作用,用于当你频繁改变数据的时候,减少网络请求的次数。比如你连续触发更改了两次数据,那么他就会清除第一次的网络请求。

举例如下:

6.2 watch

watch时惰性的,只有当侦听的源发生改变时才会执行回调

跟watchEffect比较,watch允许我们:

  • 惰性执行副作用(第一次不会直接执行)

  • 更具体的说明当哪些状态发生改变时,触发侦听器的执行

  • 访问侦听状态变化前后的值

6.2.1 watch侦听单个数据源

watch侦听函数的数据源有两种类型:

  1. 一个getter函数,但是getter函数必须引用可响应式的对象(比如reactive或者ref)

  1. 直接写入一个可响应式的对象,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函数的写法还是有一些差异,如下:

基础写法的差异

引入组件的差异

props写法的差异

emit写法的差异

猜你喜欢

转载自blog.csdn.net/weixin_46872121/article/details/128578079