Vue3 技术分享

Vue3 生命周期

Vue3 与 Vue2 的生命周期没有很大的不同

Vue3 与 Vue2 对比: 

总结:

其中 Composition API (通过组合式API的形式去使用生命周期钩子)即 on 打头的 都写在 setup() 函数中的 

原来的不变的(通过配置项的形式使用生命周期钩子)可以写在 setup() 并列外面

这两种方式二选一,如果都存在,则 setup 中的(即 on 开头的) 会比写在 setup() 外面的先执行 

CompositionAPI 的优势:

使用传统OptionsAPI(Vue2)中,新增或者修改一个需求,就需要分别在data,methods,computed里修改 

Composition API (Vue3)的优势:我们可以更加优雅的组织我们的代码,函数。让相关功能的代码更加有序的组织在一起
通过 hook 来实现(把相关的数据,方法啥的放到一个 hook 中,要用的时候调用即可)

注意:使用组合式 API 记得要引入!

自定义 hook

hook:本质是一个函数,把 setup 函数中使用的 Composition API (比如 ref 函数、reactive 函数、计算属性与监视属性、生命周期等等)进行了封装,类似于 vue2.x 中的 mixin

自定义 hook 的优势:复用代码,让 setup 中的逻辑更清楚易懂

总结:

  • 在 src 中创建一个 hook 文件夹,在此文件夹中创建 .js 文件,放要封装的函数
  • 此函数记得要暴露出去,并且要有返回值(return)
  • 哪个组件想用,引入就可以使用了
  • 创建一个变量去接收此函数的返回值
  • 模板中也可以直接使用该函数中的属性

toRef 与 toRefs

toRef:

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

语法:const name = toRef(person,'name')

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

扩展:toRefs 与 toRef 功能一致,但可以批量创建多个 ref 对象,语法:toRefs(person)

例子:

<template>
  <h2>姓名:{{name}}</h2>
  <h2>年龄:{{age}}</h2>
  <h2>薪资:{{job.j1.salary}}K</h2>
  <button @click="name+='~'">修改姓名</button>
  <button @click="age++">增长年龄</button>
  <button @click="job.j1.salary++">涨薪</button>
</template>

<script>
import {reactive,toRefs} from 'vue'
export default {
    name: 'Demo',
    setup(){
    // 数据
    let person = reactive({
      name:'张三',
      age: 18,
      job:{
        j1:{
          salary: 20
        }
      }
    })
    
    // 方法一  toRef(对象,'属性')
    // return{
    //   name:toRef(person,'name'),
    //   age:toRef(person,'age'),
    //   salary:toRef(person.job.j1,'salary')
    // }
    //方法二  ...toRefs(对象)
    return {
      //因为对象中不能再跟一个对象,所以用解构,这样就把 person 中所有的属性都解析了
      // 但是只能解析普通变量,对于对象类型的,还是要用 job.j1.salary
      ...toRefs(person)
    }
}
}
</script>

其中 return name = toRef(person,'name'),相当于这里的 name 指向的就是 person 中的 name , 这样在模板字符中就只用写 name 而不是 person.name,用 toRef 可以把 person 对象中的对象再解析出来,比如 salary:toRef(person.job.j1,'salary')

toRefs 更方便, ...toRef(person),就可以把 person 中所有属性都解析出来,但是有一个问题,就是深层次的对象中的属性解析不出来,只解析第一层的属性,这样在模板字符中还是写job.j1.salary

shallowReactive与shallowRef 

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

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

什么时候用:

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

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

例子:

<template>
  <h2>姓名:{{name}}</h2>
  <h2>年龄:{{age}}</h2>
  <h2>薪资:{{job.j1.salary}}K</h2>
  <button @click="name+='~'">修改姓名</button>
  <button @click="age++">增长年龄</button>
  <button @click="job.j1.salary++">涨薪</button>
  <hr>
  <h2>{{sum.y}}</h2>
  <button @click="sum.y++">点我加1</button>
  <button @click="sum={y:888}">点我替换sum</button>
</template>

