Vue3


Vue3

本篇主要总结了vue3的知识与概念

一.常用Composition API (组合式API)

1.1拉开序幕的setup

1.是什么?是vue3中一个新的配置项,值为一个函数
2.理解:是所有组合式API"表演的舞台"
3.怎么使用? 组件中所用到的:数据,方法等等,均要配置在setup中
4.返回值:setup函数的两种返回值,一个是对象,第二个就是返回一个渲染函数
a.若返回一个对象,则对象中的属性,方法,在模板中均可使用(重点)
b.若返回的是一个渲染函数:则可以自定义渲染内容(了解)
5.注意点:
a. 尽量不要和vue2配置混用

  1. vue2配置(data,methods,computed...)中可以访问到setup中的属性与方法
  2. 但在setup中不能访问到vue2配置的(data,methods,computed...)
  3. 如果有重名,setup优先

b. setup不能是一个async函数,因为返回值不再是return的对象,
而是promise,模板看不到return对象中的属性。
注意:后期也可以使用返回一个Promise实例,但是需要Suspense和异步组件配合)

setup参数

参数:
props:父组件传过来的,值为对象,包含:组件外部传过来,且组件内部声明接收了的属性

context: 上下文对象

emit 可以触发父亲的自定义事件
attrs 非props属性组成的对象(捡漏)
slots 插槽信息组成的对象

说一下你对Vue3的了解

性能更高了
1.响应式原理换成了proxy
2.VNode Diff的算法进行了优化

体积更小了
1.删除了一些不常用的API,例如filter,EventBus...
2.所以API都是按需引入,能配合webpack等构建工具支持Tree Shaking

对TS支持更好了 源码就是使用TS写的

Compostition API(组合API)
1.解决了同一功能的数据和业务逻辑复用的问题
2.Vue2确实可以通过mixin进行复用,
但是mixin容易造成命名冲突和数据来源不清晰等问题

新特性
1.Fragment
2.Teleport
3.Suspense
……

1.2 ref函数

作用:定义一个响应式数据
语法:

//第一步:按需导入ref函数 
import {ref} from 'vue'
//第二步:在setup配置项中使用
const xxx = ref('666')

/*
创建了一个包含响应式数据的引用对象(reference对象,简称ref对象)
js中操作数据:xxx.value
模板中读取数据:不需要使用.value  直接:{{xxx}}
*/

备注:
1.接收的数据可以是:基本数据类型,也可以是对象类型
2.基本数据类型的数据:
响应式依然依靠Object.defineProperty()的getter与setter完成的
他只能读取,不能新增和删除
3.对象类型的数据:
内部使用了vue3中的一个新函数—reactive函数(内部封装es6新增的Proxy的操作)
4.ref包裹的内部数据,如果说是一个复杂数据类型,其实他还是一个reactive

1.3 reactive函数

1.作用:定义一个对象,数组类型的响应式数据(基本数据类型别用他,用ref函数)
2.语法:
const 代理对象名 = reactive(源对象),接收一个对象(或数组),
返回一个代理对象(Proxy的实例对象,简称proxy对象)

//第一步:按需导入ref函数 
import {reactive} from 'vue'
//第二步:在setup配置项中使用
const 对象名 = reactive({属性名:属性值})
//直接读取,别写.value了
console.log(对象名.属性名)

3.reactive定义的响应式数据是’深层次的’
4.内部是基于ES6Proxy实现,通过代理对象操作源对象内部数据都是响应式的
5.reactive包裹的内部数据,如果说是一个复杂数据类型,其实他还是一个reactive

1.4 Vue3中的响应式原理

vue2的响应式

实现原理:

对象类型:通过Object.defineProperty()对属性的读取,修改进行拦截(数据劫持)
数组类型:通过重写更新数组的一系列方法来实现拦截(对数组的变更方法进行了包裹)

let p = {}
Object.defineProperty(给谁添加属性,'新增属性的名字',{
                      //有人读取新增属性的时候调用
                      get(){
    return 修改后的值  //修改后的值会传给set的形参
},
    //有人修改新增属性时调用
    set(value){}
                      })
//get,set只能读取,无法捕获到新增和删除,因此在vue中,
//新增,删除操作没有响应式,这就是单向数据流为啥产生的原因
存在的问题:

1.新增属性,删除属性,界面不会更新
this.$set(添加谁,’属性名’,’属性值’) Vue.set(添加谁,’属性名’,’属性值’)
this.$delete(删除谁,’属性名’) Vue.delete(删除谁,’属性名’)

2.直接通过下标修改数组,界面不会自动更新

vue3的响应式

实现原理:

步骤1:使用Proxy代理对象完成:通过它去拦截对象中任意属性的变化,包括属性的读写,增删

const p = new Proxy(给谁绑定,{
    //3个方法get set deleteProperty
    //有人读取p的某个属性
    /*
    target   就是目标源   也就是给谁绑定的对象
    propName  就是增删改查的哪个属性
    value    就是你修改过后的值
    */
    get(target,propName) {
        return target[propName]
    },
    //有人修改了p的某个属性,或给p追加某个属性时调用
    set(target,propName,value){
        target[propName] = value
    },
    //有人删除了p的某个属性
    deleteProperty(target,propName){
        return delete target[propName]
    }
})

步骤2:使用Reflect反射对象完成:对源对象的属性进行操作
配合window的内置对象Reflect 反射 ES6新增的一个内置对象

除了原始的增删改查,还能使用Reflect内置对象

//查
Reflect.get(从哪个对象身上得到,'得到哪个属性')
//改  增
Reflect.set(从哪个对象身上改,'改哪个属性','改成什么')
Reflect.set(从哪个对象身上增,'增哪个属性','添加什么')
//删
Reflect.deleteProperty(从哪个对象身上上,'删哪个属性')

Proxy配合Reflect实现vue3的响应式

const p = new Proxy(给谁绑定,{
    //3个方法get set deleteProperty
    //有人读取p的某个属性
    /*
    target   就是目标源   也就是给谁绑定的对象
    propName  就是增删改查的哪个属性
    value    就是你修改过后的值
    */
    get(target,propName) {
        return Reflect.get(target,propName)
    },
    //有人修改了p的某个属性,或给p追加某个属性时调用
    set(target,propName,value){
        Reflect.set(target,propName,value)
    },
    //有人删除了p的某个属性
    deleteProperty(target,propName){
        return Reflect.deleteProperty(target,propName)
    }
})

1.5 reactive和ref

从定义数据角度对比:
1.ref用来定义:基本数据类型
2.reactive用来定义:对象和数组类型的数据
备注:ref也可以用来定义对象(或数组)类型数据,它内部会自动通过reactive转化为代理对象

从原理角度对比:
1.ref通过Object.defineProperty()的get和set实现响应式(数据劫持)
2.reactive是通过Proxy来实现响应式(数据劫持),并通过Reflect操作源对象内部的数据

从使用角度对比:
1.ref定义的数据:操作数据需要 .value ,读取数据时,模板中直接读取不需要 .value
2.reactive定义的数据:操作数据和读取数据,均不需要 .value

1.6计算属性与监视

计算属性

与vue2中computed配置功能一致

语法:

//vue3中一定要按需导入computed,才能使用计算属性
import {computed} from 'vue'

export default {
    name:"demo",
    setup() {
        let p = reactive({
            first = 'a',
            last = 'b'
        })
        //这里的full在页面上能直接使用,p.full
        person.full = computed(()=>{
            get() {
                return p.first + '-' + p.last
            },
            set(val){
                p.first = val.split('-')[0]
                p.last = val.split('-')[1]
            }
        })
        //搞完以后一定要返回出去
        return {
            p
        }
    }
}

watch函数

与vue2中watch功能一致

语法:

//vue3中一定要按需导入watch,才能使用计算属性
import {watch} from 'vue'

export default {
    name:"demo",
    setup() {
        let sum = ref(0)
        let msg = ref('你好')
        /*
        监视ref定义的一个响应式数据
         watch(你要监视谁,(newVal,oldVal)=>{
            console.log('sum改变了',newVal,oldVal)
        })
        
         watch(sum,(newVal,oldVal)=>{
            console.log('sum改变了',newVal,oldVal)
        })
        */
        
        /*
        监视ref定义的多个响应式数据
        此时newVal,oldVal返回的是一个数组
         watch([sum,msg],(newVal,oldVal)=>{
            console.log('sum改变了',newVal,oldVal)
        },{immediate:true,deep:true})
        */
        
        
        //搞完以后一定要返回出去
        return {
            sum,
            msg
        }
    }
}
1.1监视ref定义的一个响应式数据
watch(sum,(newVal,oldVal)=>{
       console.log('sum改变了',newVal,oldVal)
   })
