【VUE】Vue3.x学习汇总分享

相对于Vue2.x Vue3.给我们带来了一下优化

性能提升
源码升级
TypeScript
新的特性

Composition API(组合API)

1.在Vue2中使用Options API(配置项API),使用这种方法对数据、方法等进行配置会存在一定的问题
2.于是在Vue3.0中,提出了一个新的概念是Composition API,即组合API,将所有的数据、方法等放入setup中,即组合API的入口函数
3.配置项API存在的问题:当开发代码数据过长时,不方便查找某个功能对应的数据、方法或其他生命周期钩子所在的位置,可能需要通篇阅览
4.组合API的优势:可以将每个功能需要的数据、方法等内容放在一块,或者使用hook函数,在setup中使用即可,需要修改时,直接找到对应的hook函数修改即可

setup介绍

setup 有两种表现方式
起初 Vue3.0 暴露变量必须 return 出来,vue3.2只需在script标签中添加setup语法糖

直接在script标签中使用setup函数,需要return

<script lang="ts">
   import { ref } from "vue";
   export default {
      name: 'home',
       setup() {
         const info = ref('data') 
         const onChange = () => { info.value = 'datas' }
         // 需要return
         return { info, onChange};
       },
   };
</script>

直接使用setup语法糖(主要介绍语法糖)

<script setup lang="ts">
   import { ref } from "vue";
   const info = ref('data') 
   const onChange = () => { info.value = 'datas' }
</script>

Vue2&Vue3响应式介绍

Vue2的响应式是基于Object.defineProperty实现的

Vue3的响应式是基于ES6的Proxy+Reflect来实现的

Vue2中存在的问题

  • 需要对每个属性进行遍历监听,如果嵌套对象,需要深层监听,造成性能问题
  • 对象的新增属性、删除属性、界面不会更新
  • vue2的解决方法:通过vm. s e t ( o b j , k e y . v a l ) / v m . set(obj,key.val)/vm. set(obj,key.val)/vm.delete(obj,key)新增属性/删除属性
  • 直接通过下标修改数组,界面不会自动更新
  • vue2的解决方法:vm.$set(arr,index.value),调用数组的splice方法

语法

<template>
   <div>
        <p>姓名:{{obj.name}}</p>
        <p>年龄:{{obj.age}}</p>
        <p>性别:{{obj.sex}}</p>
        <el-button @click="addSex">添加性别sex</el-button>
      	<el-button @click="delAge">删除年龄age</el-button>
    </div>
</template>
<script>
export default {
	data() { return { obj:{ name:'weihua', age:20 } } },
     methods: {
        addSex(){
      	  // 控制台打印可以看到数据变了,但是页面没有更新      
          this.obj.sex = '女'
          this.$set(this.obj, 'sex', '女')
          console.log("添加性别sex",this.obj);
        },
        delAge(){
          // 控制台打印可以看到数据变了,但是页面没有更新
          delete this.obj.age
         // 只有使用  $delete   $set 才可以触发
          this.$delete(this.obj, 'age')
          console.log("删除年龄age",this.obj);
        }
      },
}
</script>

控制台打印
图片

图片

1.可发现对象的sex属性被添加了,age属性被删除了,但是数据未被响应,界面没有更新
2.使用 s e t 、 set、 setdelete 可以看出数据的变化会影响到界面的更新

vue3中的优化

  • vue2是监听对象的属性,vue3是监听对象 不需要一次性遍历data的属性,可以显著提高性能。需要的时候再进行深度监听
  • 基于ES6 Proxy,对被代理对象设置拦截,访问或操作被代理对象都要先通过拦截。所以可以监听对象属性的添加和删除
  • 可以原生监听数组,不需要通过重写方法来实现对数组的监控

Vue3响应式实现

Vue3中实现响应式数据的方法是使用ref和reactive,所谓响应式就是界面和数据同步,能实现实时更新

  • ref和reactive都属于递归监听,也就是数据的每一层都是响应式的,如果数据量比较大,非常消耗性能
  • shallowRef和shallowReactive 都属于非递归监听只会监听数据的第一层

ref

  • ref标准是定义一些 基本数据类型(可接收基本数据类型和对象)
  • 本质上,可以理解为ref对reactive的二次包装,ref定义的数据访问的时候要多一个.value;
    语法