<script>
import {toRefs,shallowReactive,shallowRef} from 'vue'
export default {
    name: 'Demo',
    setup(){
    // 数据
    let person = shallowReactive({
      name:'张三',
      age: 18,
      job:{
        j1:{
          salary: 20
        }
      }
    })

    let sum = shallowRef({
      y:0
    })
    
    return {
     
      ...toRefs(person),
      sum
    }
}
}
</script>

其中生效的有修改 name 、age、替换 sum 的按钮

readonly与shallowReadonly 

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

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

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

例子:

<template>
  <h2>姓名:{{name}}</h2>
  <h2>年龄:{{age}}</h2>
  <h2>薪资:{{job.j1.salary}}K</h2>
  <button @click="name+='~'">修改姓名</button>
  <button @click="age++">增长年龄</button>
  <button @click="job.j1.salary++">涨薪</button>
  <hr>
  <h2>{{sum.y}}</h2>
  <button @click="sum.y++">点我加1</button>
  <button @click="sum={y:888}">点我替换sum</button>
</template>

<script>
import {toRefs,reactive,shallowRef, shallowReadonly} from 'vue'
export default {
    name: 'Demo',
    setup(){
    // 数据
    let person = reactive({
      name:'张三',
      age: 18,
      job:{
        j1:{
          salary: 20
        }
      }
    })
    // 深只读,就是全部都不能更改
    // person = readonly(person)
    // 浅只读,只有第一层不能修改,第二三层都可以修改
    person = shallowReadonly(person)
    let sum = shallowRef({
      y:0
    })
    
    return {
     
      ...toRefs(person),
      sum
    }
}
}
</script>

toRaw与markRaw

toRaw:

   作用:将一个由 reactive 生成的响应式对象转为普通对象

   使用场景:用于读取响应式对象对应的普通对象,对这个普通对象的所有操作,不会引起页面更新

markRaw: 

   作用:标记一个对象,使其永远不会再成为响应式对象

   应用场景:

       1. 有些值不应被设置为响应式的,例如复杂的第三方类库等

       2. 当渲染具有不可变数据源的大列表时,跳过响应式转换可以提高性能

例子:

<template>
  <h2>姓名:{{name}}</h2>
  <h2>年龄:{{age}}</h2>
  <h2>薪资:{{job.j1.salary}}K</h2>
  <h3>座驾信息:{{person.car}}</h3>
  <button @click="name+='~'">修改姓名</button>
  <button @click="age++">增长年龄</button>
  <button @click="job.j1.salary++">涨薪</button>
  <button @click="showPerson">输出最原始的person</button>
  <button @click="addCar">给人添加一台车</button>
  <button @click="person.car.name+='!'">换车名</button>
  <button @click="changePrice">换价格</button>
  <hr>
</template>

<script>
import {toRefs,reactive,toRaw, markRaw} from 'vue'
export default {
    name: 'Demo',
    setup(){
    // 数据
    let person = reactive({
      name:'张三',
      age: 18,
      job:{
        j1:{
          salary: 20
        }
      }
    })
    function showPerson(){
      const p = toRaw(person)
      p.age++
      console.log(p);
    }

    function addCar(){
      let car = {name:'奔驰',price:40}
      person.car = markRaw(car)
    }

    function changePrice(){
      person.car.price++
      console.log(person.car.price);
    }
    
    return {
      person,
      ...toRefs(person),
      showPerson,
      addCar,
      changePrice
    }
}
}
</script>

customRef⭐

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

需要注意的点:创建自定义的 ref 必须要用到 customRef,来实现对数据的跟踪和触发

代码:

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

<script>