1.2监视ref定义的多个响应式数据

此时newVal,oldVal返回的是一个数组

watch([sum,msg],(newVal,oldVal)=>{
            console.log('sum改变了',newVal,oldVal)
        },{immediate:true,deep:true})
2.监视reactive定义的数据

注意:
1.如果监视的是reactive定义的响应式数据时(整个对象,或者使用ref传递的数据是对象),
此处无法正确的获取oldValue,并且强制开启了深度监视(deep配置无效)

2.如果监视的是reactive定义的响应式数据中的某个属性,这个属性是基础数据类型,
但是需要把数据写成函数返回的代码,例如watch(()=>对象.属性,(newval,oldval)=>{})

3.如果监视的是reactive定义的响应式数据中的某些属性,这个属性是基础数据类型,
但是需要把数据写成函数返回的代码
例如:watch([()=>对象.属性1,()=>对象.属性2],(newval,oldval)=>{})

4.如果监视的是reactive定义的响应式数据中的某个属性的值依然是一个对象,
这个属性是复杂数据类型,且deep有效

总结:监听对象类型的数据是拿不到oldVal的

//vue3中一定要按需导入watch,才能使用计算属性
import {watch} from 'vue'

export default {
    name:"demo",
    setup() {
        let p1 = ref({
            name:666
        })
        let p = reactive({
            name:"张三",
            age:18
        })
         watch(p1.value,(newVal,oldVal)=>{
             console.log('p1改变了',newVal,oldVal)
        })
        watch(p,(newVal,oldVal)=>{
             console.log('p改变了',newVal,oldVal)
        })
        //搞完以后一定要返回出去
        return {
            p,
            p1
        }
    }
}
3.对于ref在watch中是否.value去监视

1.如果是ref定义的基本数据类型不要去.value
如果你去.value,那么它侦听的就不是你的属性了,而是你属性的value

2.如果是ref定义的复杂数据类型要去.value
因为你ref里面写对象,本质上还是要拿到他的reactive对象

3.如果是ref定义的复杂数据类型,你不想写.value
则只需要开启深度监视即可

watchEffect函数

官方解释:立即运行一个函数,同时响应式地追踪其依赖,并在依赖更改时重新执行。

watch的套路是:既要指明监视的属性,也要指明监视的回调

watchEffect的套路是:不管指明监视哪个属性,监视的回调中用到哪个属性,那就监视哪个属性

watchEffect有点像computed
但是computed注重的计算出来的值(回调函数的返回值),所以必须写返回值
watchEffect更注重过程(回调函数的函数体),所以不用写返回值

watchEffect官方文档

1.7 生命周期

Vue3中可以继续使用Vue2的生命钩子,但是有两个被更名:

beforeDestroy 改为 beforeUnmount
destroyed 改为 unmounted

Vue3.0也提供了组合式API形式的生命周期钩子,即写在setup中的钩子,与Vue2.0中钩子对应关系如下

Vue2.0 改变 Vue3.0
beforeCreate setup
created setup
beforeMount onBeforeMount
mounted onMounted
beforeUpdate onBeforeUpdate
updated onUpdated
beforeDestroy onBeforeUnmount
destroyed onUnmounted

语法:生命周期(()=>执行代码)

1.8 自定义hook函数

1.什么是hook?本质是一个函数,把setup函数中使用的组合API进行了封装

2.类似于vue2中的mixin

优势:复用代码,让setup中的逻辑更清除易懂

1.9 toRef

作用:创建一个ref对象,其value值指向另一个对象中的某个属性值

语法:const name = toRef(操作的对象,'属性')

应用:要将响应式对象中的某个属性单独提供给外部使用

扩展:toRefstoRef功能一致,但是可以批量创建多个ref对象,语法:toRefs(操作对象)

二.其他的组合API(了解)

1. shallowReactive和shallowRef

shallowReactive:只处理对象最外层属性的响应式(只考虑第一层)

shallowRef:只支持处理基本数据类型的响应式,不进行对象响应式处理

什么时候使用?