<template>
    <div>
        <!-- 输入框双向绑定值 -->
        <input v-model="info" type="text">
        {{ info }}
        <!-- 点击按钮替换值 -->
        <button @click="clickValue">我是按钮替换值'zzzz'</button>
    </div>

  </template>
  <script setup lang="ts">
      // 按需导入API
	import { ref } from 'vue'
    // 定义初始化赋值
	let info = ref('name')
    // 定义函数修改info值
    const clickValue = () => {
        info.value = 'zzzz'
        console.log(info); // 打印 name
    }
    console.log(info); // 打印 zzzz
  </script>

控制台打印
图片

1.__v_isRef:true 判断是不是ref 可以通过内置的方法isRef()来判断的
2.在script中使用/修改需要通过info.value方式,模板中使用{{ info }}方式

reactive

  • reactive的参数必须是一个对象,包括json数据和数组都可以,否则不具有响应式
  • reactive内部是通过Proxy(代理): 拦截对象中任意属性的变化, 包括:属性值的读写、属性的添加、属性的删除等
    语法
<template>
      <div>
          <!-- 输入框双向绑定值 -->
          <input v-model="info.name" type="text">
          {{ info }} {{ info.name }} {{ info.age }}
          <!-- 点击按钮替换值 -->
          <button @click="clickValue">我是按钮替换值'zzzz'</button>
      </div>
  
    </template>
    <script setup lang="ts">
        // 按需导入API
  	import { reactive } from 'vue'
      // 定义初始化赋值
  	let info = reactive({ name: 'weihua', age: '21' })
      // 定义函数修改info值
      const clickValue = () => {
          info.name = 'zzzz'
          console.log(info); // 打印 weihua
      }
      console.log(info); // 打印 zzzz
    </script>

控制台打印
图片

ref和reactive的对比
图片

其他新特性

shallowReactive 与 shallowRef

shallowReactive()

shallowReactive:只处理对象最外层属性的响应式(浅响应式)

shallowRef()

shallowRef定义的数据,只有第一层是响应式的,即只有在更改.value的时候才能实现响应式

  • 什么时候使用?
  • shallowReactive:如果有一个对象数据,结构比较深, 但变化时只是外层属性变化
  • shallowRef:如果有一个对象数据,后续功能不会修改该对象中的属性,而是生新的对象来替换

语法(shallowReactive)

<template>
    <div>
      <h1>姓名:{{info.name}}</h1>
      <h2>年龄:{{info.age}}</h2>
      <h3>备注信息:{{info.son.item.msg}}</h3>
      <!-- 按钮操作区域 -->
      <button @click="changeName">修改姓名</button>
      <button @click="changeAge">修改年龄</button>
      <button @click="changeMsg">修改备注信息</button>
    </div>
  </template>
  <script setup lang="ts">
    // 按需导入API
	  import { shallowReactive } from 'vue'
    // 定义了一段数据
    // 只将第一层数据做了响应式处理 
    let info = shallowReactive({
      name: 'weihua',
      age: 18,
      son: {
        item:{
          msg: '我是msg值信息'
        }
      }
    })
    const changeName = () => { info.name = info.name += '~'}
    const changeAge = () => { info.age++}
    // 因为msg在该对象的最深层,数据变了,但是vue监测不到 
    // 无法触发页面的更新
    const changeMsg = () => { info.son.item.msg = '修改msg值' }
  </script>