import {customRef} from 'vue'
export default {
  name: 'App',
  setup(){
    //自定义一个 ref——名为:myRef
    function myRef(value,delay){
      let timer
      return customRef((track,trigger)=>{
        return {
          get(){
            console.log(`有人从myRef这个容器中读取数据了,我把${value}`);
            track() //通知Vue追踪value的变化(提前和get商量一下,让他认为这个value是有用的)
            return value
          },
          set(newValue){
            console.log(`有人把myRef这个容器中数据改为了:${newValue}`);
            clearTimeout(timer)
            timer = setTimeout(()=>{
              value = newValue
              trigger() //通知Vue去重新解析模板
            },delay)
          }
        }
      })
    }

    // let keyWord = ref('hello') //使用Vue提供的ref
    let keyWord = myRef('hello',500) //使用程序员自定义的ref
    return {keyWord}
  }
}
</script>

其中,track 代表跟踪数据,用来告诉 get 输入的 value 是有用的,如果不写,修改后 get 不会重新执行,trigger 用来触发页面重新加载的,通知 Vue 去重新解析模板,在 set 里面放一个定时器可以实现防抖效果

provide与inject

作用:实现祖与后代组件间的通信

套路:父组件有一个 provide 选项来提供数据,后代组件有一个 inject 选项来开始使用这些数据

具体写法:

1.祖组件中:

setup(){
  ...
  let car = reactive({name:'奔驰',price:'40w'})
  provide('car',car)
  ...
}

2. 后代组件中:

setup(props,context){
  ...
  const car = inject('car')
  return {car}
  ...
}

一般用于祖孙间的传递,祖组件通过 provide('名字',数据) , 孙组件通过 inject('名字') 接收,就可以用了,父子间的传递用 props 就可以了

响应式数据的判断

isRef: 检查一个值是否为一个 ref 对象

isReactive: 检查一个对象是否是由 reactive 创建的响应式代理

isReadonly: 检查一个对象是否是由 readonly 创建的只读代理

isProxy: 检查一个对象是否是由 reactive 或者 readonly 方法创建的代理

Fragment 组件

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

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

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

Teleport 组件 ⭐

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

<teleport to="移动位置">
  <div v-if="isShow" class="mask">
     <div class="dialog">
        <h3>我是一个弹窗</h3>
        <button @click="isShow = false">关闭弹窗</button>
     </div>
  </div>
<teleport>

比如在子子子组件中嵌套一个弹框,如果只是单单的放在子组件中,那么出现的时候就会把该弹框的所有父组件都撑大,不太好

如果用 Teleport 包裹,其中 to 代表以什么为父组件(比如以 body 为父组件),进行定位设置,那么弹框就会脱离它的父组件,可以直接跑到页面中间显示

Suspense

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

使用步骤:

※ 异步引入组件

import {defineAsyncComponent} from 'vue'
const Child = defineAsyncComponent(()=>import('./components/Child')) //异步引入

※ 使用 Suspense 包裹组件,并配置好 default 与 fallback

<template>
  <div class="app">
    <h3>我是App组件</h3>
    <Suspense>
       <!-- 真正要显示的内容放在 v-slot:default 中 -->
       <template v-slot:default>  
          <Child/>
       </template>
       <!-- 页面还没加载完毕要显示的内容放在 fallback 中 -->
       <template v-slot:fallback>
         <h3>稍等,加载中...</h3>
       </template>
    </Suspense>
   
  </div>
</template>

※ 要引入的组件中的代码

<template>
  <div class="child">
      <h3>我是Child组件</h3>
      {{sum}}
  </div>
</template>

<script>
import {ref} from 'vue'
export default {
    name:'Child',
     setup(){
        let sum = ref(0)
        return new Promise((resolve)=>{
            setTimeout(()=>{
                resolve({sum})   //延迟1秒才出现
            },1000)
        })

    }
}
</script>

如果静态引入,那么就会导致最内层的元素没有加载完,那么所有的元素都不会显示,相当于就等待最慢的那一个

如果用异步引入的话,异步引入的组件会后出现,但是用户会不知道还有内容,出现的时候会有抖动,所以就要用到 Suspense, 把出现的组件放到 Suspense  标签中,相当于插槽

也可以手动调慢页面加载,写在Child 组件中,通过返回一个 Promise(只有异步才能用 Promise ,否则没效果)


以上就是全部内容啦~如果没看懂的同学,下去要多多复习噢

  • 3
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值