• 【Vue】Vue3的系统性学习


    【Vue】Vue3的系统性学习

    1、前言

    之前在做一个springboot前后端分离项目的时候,前端使用的是Vue3。并不是说我会Vue3或者Vue2,而是Vue这东西是一个渐进式的框架,所有用啥可以学啥,随便学学就实现了一个功能了。但是在使用Vue3写前端的时候,遇到了非常多的麻烦,比如根本不会typescript,也不会使用最新的setup,同时语法还是使用老旧的Vue2,这就导致了虽然看起来我的前端功能完成了,但实际上其中的代码是一坨shit山的情况。

    通过某个契机,准备进行Vue3的系统性学习。目前对于Vue的了解仅仅只有Vue2的一些语法格式,但是在本篇文章中,会进行Vue3的各种系统性学习。

    2、Vue3介绍

    由Vue2重构而来,使用MVVM架构编写。

    • View
    • ViewModel
    • Model

    系统的介绍就看看Vue的官方文档吧

    对比Vue2,Vue3有何改进?

    • 重写数据的双向绑定(Vue2使用Object.defineProperty(),Vue3使用Proxy
    • 优化了Vdom渲染
    • 允许有多个根节点
    • 智能导入,仅仅导入需要使用到的功能

    3、开发环境

    首先安装nodejs,前往官网下载,需求的版本 >= 12

    再构建vite项目

    为了快速访问npm中的资源,先安装一个cnpm或者给npm换源

    npm install -g cnpm
    
    • 1

    到工作文件夹中输入如下代码进行构建

    cnpm init vite@latest
    
    • 1

    选择构建vue项目

    image-20220708222508046

    我这里选择的是vue-ts版本

    image-20220708222640960

    构建成功如下:

    image-20220708222621537

    进入到创建好的init文件夹,进行install

    image-20220708222840027

    输入 cnpm run dev 进行启动项目

    image-20220708222920623

    进入本地,构建成功

    image-20220708222956403

    如果使用vscode进行开发,那么有很多的插件可以使用

    image-20220708223456041

    4、认识文件 & 文件夹

    • public文件夹用于存放静态资源,例如图片

    • src中是会被编译的源文件

      • assets虽然也是资源文件夹,但是其中的文件是会被编译的,例如图片可以编译成base64
      • components是用于存放公共组件,例如 页头 页尾
      • App.vue文件是应用于全局的vue文件
      • main.ts文件也是公共全局的ts文件
    • index.html是首页文件,比较重要

    • package.json是依赖管理配置

    • tsconfig.json是typescript的配置文件

    • vite.config.ts是vite的配置文件

    在一个vue文件中,由三部分组成template、script、style

    template在一个vue文件中只能有一个,scripte如果是setup模式,也只能有一个

    5、模板语法 & Vue指令

    在Vue3中,模板语法是非常快速进行数据解析的一种方式,例如我在script中得到的变量需要渲染到dom中,使用{{ }}的方式就可以套入

    
        
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    同理,不仅仅支持字符串,还可以支持script语法,例如判断、api调用、计算等等

    
        
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    接下来就是Vue常用的指令,v- 开头的,都是vue的指令

    • v-text(用来显示文本)
    • v-html(用来显示富文本)
    • v-if(判断)
    • v-else-if
    • v-else
    • v-show(用来控制元素的显示和隐藏)
    • v-on(简写是@,表示给元素添加事件绑定,例如@click
    • v-bind(简写是:,用来绑定元素的属性Attr)
    • v-model(表示数据的双向绑定)
    • v-for(遍历)

    6、Ref全家桶

    到这里就是Vue3的用处非常多的Ref的出现了,这里介绍其全家桶套餐,分别是:

    • ref
    • Ref
    • isRef
    • shallowRef
    • triggerRef
    • customRef

    分别由什么用呢?我们都来看一下:

    6.1 ref与Ref

    首先是refRef,ref是一个方法,而Ref是一个类型

    ref,其用法就是将一个数据进行双向绑定,可以通过交互达到改变数据的作用

    
        
    
        
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    在如上的例子中,使用ref绑定一个msg的变量(数据类型是Ref类,同时绑定泛型为string),在绑定完之后,给一个button绑定一个点击方法,点击之后就会将msg.value进行改变,注意需要使用.value的属性对其数值进行改变

    看效果:

    chrome-202207101810523

    6.2 isRef

    从名字就看得出来,是一个判断方法,其实就是判断一个数据类型是否是Ref

    
        
    
        
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    运行后会在浏览器控制行log出true

    image-20220710142438181

    6.3 shallowRef

    从名字看出来,shallo就是浅显的意思,也就是说,使用shalloRef是不会对深层属性进行双向绑定的

    举个例子

    
        
    
        
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    我们点击按钮时不会改变msg.value.woodwhale

    chrome-202207103126865

    shalloRef只能对其value属性进行相应,所以上述代码可以改为

    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    修改后运行效果如下:

    chrome-202207103402832

    6.4 triggerRef

    从名字也可以看出,叫做触发,那么到底时什么意思呢?其实算一种强制刷新ref的绑定

    例如在上述的shallowRef中,我们无法对msg.value.woodwhale这种深层属性进行修改,如果想要修改成功,就需要使用triggerRef来调用强制刷新

    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    这样的也能达到实现修改shalloRef深层属性的效果

    6.5 customRef

    customRef是一个可以自定义相应式的ref,用法如下。如下的写法其实就是ref的原理

    <script setup lang='ts'>
    import { customRef } from "vue"
    
    function MyRef<T>(value:T) {
       return customRef((trank,trigger) => {
          return {
             get() {
                trank()  // 跟踪获取数据
                return value
             },
             set(newVal:T) {
                value = newVal
                trigger()   // 更新,刷新新数据
             }
          }
       })
    }
    
    let msg = MyRef<string>("woodwhale")
    const changeMsg = () => {
       msg.value = "sheepbotany"
    }
    </script>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    7、Reactive全家桶

    7.1 reactive

    最基本的reactiveref的性质类似,但是一般使用reactive来修饰非基本类型,例如:数组、类与对象

    ref一般是用来进行修饰基本数据类型的,当然,非基本数据类型也可以修饰,但是其背后的底层逻辑还是会判断是否是基本数据类型,如果非基本数据类型,其会调用toReactive的方法,将其转换为Reactive类型

    看一个基本的使用例子:

    
        
    
        
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    注意这里如果要给msg进行增删改,需要使用函数类型的增删改实现,例如push方法等,不能给其重新赋值

    chrome-202207130116193

    7.2 shallowReactive

    shallowRef类似,是浅层的数据绑定

    在页面渲染完成之后,只能对浅层的数据进行修改

    
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    上述代码就无法在web端实时渲染,虽然值确实是改变了,但是web端的显示渲染没有进行改变

    chrome-202207135421118

    但是如果在改变浅层数据的时候,一并改变深层数据,两者都会进行web端的渲染更新

    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    chrome-202207135151618

    7.4 readonly

    调用readonly方法进行一次数据的拷贝,copy的数据是无法进行修改的,只能读

    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    8、to全家桶

    分别对如下几种to的方法进行举例讲解

    8.1 toRef

    使用toRef可以将数据类型转为Ref类型

    
        
    
        
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30

    8.2 toRefs

    将一个数据中的多个属性转为ref

    
        
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    8.3 toRaw

    toRaw就是将相应式的数据转为原始的数据类型

    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    image-20220713233014245

    9、computed计算属性

    所谓computed就是计算属性,也就是当依赖的属性发生变化的时候,才会触发其变更。如果依赖的值不发生改变,那么使用的就是缓存中的属性值。

    
        
    
        
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    我们尝试进行first和second的修改:

    chrome-202207150101634

    发现name也是一个相应式,只不过进行了ref的组合计算

    当然computed的使用方法还可以是对象的方式,使用getset方法:

    
        
    
        
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33

    chrome-202207151148039

    10、watch监听器

    watch是vue3中的一个方法,可以监听数据的改变

    10.1 ref浅监听

    watch默认是浅监听,也就是如果一个ref对象有深层属性,是无法通过浅层监听而监听到的

    
        
    
        
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    chrome-202207152817991

    我们可以观察到,每次改变msg的值,都会调用watch中的方法

    10.2 ref深监听

    watch方法的第三个参数中设置deep:true就可以进行深层监听,这样就可以监听ref对象中的深层属性

    
        
    
        
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33

    chrome-202207151910127

    使用深层监听有一个缺点,那就是newValoldVal是一样的

    10.3 默认执行

    watch方法放在setup中是默认第一次不会执行的,也就是说,如果不改变监听的对象的属性,那么watch中的方法是不会进行调用的。

    如果想让第一次执行就进行调用,那就需要使用到immediate:true

    
        
    
        
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40

    这里的watch中的方法就是页面已加载就进行调用了

    image-20220715113528073

    10.4 reactive监听

    对于reactivewatch监听,无论是否加入deep:true,它都是深层监听,因为reactive定义的对象本来就是面向属性层次的。

    加入一个reactive对象中有多个属性,但是我们仅仅想监听其中的某一个属性,可以将watch的第一个参数写成函数的形式,然后返回reactive对象的属性

    
        
    
        
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31

    chrome-202207154532000

    这里我只对name属性进行了监听,而没有对name2进行监听

    11、watchEffect监听器

    watchEffect就是可以监听多个属性改变的监听器,其中存在的属性都会被监听

    11.1 基本方法

    
        
    
        
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29

    11.2 预处理

    如果我们需要在监听之前进行预处理呢?在匿名方法中传入一个参数就行了

    
        
    
        
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34

    11.3 关闭监听

    如果我们想要关闭watchEffect的监听呢,也很简单调用stop()方法就可以了

    
        
    
        
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39

    12、Vue3生命周期

    在介绍了上述这么多的Vue3的语法糖,可以引入vue的声明周期函数来进行讲解了。

    vue3中有一个setup的状态,对应vue2中的beforeCreatecreated

    这里先介绍六种生命周期函数:

    • onBeforeMount
    • onMounted
    • onBeforeUpdate
    • onUpdated
    • onBeforeUnmount
    • onUnmounted
    
        
    
        
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42

    一张图看如上的生命周期

    chrome-202207151613808

    这里的卸载按钮写在App.vue中

    
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    13、父子组件传递参数

    首先先编写一个父子组件

    image-20220716124749232
    BaseView中引入ContentHeaderMenu三块内容,其代码如下

    
       
    
       
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    13.1 父传子

    将父组件的内容传递给子组件也很简单,例如在BaseViewMenu传递

    
    
    • 1

    其中menuStr是普通类型的字符串,menuList是数组类型,需要使用v-bind,简写成:的形式

    我们在Menu中进行接收父组件传递的两个参数

    
        
    
        
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    setupts的情况下,使用defineProps接收传入的参数,应以一个type类型的props,其中存放的就是存入参数的申明

    显示效果如下:

    image-20220716125310459

    当然,如果我们在子组件中想定义可有可无的参数,如何实现呢?

    其实只要在type中加一个?表示可以省略就行了

    type props = {
        menuStr?: string,
        menuList?: number[]
    }
    
    • 1
    • 2
    • 3
    • 4

    但是这样就没有默认值了,如果需要默认值,在ts的环境下,使用withDefaults方法就可以了

    子组件Menu的代码

    
        
    
        
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    父组件BaseView的代码

    
       
    
       
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    image-20220716140223721

    13.2 子传父(事件)

    使用defineEmits方法可以将子组件的事件传递给父组件

    子组件Menu代码

    
        
    
        
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33

    可以看到暴露了一个my-click的事件

    在父组件中BaseView的代码

    
       
    
       
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29

    Menu标签里写入@my-click定义好的事件,就可以进行触发,而在之前是传入了list的Ref对象,那么在父组件中就可以接收这个对象

    chrome-202207160249660

    13.3 子传父(属性)

    通过defineExpose方法可以将自己这个组件的属性给暴露出来

    我们先在Menu子组件中暴露一些属性出来

    
        
    
        
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    然后在父组件BaseView中读取

    
       
    
       
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36

    注意这里需要给Menu标签注入一个ref,即**

    **

    然后再ts中也要定义一个相对应的menuRef

    14、动态组件

    在vue3中,使用标签来完成动态组件的使用

    
       
    
       
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    这里使用component的作用和直接使用

    的效果是一样的,但是动态组件之所以叫动态组件,是因为可以改变is的属性,从而做到切换组件的作用

    15、插槽

    使用v-slot或者简写成#来使用

    现在Menu中申明两个插槽,第一个有name,第二个没有

    
        
    
        
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    然后再BaseView中的组件中插入插槽

    
       
    
       
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34

    注意,默认插槽使用#default,有名字的插槽例如这里的#top。效果如下:

    image-20220716184040290

    如果我想让申明插槽的子组件将一些属性传递给父组件,如何完成?使用v-bind即可

    子组件代码,定义了:data="item"

    
        
    
        
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37

    父组件接收,使用#top="{ data }"接收data

    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    image-20220716184839772

    如果我们想使用动态插槽呢?使用变量即可

    
       
    
        
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    现在问题就是,如何引入这样的一个异步组件?

    我们回到Menu组件中,通过defineAsyncComponent() + import()的方法引入异步组件

    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    引入完了还需要进行渲染,需要使用到标签,其中有两个插槽

    • default(接收到异步消息后渲染)
    • fallback(请求消息中的loading)

    所以Menu的代码为

    
        
    
        
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    最终的效果如下:

    chrome-202207164043067

    17、Teleport

    Teleport是vue3的新特性,叫做传送组件

    它的功能就是为了防止样式的冲突的情况下可以进行组件引入

    我们随意在一个组件中进行插入,使用Teleport插入不需要担心css渲染问题

    
        
    
        
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    使用to="body",这样其中的内容就被插入到了body中

    image-20220716194457303

    18、keep-active

    keep-active组件是为了保存缓存而设置的

    举个例子,假设我现在有loginregister两个组件,需要通过点击一个button进行组件的切换

    如果我们使用动态组件的形式,那么每一次输入的数据会被重新渲染(消失),所以为了保存缓存,需要使用keep-active

    首先是login的代码

    
        
    
        
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31

    然后是register的代码

    
        
    
        
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36

    最后在Menu组件上放置两个子组件

    
        
    
        
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    效果如下:

    chrome-202207162912084

    可以看到两个组件的状态被保存了

    当然,keep-active是有参数的,比如include、exclude,就是包含或者不包含某个组件

    例如我这里只需要保存Login组件的状态

    首先需要去Login组件中申明一个name属性

    
    
    • 1
    • 2
    • 3
    • 4
    • 5

    然后在Menu中的keep-active设置include = "Login"

    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    当然exclude同理,这里就不演示了。

    19、依赖注入

    依赖注入这里值的是provideinject

    在深度嵌套的关系下,如果仅仅使用父子组件传参,是非常麻烦的。

    这里引入的依赖注入技术就是为了解决这样的问题而实现的。

    在父组件中提供provide就可以在任何子组件中使用inject获取

    举个例子,首先在App.vue中引入A这个子组件

    
    
    
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    在A中引入B

    
        
    
        
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    最后可以在B中通过inject获取root中的str

    
        
    
        
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    页面效果如下:

    image-20220719190436993

    但是我们上述提供的是非相应式的str

    如果需要使用相应式,就是用ref或者reactive

    两种方法:

    • 强转
    • 兜底
    // 父组件
    provide("str",ref("这是root中注入的字符串"))
    // 1.子组件强转
    let str = inject("str") as Ref
    str.value = "114514"
    // 2.兜底逻辑
    let str = inject("str",ref(""))
    str.value = "1919810"
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    20、v-model

    在Vue3中v-model是一个破坏性更新(相对于Vue2)

    改变如下:

    • prop --> modelValue
    • event --> update:modelVale
    • 支持多个v-model
    • 支持自定义修饰符

    因为v-model是双向绑定的,所以可以在子组件中更改父组件的值

    父组件代码中,给子组件A一个v-model="flag"

    
    
    
    
    
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    子组件A代码,使用defineProps接收modelValue默认绑定值,同时通过defineEmits派发事件update:modelValue,将modelValue改为false

    
        
    
        
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

    效果如下,可以发现是父子之间双向绑定

    chrome-202207201348265

    还可以使用自定义的v-model

    // 父组件
    
    // 父组件 setup ts
    let flag = ref(true)
    let woodwhale = ref("sheepbotany")
    
    // 子组件 setup ts
    type props = {
       modelValue: boolean 
       woodwhale: string
     
    }
    defineProps()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    21、全局变量

    在Vue3中,使用app.config.globalProperties来定义全局的变量和方法。

    因为Vue3删除了Vue2中的filters,所以我们可以在全局属性中自己写一个filters

    import { createApp } from 'vue'
    import App from './App.vue'
    import ElementPlus from 'element-plus'
    import 'element-plus/dist/index.css'
    import "./assets/reset.less"
    
    const app = createApp(App)
    
    
    type Filter = {
        format: <T>(str: T) => string
    }
    
    declare module "@vue/runtime-core" {
        export interface ComponentCustomProperties {
            $filters: Filter,
            $val: string
        }
    }
    
    // 全局变量
    app.config.globalProperties.$val = "114514"
    
    // 过滤器
    app.config.globalProperties.$filters = {
        format<T>(str: T): string {
            return "formate之后的str --> " + str
        }
    }
    
    app.use(ElementPlus)
    app.mount('#app')
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32

    因为我这里运行的是ts版本的vue,所以为了不让编译器报错,需要使用declare进行申明

  • 相关阅读:
    【HTML特效程序】① 给女神表白的程序(让女神看科技烟花),输入名字自动生成表白二维码
    Docker安装Nginx较佳实践
    Web渗透之域名(子域名)收集方法
    salesforce零基础学习(一百一十八)Restrict Rule
    C# WPF入门学习主线篇(七)—— Label常见属性和事件
    浪潮java面经总结
    设计模式--观察者模式
    [操作系统]1.概述
    spring boot日常开发中的小技巧
    【JDK 8-函数式编程】4.4 Supplier
  • 原文地址:https://blog.csdn.net/woodwhale/article/details/125899530