shallowReactive:如果有一个对象数据,结构比较深,但变化时只是外层属性变化

shallowRef:如果有一个对象数据,后续功能不会修改该对象中的属性,而是生成新的对象来替换

2. readonly和shallowReadonly

readonly:让一个响应式数据变为只读的(深只读)

shallowReadonly:让一个响应式数据变为只读的(浅只读)

应用场景:不希望数据被修改时

3. toRaw和markRaw

toRaw
作用:将一个由reactive生成的响应式对象转换为普通对象
使用场景:用于读取响应式对象对应的普通对象,对这个普通对象的所以操作,不会引起页面刷新

markRaw
作用:标记一个对象,使其永远不会再成为响应式
应用场景:
1.有些值不应该被设置为响应式的,例如第三方库等
2.当渲染具有不可变数据源的大列表时,跳过响应式转换可以提高性能

4. customRef

作用:创建一个自定义的ref,并对其依赖项跟踪和更新触发进行显式控制

看官网:customRef

track 通知vue追踪数据的变化
trigger 通知vue再次解析模板

5. provide/inject

作用:实现祖与后代通信

使用:
发:provide(‘叫啥’, 数据变量名)
收:inject(‘叫啥’)

两个叫啥必须一致,可以随便叫什么名字

孙传爷
思路:在爷爷中封装一个函数,并且将这个函数通过provide(‘叫啥’, 数据变量名)发送过去,
后代通过inject(‘叫啥’)接收,拿到函数后,找到需要时进行调用并传参

6.响应式数据的判断

isRef:检查一个值是否为ref对象
isReactive:检查一个对象是否由reactive创建的响应式代理
isReadonly:检查一个对象是否是由readonly创建的只读代理
isProxy:检查一个对象是否由reactive或者readonly方法创建的代理

三.组合API的优势

1.解决了同一功能的数据和业务逻辑复用的问题
2.Vue2确实可以通过mixin进行复用,但是mixin容易造成命名冲突和数据来源不清晰等问题

四.新的组件

Fragment组件

在vue2中:组件必须有一个根标签

在vue3中,组件可以没有根标签,内部会将多个标签包含着一个Fragment虚拟元素中

好处:减少标签层级,减少内存占用

Teleport组件

是什么?Teleport是一种能够将我们的组件html结构移动到指定的位置的技术

<template>
    <div class="child">
        <dialog v-if="bBar" />
        <button @click="handleDialog">显示弹框</button>
    </div>
</template>

<script>
    import { ref } from 'vue'
    import Dialog from './Dialog.vue'
    export default {
        name: 'Child',
        components: {
            Dialog,
        },
        setup() {
            const bBar = ref(false)
            const handleDialog = () => {
                bBar.value = !bBar.value
            }
            return {
                bBar,
                handleDialog,
            }
        },
    }
</script>

解决

<template>
    <div class="child">
        <teleport to="body">
            <dialog v-if="bBar" />
        </teleport>
        <button @click="handleDialog">显示弹框</button>
    </div>
</template>

Suspense组件(配合异步组件使用)

等待异步组件时渲染一些额外内容,让应用有更好的用户体验

Suspense组件文档

<Suspense>
    <异步组件名/>
  <!-- 在 #fallback 插槽中显示 “正在加载中” -->
  <template #fallback>
    Loading...
  </template>
</Suspense>

在上方存放异步组件,在#fallback中存放异步组件未加载出来时的页面,
当上方的异步组件未加载出来时,让他先加载下面的页面

五.其他

1.全局API的转移

vue2有许多全局的API和配置,例如注册全局组件,注册全局指令等

vue3中对这些API做出了调整:
即:Vue.xxx调整到应用实例(app)上

2.x 全局API(Vue) 3.x 实例API(app)
Vue.config.xxx app.config.xxx
Vue.config.ProductionTip 移除
Vue.component app.component
Vue.directive app.directive
Vue.mixin app.mixin
Vue.use app.use
Vue.prototype app.config.globalProperties

2.其他改变

1.data选项应该始终被声明为一个函数

2.过渡类名的更改

3.移除了keyCode作为v-on的修饰符,同时也不再支持config.keyCodes

4.移除v-on.native修饰符

5.移除过滤器filter


文章作者: 冷杨威
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 冷杨威 !
  目录
-->