控制台打印
语法(shallowRef

<template>
    <div>
      <h1>年纪:{{ info.age }}</h1>
    <button @click="clickSum">点击+</button>
    </div>
  </template>
  <script setup lang="ts">
    // 按需导入API
	  import { shallowRef } from 'vue'
    // info将是一个普通的对象,不具有响应式 
    let info = shallowRef({
      name: 'weihua',
      age: 18,
    })
	// 该操作无法修改age数据,且不触发页面的更新 因为vue监测不到了     
    const clickSum = () => {
      info.age++
    }
  </script>

控制台打印
图片

readonly 与 shallowReadonly

  • readonly: 让一个响应式数据变为只读的(深只读)
  • shallowReadonly:让一个响应式数据变为只读的(浅只读)
  • 应用场景: 不希望数据被修改时
    • 如果使用readonly包裹的person对象的所有属性数据都只读不可需改,即name、age、msg都只读无法修改
    • 如果使用shallowReadonly包裹的person对象的所有属性数据,其中浅层次的设为只读,即name、age只读无法修改,但是msg可读可修改,因为msg是深层次的,不受管控
      语法(readonly)
<template>
  <div>
      <h2>姓名:{{info.name }}</h2>
      <h2>年龄:{{info.age }}</h2>
      <h2>备注信息:{{info.son.item.msg }}</h2>
      <!-- 按钮操作区域 -->
      <button @click="changeName">修改姓名</button>
      <button @click="changeAge">修改年龄</button>
      <button @click="changeMsg">修改备注信息</button>
      <h2>sum:{{sum}}</h2>
      <button @click="clickSum">sum++</button>
  </div>
</template>
<script lang="ts" setup>
    import { ref, reactive, readonly } from 'vue'
    // 定义响应式
    let sum = ref(0)
    // 定义响应式对象
    let info = reactive(
      {
        name:'weihua', age:18,
        son:{ item: { msg: '我是msg值信息' } }
      }
    )
    // readonly 将info、sum响应式数据变为只读的(深只读) 点击任何都不能操作,点击报:warn
    info = readonly(info)
    sum = readonly(sum)
    const changeName = () => { info.name = info.name += '~'}
    const changeAge = () => { info.age++}
    const changeMsg = () => { info.son.item.msg = '修改msg值' }
    const clickSum = () => { sum.value++ }
</script>

控制台打印
在这里插入图片描述
语法(shallowReadonly)

<template>
  <div>
      <h2>姓名:{{info.name }}</h2>
      <h2>年龄:{{info.age }}</h2>
      <h2>备注信息:{{info.son.item.msg }}</h2>
      <!-- 按钮操作区域 -->
      <button @click="changeName">修改姓名</button>
      <button @click="changeAge">修改年龄</button>
      <button @click="changeMsg">修改备注信息</button>
      <h2>sum:{{sum}}</h2>
      <button @click="clickSum">sum++</button>
  </div>
</template>
<script lang="ts" setup>
    import { ref, reactive, shallowReadonly } from 'vue'
    // 定义响应式
    let sum = ref(0)
    // 定义响应式对象
    let info = reactive(
      {
        name:'weihua', age:18,
        son:{ item: { msg: '我是msg值信息' } }
      }
    )
    // shallowReadonly 将info、sum响应式数据变为只读的(浅只读) 点击changeName、changeAge、clickSum都不能操作,点击报:warn
    info = shallowReadonly(info)
    sum = shallowReadonly(sum)
    const changeName = () => { info.name = info.name += '~'}
    const changeAge = () => { info.age++}
    // 因为msg在该对象的最深层无法监控所以可以改变msg值
    const changeMsg = () => { info.son.item.msg = '修改msg值' }
    const clickSum = () => { sum.value++ }
</script>

控制台打印
在这里插入图片描述
toRawmarkRaw

  • toRaw
    • 作用:将一个由reactive生成的响应式对象转为普通对象
    • 应用场景:用于读取响应式对象对应的普通对象,对这个普通对象的所有操作,不会引起页面更新
  • markRaw
    • 作用:标记一个对象,使其永远不会再成为响应式对象(即便是再将转成响应式也是无效的)
    • 应用场景:有些值不应被设置为响应式的,例如复杂的第三方类库等,当渲染具有不可变数据源的大列表时,跳过响应式转换可以提高性能
  • 语法(toRaw)
<template>
 <div>
   <h1>姓名:{{ info.name }}</h1>
   <h2>年龄:{{ info.age }}</h2>
   <h3>备注信息:{{ info.son.item.msg }}</h3>
   <!-- 按钮操作区域 -->
   <button @click="changeName">修改姓名</button>
   <button @click="changeAge">修改年龄</button>
   <button @click="changeMsg">修改备注信息</button>
 </div>
</template>
<script lang="ts" setup>
   import { reactive, toRaw  } from 'vue'
    // 定义响应式数据
    let info = reactive(
     {
         name: "weihua", age: 18,
         son: {
           item: { msg: "我是msg值信息"},
         },
       }
    )
   // 将响应对象变为普通对象, 点击事件info值发生了改变但是页面将不会去渲染页面,因为转为了普通对象
   info = toRaw(info) 
   const changeName = () => { info.name = info.name += '~'}
   const changeAge = () => { info.age++}
   const changeMsg = () => { 
     info.son.item.msg = '修改msg值' 
     console.log(info)
   }
</script>

控制台打印
在这里插入图片描述
toRef

  • 为源响应式对象上的某个属性创建一个ref对象,二者内部操作的是同一个数据值,更新时二者是同步的
  • 区别ref:拷贝了一份新的数据单独操作,更新时相互不影响
  • 应用:将某个prop的ref传递给符合函数时,toRef很有用
  • toRef之后,两者的数据是互通的,一个数据改变,另一个数据也变,可以理解为同一个地址,但是ref就是不同的地址,数据不是互通的!
  • 应用场景:有时候,我们的函数入口是ref类型,需要用到这个来进行转换
  • 语法
// 在这个代码中{{}}中读取对象中的属性显得很麻烦,对象.属性名这种方式,所以出现了toRef
<template>
  <div>
      <h3>state:{{state}}</h3>
      <h3>state.age:{{state.age}}</h3>
      <h3>age:{{age}}</h3>
      <h3>money:{{money}}</h3>
      <hr>
      <button @click="update">更新数据</button>
  </div>
</template>
<script lang="ts" setup>
	import { reactive, toRaw, toRef, ref  } from 'vue'
    // 定义响应式数据
    const state = reactive({
      age:5,
      money:100
    })
    // 把响应式数据state对象中的某个属性age变成了ref对象了
    const age = toRef(state,'age')
    console.log(age, 'ageageage');
    // ref包装把state.money变成了一个新的ref对象
    const money = ref(state.money)
    const update = ()=>{
      console.log('test');
      state.age+=2
      age.value+=3
      // 不会相互影响
      money.value+=10
    }
</script>

控制台打印
在这里插入图片描述

customRef

  • 作用:创建一个自定义的 ref,并对其依赖项跟踪和更新触发进行显式控制
  • 应用场景:实现防抖效果(修改数据后1s中再触发页面重新解析模板)
  • 语法
实现防抖效果:**get:用于读数据进行调用    set:用于修改数据时进行调用** 

<template>
  <input type="text" v-model="keyWord"/>
  <h3>{{keyWord}}</h3>
</template>

<script setup lang="ts">
import {customRef} from 'vue'
 // 自定义myRef
  function myRef(value: any, delay: number) {
      let timer: any = null
      return customRef((track,trigger) => { 
          return {
              // 有人读取该属性时调用
              get() {
                  // 追踪set修改value的变化
                  track()
                  return value
              },
              // 有人修改该属性时调用
              set(newValue) {
                  clearTimeout(timer)
                  timer = setTimeout(() => {
                      value = newValue
                      // 通知Vue去重新解析模板(重新调用get)
                      trigger()
                  }, delay)
              }
          }
      })
  }

let keyWord = myRef("hello",1000)

</script>

控制台打印
在这里插入图片描述

计算属性&监听器

computed

  • 用法(与Vue2一致),写法有两种
  • 支持缓存,只有依赖的数据发生改变才真的重新计算
  • 支持 get set存储器方法
  • 语法
<template>
    <div>name: <input type="text" v-model="info.name" /></div>
    <div>age: <input type="text" v-model="info.age" /></div>
    <div>改变后的值 {{ computedAge }}</div>
    <div>改变后的值 {{ computedName }}</div>
</template>
  
  <script setup lang="ts">
    import { computed, ref, reactive } from 'vue'
    const info = reactive({
      name: 'weihua',
      age: '21'
    })

    // 简单用法 回调函数写法
    let computedAge = computed(() => {
      return 'name:' +  info.name + '-----' + 'age:'+ info.age
    })

    // 完整用法 参数为options对象(get和set)使用get和set创建可写的ref对象
    // 当name的值发生改变时,走get函数函数,那么影响展示数据的变化
    // 当展示数据的值在事件中直接改变,那么会走set方法,
    // set函数的形参就是你赋给他的值,set方法可以去修改name的值
    let computedName = computed({
        // 获取computedName 触发的函数
      get() {
        return info.name + '我在get中增加信息'
      },
      // 设置computedName 所触发的函数 value 修改的值
      set(value: string) {
        return info.name = value
      }
    })
  </script>

控制台打印
在这里插入图片描述
注:Vue3中移除了filter过滤器,可以使用computed代替过滤器,也可自己封装方法使用

const textSigh = computed(() => {
  // value是计算属性执行后,再次执行return里面的函数时传的参数
    return (value:any) => {
    	state.message =  value + '解决vue3不支持过滤器filter的问题 '
    	return state.message
    }
})

watch

  • 既要指明监视的属性,也要指明监视的回调

  • 数据监听,就是监听的某个数据发生了变化后,那么执行相对应的函数,数据不缓存

  • 同步异步场景都可以使用,性能开销很大的场景也适合

  • 场景:一个数据影响多个数据,主要做业务处理

  • 不是必须return

  • watch() 默认是懒侦听的,即仅在侦听源发生变化时才执行回调函数

  • 第一个参数:侦听源,侦听源可以是一下几种

    • 一个函数,返回一个值
    • 一个 ref
    • 一个响应式对象(reactive)
    • 或是由以上类型的值组成的数组
  • 第二个参数:侦听源发生变化时要触发的回调函数

    • (newValue, oldValue) => { /* code */}
    • 当侦听多个来源时,回调函数接受两个数组,分别对应源数组中的新值和旧值
    • ( [ newValue1, newValue2 ] , [ oldValue1 , oldValue2 ]) => {/* code */}
  • 第三个参数:可选对象,可以支持一下这些选项

    • immediate:侦听器创建时立即触发回调
    • deep:如果源是一个对象,会强制深度遍历,以便在深层级发生变化时触发回调函数
    • flush:调整回调函数的刷新时机
    • onTrack / onTrigger:调试侦听器的依赖
  • 语法

    监听ref类型

<template>
    <input type="text" v-model="refDc">
   <div>监听ref:{{refDc}}</div>
</template>
  
<script lang="ts" setup>
    import { ref, watch } from "vue";
    const refDc = ref('swh')
    // 监视ref定义的响应式数据
    // immediate:true 配置项,代表首次执行监听一次
    watch(refDc ,(newValue,oldValue)=>{
        console.log('refDc-newValue:',newValue)
        console.log('refDc-oldValue:',oldValue)
    },{immediate:true})
</scri

控制台打印
在这里插入图片描述
监听ref类型(监听多个ref)

<template>
    name: <input type="text" v-model="refDc">
    age:<input type="text" v-model="refDcAge">
   <div>监听多个ref:{{refDc}}</div>
</template>
  
<script lang="ts" setup>
    import { ref, watch } from "vue";
    const refDc = ref('swh')
    const refDcAge = ref('21')

    // 监视多个ref定义的响应式数据,用数组
    // newValue是监听的两个值的新值:['refDc','refDcAge'] 
    // oldValue是监听的两个值的旧值数组:['refDc','refDcAge']

    watch([refDc, refDcAge ] ,(newValue,oldValue)=>{
        console.log('newValue:',newValue)
        console.log('oldValue:',oldValue)
    },{immediate:true})
</script>

控制台打印
在这里插入图片描述
监听reactive

<template>
    name: <input type="text" v-model="infoData.name">
    age:  <input type="text" v-model="infoData.age">
   <div>reactive数据:{{infoData}}</div>
</template>
  
<script lang="ts" setup>
    import { reactive, watch } from "vue";
    const infoData = reactive({
      name: 'swh',
      age: '21'
    })
    // 监视reactive定义的响应式数据
    // 若watch监视的是reactive定义的响应式数据,则无法正确获得oldValue
    // 若watch监视的是reactive定义的响应式数据,
		// 则强制开启了深度监视 (deep) 此处的deep(false)配置则不生效
    // immediate:true 配置项,代表首次执行监听一次
    watch(infoData ,(newValue,oldValue)=>{
        console.log('监听infoData--newValue',newValue)
        console.log('监听infoData--oldValue',oldValue)
    },{immediate:true,deep:false}) 

    // 监视reactive定义的响应式数据中的某个属性
    // 子组件监听props里的变量,无论是啥类型,都必须是watch(()=>props,function),
    // 而不是watch(props,function)
    watch(()=>infoData.age,(newValue,oldValue)=>{
        console.log('监听infoData.age-newValue',newValue)
        console.log('监听infoData.age-oldValue',oldValue)
    },{immediate:true,deep:true})


    // 监视reactive定义的响应式数据中的某些属性
    // newValue、oldValue返回的都是数组,第一位值infoData.name,
		// 第二位是infoData.age
    watch([()=>infoData.name,()=>infoData.age],(newValue,oldValue)=>{
        console.log('监听infoData.age name-newValue',newValue)
        console.log('监听infoData.age name-oldValue',oldValue)
    },{immediate:true,deep:true})

</script>

控制台打印
在这里插入图片描述
监听props 中基本数据类型
语法

<!-- 父组件 -->
<template>
    <defineProps  :masg="masg" />
    <div>
      点击父子间按钮修改传给子组件的信息:
			<button @click="clickMasg"> 按钮</button>
    </div>
</template>
<script setup >
import defineProps from "./components/defineProps.vue"
import { ref } from "vue";
const masg = ref('我是信息父组件')
const clickMasg = () => {
  masg.value = '我是信息父组件2223233'
}
</script>

<!-- 子组件 -->
<template>
    <button> {{ '父传子信息:' +  masg }} </button>
</template>
<script setup>
    import { watch } from "vue";
    const props = defineProps({
        masg: String,
   })
    // 监听props入参信息
    watch(
				// 监听其中的属性 侦听一个 getter函数
        () => props.masg,
				// 如果直接写成props.masg 则不生效
				// props.masg,
        (newVal, oldVal) => {
            console.log('监听基本类型数据masg')
            console.log('new', newVal) console.log('old', oldVal)
        }
    )
原因:
// 无法触发子组件内的监听,因为我们直接修改了masg的value,
// 相当于把masg.value指向了另一个proxy对象。这里是value的指向发生了变化。
// 而子组件内监听的是masg内属性的变化,而不是masg.value内存储的引用指向的变化
具体可以查看下watch源码


    watch(
				// 直接监听props
				 props,
        (newVal, oldVal) => {
            console.log('监听基本类型数据masg')
            console.log('new', newVal) console.log('old', oldVal)
        }
    )

</script>

控制台打印
在这里插入图片描述

watchEffect

  • watchEffect的是:不用指明监视哪个属性监视的回调中用到哪个属性,那就监视哪个属性
  • 跟计算属性有点像:而watchEffect函数没返回值注重过程,计算属性注重结果,有返回值
  • watchEffect 相当于将 watch 的依赖源和回调函数合并,当任何你有用到的响应式依赖更新时,该回调函数便会重新执行。不同于 watchwatchEffect 的回调函数会被立即执行(即 { immediate: true }
  • 语法
<template>
    name: <input type="text" v-model="info.name">
    age:<input type="text" v-model="info.age">
   <div>reactive数据:{{info}}</div>
</template>
  
<script lang="ts" setup>
    import { reactive, watchEffect } from "vue";
    const info = reactive({
          name: 'dc',
          age: '21'
     })
    watchEffect(() => {
        // 用到的数据只要发生变化(即 info.age发生变化),则直接重新执行回调。
        // const nawValue = info.age
        console.log(info.age, '我执行了!')
    })
</script>

控制台打印
在这里插入图片描述
侦听 props 上的属性变化

<script lang="ts" setup>
    // 如果我们想侦听 props 上的属性变化,需要采用第一种写法
    // 假设 props 上有个 name 属性
	// 下面的写法会生效
    watch(
      () => props.name,
      (count, prevCount) => {
        /* ... */
      }
    )

    // 下面的写法不会被触发
    watch(props.name, (count, prevCount) => {
      /* ... */
    })
</script>

生命周期

Composition API 形式的生命周期钩子,与Vue2.x中钩子对应关系如下

生命周期钩子函数需要按需引用

Vue3Vue2描述
setup()beforeCreate开始创建组件之前,在beforeCreate和created之前执行。创建的是data和method
setup()created
onBeforeMount()beforeMount组件挂载到节点上之前执行的函数
onMounted()mounted组件挂载完成后执行的函数
onBeforeUpdate()beforeUpdate组件更新之前执行的函数
onUpdated()updated组件更新完成之后执行的函数
onBeforeUnmount()beforeDestroy组件卸载之前执行的函数
onUnmounted()destroyed组件卸载完成后执行的函数
onDeactivated()deactivated比如从 A 组件,切换到 B 组件,A 组件消失时执行
onErrorCaptured()errorCaptured当捕获一个来自子孙组件的异常时激活钩子函数(以后用到再讲,不好展现)
onRenderTracked()/状态跟踪:它会跟踪页面上所有响应式变量和方法的状态,也就是我们用return返回去的值,他都会跟踪。只要页面有update的情况,他就会跟踪,然后生成一个event对象,我们通过event对象来查找程序的问题所在event 对象属性的详细介绍 key 那边变量发生了变化newValue 更新后变量的值oldValue 更新前变量的值target 目前页面中的响应变量和函
onRenderTriggered()/状态触发:当虚拟 DOM 重新渲染被触发时调用。和 https://v3.cn.vuejs.org/api/options-lifecycle-hooks.html#rendertracked类似,接收 debugger event作为参数。此事件告诉你是什么操作触发了重新渲染,以及该操作的目标对象和键event 对象属性的详细介绍 key 那边变量发生了变化newValue 更新后变量的值oldValue 更新前变量的值target 目前页面中的响应变量和函

组件间通信

父传子defineProps

  • 父组件传值给子组件主要是由父组件为子组件通过v-bind绑定数值,而后传给子组件;子组件则通过defineProps接收使用
  • 只能在<script setup>中使用
  • 不需要被导入即可使用,它会在编译<script setup>语法块时一同编译掉
  • 必须在<script setup>的顶层使用,不可以在<script setup>的局部变量中引用
  • 不可以访问<script setup>中定义的其他变量,因为在编译时整个表达式都会被移到外部的函数中
  • 语法
// 父组件
<template>
    <div>
        <helloWorld :msg="msg" title="我是标题"/>
    </div>
</template>
  
<script lang="ts" setup>
import helloWorld from "./components/helloWorld.vue"
import {ref} from "vue";

const msg= ref<String>("我是父组件传过来的值")

</script>

// 子组件
<template>
    <h1>{{ msg }}</h1>
</template>
<script setup>
    // 第一种写法 不设置默认值
    defineProps({
      msg?: String,
      title: String
    })
    // 第二种写法 设置默认值   
    defineProps({
        msg: {
            type: String,
            default: '默认值',
            required: false
        },
        title: {
            type: String,
            default: '我是标题2',
            required: false
        },
    })
</script>

补充:使用ts写法
<script setup lang="ts">
  import { withDefaults } from 'vue';
  // 接收Props 字段 定义类型
  // 利用ts接口声明类型  分离模式写法定义默认值和定义类型
	interface Props {
	  msg: string,
		title?: String
	}
const props  = withDefaults(defineProps<Props>(),{
    msg:'默认值',
    title:'默认值'
 })
  // 组合模式 定义类型&定义默认值
  // withDefaults 只能与基于类型的defineProps声明一起使用
	const props = withDefaults(defineProps<{
        // 非必传
        title?: string,
        // 必传
        msg:string
    }>(),{
	  title:'默认值',
      msg:'默认值',
	})
</script>

子传父defineEmits

  • 用于组件通信中子级组件向父级组件传值,其用来声明emit,其接收内容于emit选项一致
  • 只能在<script setup>中使用,不需要被导入即可使用,它会在编译<script setup>语法块时一同编译掉
  • 必须在<script setup>的顶层使用,不可以在<script setup>的局部变量中引用
  • 语法
// 父组件
<template>
    <son  @handLer="hand" />
</template>
<script setup>
import son from "./son.vue";
const hand = (v) => {
  console.log(v, '我是子组件数据');
};
</script>

// 子组件
<template>
    <button @click="fun"> 子传父 </button>
</template>
<script lang="ts" setup>
    import { reactive } from 'vue'
    // 使用defineEmits语法糖的方法来创建自定义事件
    let emigFun = defineEmits(["handLer"]);
    const dat = reactive([
      {
        name: 'weihua',
        id: 1,
      },
    ]);
    const fun = () => {
     // emigFun函数第一个参数是自定义事件名称,第二个参数是需要传递的内容
      emigFun("handLer", dat);
    }
</script>

祖与后代组件传值 provide 与 inject

  • 父组件有一个 provide 选项来提供数据,后代组件有一个 inject 选项来开始使用这些数据,允许一个祖先组件向其所有子孙后代注入一个依赖,不论组件层次有多深,
  • provide函数:用于给自己的后代组件传递参数
    provide(‘car’,car),第1个参数为给你传入的参数起个名字,第2个参数为传入的参数。
  • inject函数:用于注入祖组件传递过来的参数
    inject(‘car’),形参为传递过来的参数名字
  • 语法
// 父组件 index.vue
<template>
    <div> <child /></div>
</template>
<script lang="ts" setup>
    import child from './components/child.vue'
    import { reactive, provide } from 'vue'
    // 定义响应式数据
    let car = reactive({name:'shaoweihua',age:'18'})
    //给自己的后代组件传递数据
    provide('car',car)
</script>

// 子组件 child.vue
<template>
    <div><son /></div>
</template>
<script lang="ts" setup>
    // 引入子子组件    
    import son from "./son.vue";
</script>

// 子子组件 son.vue
<template>
    <div>{{ car.age }}</div>
</template>
<script lang="ts" setup>
    import { inject } from 'vue'
    let car = inject('car')
    console.log(car);
</script>
  • const car = inject(‘car’)

  • 其中inject注入的参数返回值也是个Proxy类型的对象,如图打印car
    在这里插入图片描述

  • provide 与 inject函数 只能用在由“祖组件” -> “子组件”传递参数,不能由子组件 ->
    祖组件,且祖组件传递给子组件的参数只能使用,子组件不能修改传递过来的值,否则报错
    父取子组件的ref defineExpose

  • 组件暴露自己的属性以及方法,去供外部使用,常用于父子组件关系

  • 在vue3.2中setup 挂载到 script (ref默认是关闭的) 是不能直接使用ref 取到子组件的方法和变量,需要使用defineExpose

  • 语法

// 子组件 child.vue 设置defineExpose
<template>
	<h1>{{name}}</h1>
</template>
<script lang="ts" setup>
    const name = '张三';
    const btn = function () {
      console.log(111)
    }
    // 子组件中设置defineExpose等同于将子组件中的方法以及属性全部做出暴漏
    // 只需要父级通过ref去获取就可以
    defineExpose({ name, btn })
</script>

// 父级组件代码 index.vue
<template>
	<child ref="childrenDom"/>
	<button @click='handleClick'>点击</button>
</template>
<script lang="ts" setup>
    import child from './components/child.vue'
    import { ref } from "vue";
    // 父级通常使用ref来定义虚拟dom,用来操作子组件
    // 那么这个时候打印ref的value值,我们拿不到子组件的属性以及方法
    const childrenDom = ref(null)
    const handleClick = function () {
      console.log(childrenDom.value)
      console.log(childrenDom.btn)
    }
</script>

Vue3.0 Bus总线mitt实现组件间通信

  • Vue2.0的事件总线在3就被弃用了,换成了mitt事件总线
  • Vue到3.0之后的Bus的房市变成了使用mitt,2.0是通过创建空的Vue来作为总线
  • 使用emit来注册,emit(‘type’, ‘event’),第一个入参是事件名称,第二个入参是参数
  • 使用on来监听:on(‘type’, ‘handler’ => { 操作逻辑 }),第一个入参是事件名称需要和上面的emitt事件名称对应,emit和on是成对出现的,一个发起,一个接收
  • 2.0之前是$emit, o n , 3.0 去掉了 on, 3.0去掉了 on,3.0去掉了
  • 接收方可以是多个组件,只要第一个参数匹配都可以获取到想要的数据
  • 语法
  • 创建公共的 eventBus 模块 mittBus.ts
import mitt from "mitt";
const mittBus = mitt();
export default mittBus
  • 在数据接发送方触发事件
<script lang="ts" setup>
    import bus from './mittBus.ts'
    import {onUnmounted} from "vue";
     // 监听时间方法
    const callback = (count:string) => {
        console.log(count)
    }
    mittBus.on('countChange', callback)
    // 用完后要清理监听器
    onUnmounted(()=>{
        mittBus.off('countChange',callback)
    })
</script>
  • 在数据接收方自定义事件
<script lang="ts" setup>
    import {onMounted} from "vue";
    import bus from './mittBus.ts'
 // 定义赋值
const count = ref('0')
// 加载完后调用 onMounted同mounted
onMounted(()=>{
   bus.emit('countChange', count)
})
</script>
  • 注册并监听自定义事件:mittBus.on(eventType,callback)
  • 触发自定义事件:mittBus.emit(eventType,value)
  • 取消所有的mitt事件:mittBus.all.clear()
  • 取消单个的mitt事件:mittBus.off(eventType,callback)
  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值